From 3879498af3cbd4f171ec9e2c67ebbd4392294eee Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Thu, 10 Jul 2014 14:24:30 -0500 Subject: [PATCH] Updated JUCE modules to version 3, which also updated the license headers to reflect AGPLv3 compatibility. --- JuceLibraryCode/AppConfig.h | 16 + JuceLibraryCode/JuceHeader.h | 4 +- .../buffers/juce_AudioDataConverters.cpp | 63 +- .../buffers/juce_AudioDataConverters.h | 138 +- .../buffers/juce_AudioSampleBuffer.cpp | 383 +- .../buffers/juce_AudioSampleBuffer.h | 196 +- .../buffers/juce_FloatVectorOperations.cpp | 774 +++ .../buffers/juce_FloatVectorOperations.h | 132 + .../juce_audio_basics/effects/juce_Decibels.h | 28 +- .../effects/juce_IIRFilter.cpp | 292 +- .../effects/juce_IIRFilter.h | 199 +- .../effects/juce_LagrangeInterpolator.cpp | 200 + .../effects/juce_LagrangeInterpolator.h | 94 + .../juce_audio_basics/effects/juce_Reverb.h | 58 +- .../juce_audio_basics/juce_audio_basics.cpp | 55 +- .../juce_audio_basics/juce_audio_basics.h | 107 +- .../juce_audio_basics/juce_audio_basics.mm | 17 +- .../juce_audio_basics/juce_module_info | 7 +- .../midi/juce_MidiBuffer.cpp | 183 +- .../juce_audio_basics/midi/juce_MidiBuffer.h | 59 +- .../juce_audio_basics/midi/juce_MidiFile.cpp | 221 +- .../juce_audio_basics/midi/juce_MidiFile.h | 55 +- .../midi/juce_MidiKeyboardState.cpp | 19 +- .../midi/juce_MidiKeyboardState.h | 28 +- .../midi/juce_MidiMessage.cpp | 502 +- .../juce_audio_basics/midi/juce_MidiMessage.h | 173 +- .../midi/juce_MidiMessageSequence.cpp | 114 +- .../midi/juce_MidiMessageSequence.h | 91 +- .../sources/juce_AudioSource.h | 52 +- .../sources/juce_BufferingAudioSource.cpp | 112 +- .../sources/juce_BufferingAudioSource.h | 44 +- .../juce_ChannelRemappingAudioSource.cpp | 36 +- .../juce_ChannelRemappingAudioSource.h | 40 +- .../sources/juce_IIRFilterAudioSource.cpp | 29 +- .../sources/juce_IIRFilterAudioSource.h | 41 +- .../sources/juce_MixerAudioSource.cpp | 28 +- .../sources/juce_MixerAudioSource.h | 35 +- .../sources/juce_PositionableAudioSource.h | 25 +- .../sources/juce_ResamplingAudioSource.cpp | 41 +- .../sources/juce_ResamplingAudioSource.h | 36 +- .../sources/juce_ReverbAudioSource.cpp | 21 +- .../sources/juce_ReverbAudioSource.h | 34 +- .../sources/juce_ToneGeneratorAudioSource.cpp | 24 +- .../sources/juce_ToneGeneratorAudioSource.h | 33 +- .../synthesisers/juce_Synthesiser.cpp | 146 +- .../synthesisers/juce_Synthesiser.h | 143 +- .../audio_cd/juce_AudioCDBurner.h | 27 +- .../audio_cd/juce_AudioCDReader.cpp | 17 +- .../audio_cd/juce_AudioCDReader.h | 27 +- .../audio_io/juce_AudioDeviceManager.cpp | 503 +- .../audio_io/juce_AudioDeviceManager.h | 143 +- .../audio_io/juce_AudioIODevice.cpp | 36 +- .../audio_io/juce_AudioIODevice.h | 74 +- .../audio_io/juce_AudioIODeviceType.cpp | 17 +- .../audio_io/juce_AudioIODeviceType.h | 32 +- .../audio_io/juce_SystemAudioVolume.h | 61 + .../juce_audio_devices/juce_audio_devices.cpp | 49 +- .../juce_audio_devices/juce_audio_devices.h | 68 +- .../juce_audio_devices/juce_audio_devices.mm | 17 +- .../juce_audio_devices/juce_module_info | 6 +- .../midi_io/juce_MidiInput.h | 31 +- .../midi_io/juce_MidiMessageCollector.cpp | 18 +- .../midi_io/juce_MidiMessageCollector.h | 35 +- .../midi_io/juce_MidiOutput.cpp | 17 +- .../midi_io/juce_MidiOutput.h | 29 +- .../native/juce_MidiDataConcatenator.h | 107 +- .../native/juce_android_Audio.cpp | 94 +- .../native/juce_android_Midi.cpp | 18 +- .../native/juce_android_OpenSL.cpp | 175 +- .../native/juce_ios_Audio.cpp | 277 +- .../native/juce_linux_ALSA.cpp | 941 ++-- .../native/juce_linux_AudioCDReader.cpp | 17 +- .../native/juce_linux_JackAudio.cpp | 467 +- .../native/juce_linux_Midi.cpp | 640 ++- .../native/juce_mac_AudioCDBurner.mm | 626 +-- .../native/juce_mac_AudioCDReader.mm | 25 +- .../native/juce_mac_CoreAudio.cpp | 1539 ++++-- .../native/juce_mac_CoreMidi.cpp | 297 +- .../native/juce_win32_ASIO.cpp | 1401 +++-- .../native/juce_win32_AudioCDBurner.cpp | 34 +- .../native/juce_win32_AudioCDReader.cpp | 21 +- .../native/juce_win32_DirectSound.cpp | 506 +- .../native/juce_win32_Midi.cpp | 166 +- .../native/juce_win32_WASAPI.cpp | 695 ++- .../sources/juce_AudioSourcePlayer.cpp | 54 +- .../sources/juce_AudioSourcePlayer.h | 37 +- .../sources/juce_AudioTransportSource.cpp | 66 +- .../sources/juce_AudioTransportSource.h | 49 +- .../juce_audio_formats/codecs/flac/all.h | 37 +- .../juce_audio_formats/codecs/flac/alloc.h | 103 +- .../juce_audio_formats/codecs/flac/assert.h | 3 +- .../juce_audio_formats/codecs/flac/callback.h | 3 +- .../juce_audio_formats/codecs/flac/compat.h | 195 + .../juce_audio_formats/codecs/flac/endswap.h | 52 + .../juce_audio_formats/codecs/flac/export.h | 22 +- .../juce_audio_formats/codecs/flac/format.h | 15 +- .../codecs/flac/libFLAC/bitmath.c | 43 +- .../codecs/flac/libFLAC/bitreader.c | 614 +-- .../codecs/flac/libFLAC/bitwriter.c | 110 +- .../codecs/flac/libFLAC/cpu.c | 5 +- .../codecs/flac/libFLAC/crc.c | 5 +- .../codecs/flac/libFLAC/fixed.c | 20 +- .../codecs/flac/libFLAC/float.c | 12 +- .../codecs/flac/libFLAC/format.c | 53 +- .../codecs/flac/libFLAC/include/private/all.h | 3 +- .../flac/libFLAC/include/private/bitmath.h | 135 +- .../flac/libFLAC/include/private/bitreader.h | 3 +- .../flac/libFLAC/include/private/bitwriter.h | 3 +- .../codecs/flac/libFLAC/include/private/cpu.h | 3 +- .../codecs/flac/libFLAC/include/private/crc.h | 5 +- .../flac/libFLAC/include/private/fixed.h | 5 +- .../flac/libFLAC/include/private/float.h | 3 +- .../flac/libFLAC/include/private/format.h | 3 +- .../codecs/flac/libFLAC/include/private/lpc.h | 3 +- .../flac/libFLAC/include/private/memory.h | 14 +- .../flac/libFLAC/include/private/metadata.h | 5 +- .../include/private/stream_encoder_framing.h | 3 +- .../flac/libFLAC/include/private/window.h | 3 +- .../flac/libFLAC/include/protected/all.h | 3 +- .../include/protected/stream_decoder.h | 5 +- .../include/protected/stream_encoder.h | 5 +- .../codecs/flac/libFLAC/lpc_flac.c | 91 +- .../codecs/flac/libFLAC/md5.c | 14 +- .../codecs/flac/libFLAC/memory.c | 47 +- .../codecs/flac/libFLAC/stream_decoder.c | 176 +- .../codecs/flac/libFLAC/stream_encoder.c | 216 +- .../flac/libFLAC/stream_encoder_framing.c | 14 +- .../codecs/flac/libFLAC/window_flac.c | 3 +- .../juce_audio_formats/codecs/flac/metadata.h | 5 +- .../juce_audio_formats/codecs/flac/ordinals.h | 34 +- .../codecs/flac/stream_decoder.h | 3 +- .../codecs/flac/stream_encoder.h | 5 +- .../codecs/juce_AiffAudioFormat.cpp | 401 +- .../codecs/juce_AiffAudioFormat.h | 54 +- .../codecs/juce_CoreAudioFormat.cpp | 343 +- .../codecs/juce_CoreAudioFormat.h | 47 +- .../codecs/juce_FlacAudioFormat.cpp | 106 +- .../codecs/juce_FlacAudioFormat.h | 35 +- .../codecs/juce_LAMEEncoderAudioFormat.cpp | 222 + .../codecs/juce_LAMEEncoderAudioFormat.h | 71 + .../codecs/juce_MP3AudioFormat.cpp | 336 +- .../codecs/juce_MP3AudioFormat.h | 35 +- .../codecs/juce_OggVorbisAudioFormat.cpp | 184 +- .../codecs/juce_OggVorbisAudioFormat.h | 45 +- .../codecs/juce_QuickTimeAudioFormat.cpp | 377 +- .../codecs/juce_QuickTimeAudioFormat.h | 19 +- .../codecs/juce_WavAudioFormat.cpp | 835 +-- .../codecs/juce_WavAudioFormat.h | 63 +- .../codecs/juce_WindowsMediaAudioFormat.cpp | 108 +- .../codecs/juce_WindowsMediaAudioFormat.h | 32 +- .../oggvorbis/libvorbis-1.3.2/lib/bitrate.c | 14 +- .../oggvorbis/libvorbis-1.3.2/lib/block.c | 31 +- .../oggvorbis/libvorbis-1.3.2/lib/floor0.c | 2 +- .../oggvorbis/libvorbis-1.3.2/lib/floor1.c | 8 +- .../oggvorbis/libvorbis-1.3.2/lib/lsp.c | 2 - .../codecs/oggvorbis/libvorbis-1.3.2/lib/os.h | 6 +- .../oggvorbis/libvorbis-1.3.2/lib/psy.c | 2 +- .../oggvorbis/libvorbis-1.3.2/lib/res0.c | 8 +- .../libvorbis-1.3.2/lib/vorbisfile.c | 5 +- .../juce_audio_formats/codecs/oggvorbis/ogg.h | 2 +- .../format/juce_AudioFormat.cpp | 33 +- .../format/juce_AudioFormat.h | 46 +- .../format/juce_AudioFormatManager.cpp | 60 +- .../format/juce_AudioFormatManager.h | 33 +- .../format/juce_AudioFormatReader.cpp | 253 +- .../format/juce_AudioFormatReader.h | 55 +- .../format/juce_AudioFormatReaderSource.cpp | 31 +- .../format/juce_AudioFormatReaderSource.h | 41 +- .../format/juce_AudioFormatWriter.cpp | 167 +- .../format/juce_AudioFormatWriter.h | 38 +- .../format/juce_AudioSubsectionReader.cpp | 30 +- .../format/juce_AudioSubsectionReader.h | 40 +- .../juce_BufferingAudioFormatReader.cpp | 172 + .../format/juce_BufferingAudioFormatReader.h | 93 + .../juce_MemoryMappedAudioFormatReader.h | 106 + .../juce_audio_formats/juce_audio_formats.cpp | 21 +- .../juce_audio_formats/juce_audio_formats.h | 64 +- .../juce_audio_formats/juce_audio_formats.mm | 17 +- .../juce_audio_formats/juce_module_info | 2 +- .../sampler/juce_Sampler.cpp | 68 +- .../juce_audio_formats/sampler/juce_Sampler.h | 60 +- .../format/juce_AudioPluginFormat.cpp | 17 +- .../format/juce_AudioPluginFormat.h | 55 +- .../format/juce_AudioPluginFormatManager.cpp | 71 +- .../format/juce_AudioPluginFormatManager.h | 37 +- .../format_types/juce_AudioUnitPluginFormat.h | 46 +- .../juce_AudioUnitPluginFormat.mm | 1000 ++-- .../format_types/juce_DirectXPluginFormat.h | 62 - .../format_types/juce_LADSPAPluginFormat.cpp | 703 +++ .../format_types/juce_LADSPAPluginFormat.h | 53 +- .../format_types/juce_VST3Common.h | 420 ++ .../format_types/juce_VST3Headers.h | 172 + .../format_types/juce_VST3PluginFormat.cpp | 2516 +++++++++ .../format_types/juce_VST3PluginFormat.h | 72 + .../format_types/juce_VSTMidiEventList.h | 35 +- .../format_types/juce_VSTPluginFormat.cpp | 3197 ++++++----- .../format_types/juce_VSTPluginFormat.h | 105 +- .../juce_audio_processors.cpp | 107 +- .../juce_audio_processors.h | 114 +- .../juce_audio_processors.mm | 17 +- .../juce_audio_processors/juce_module_info | 2 +- .../processors/juce_AudioPlayHead.h | 37 +- .../processors/juce_AudioPluginInstance.h | 53 +- .../processors/juce_AudioProcessor.cpp | 230 +- .../processors/juce_AudioProcessor.h | 213 +- .../processors/juce_AudioProcessorEditor.cpp | 21 +- .../processors/juce_AudioProcessorEditor.h | 27 +- .../processors/juce_AudioProcessorGraph.cpp | 145 +- .../processors/juce_AudioProcessorGraph.h | 80 +- .../processors/juce_AudioProcessorListener.h | 24 +- .../juce_GenericAudioProcessorEditor.cpp | 90 +- .../juce_GenericAudioProcessorEditor.h | 31 +- .../processors/juce_PluginDescription.cpp | 26 +- .../processors/juce_PluginDescription.h | 64 +- .../scanning/juce_KnownPluginList.cpp | 443 +- .../scanning/juce_KnownPluginList.h | 117 +- .../scanning/juce_PluginDirectoryScanner.cpp | 121 +- .../scanning/juce_PluginDirectoryScanner.h | 46 +- .../scanning/juce_PluginListComponent.cpp | 665 ++- .../scanning/juce_PluginListComponent.h | 101 +- .../containers/juce_AbstractFifo.cpp | 56 +- .../juce_core/containers/juce_AbstractFifo.h | 41 +- .../modules/juce_core/containers/juce_Array.h | 298 +- .../containers/juce_ArrayAllocationBase.h | 41 +- .../containers/juce_DynamicObject.cpp | 97 +- .../juce_core/containers/juce_DynamicObject.h | 89 +- .../containers/juce_ElementComparator.h | 171 +- .../juce_core/containers/juce_HashMap.h | 134 +- .../containers/juce_LinkedListPointer.h | 47 +- .../containers/juce_NamedValueSet.cpp | 268 +- .../juce_core/containers/juce_NamedValueSet.h | 117 +- .../juce_core/containers/juce_OwnedArray.h | 281 +- .../juce_core/containers/juce_PropertySet.cpp | 51 +- .../juce_core/containers/juce_PropertySet.h | 81 +- .../containers/juce_ReferenceCountedArray.h | 204 +- .../containers/juce_ScopedValueSetter.h | 43 +- .../juce_core/containers/juce_SortedSet.h | 330 +- .../juce_core/containers/juce_SparseSet.h | 61 +- .../juce_core/containers/juce_Variant.cpp | 497 +- .../juce_core/containers/juce_Variant.h | 163 +- .../files/juce_DirectoryIterator.cpp | 86 +- .../juce_core/files/juce_DirectoryIterator.h | 65 +- .../modules/juce_core/files/juce_File.cpp | 239 +- .../modules/juce_core/files/juce_File.h | 180 +- .../juce_core/files/juce_FileFilter.cpp | 41 + .../files}/juce_FileFilter.h | 35 +- .../juce_core/files/juce_FileInputStream.cpp | 67 +- .../juce_core/files/juce_FileInputStream.h | 66 +- .../juce_core/files/juce_FileOutputStream.cpp | 63 +- .../juce_core/files/juce_FileOutputStream.h | 58 +- .../juce_core/files/juce_FileSearchPath.cpp | 40 +- .../juce_core/files/juce_FileSearchPath.h | 55 +- .../juce_core/files/juce_MemoryMappedFile.h | 66 +- .../juce_core/files/juce_TemporaryFile.cpp | 83 +- .../juce_core/files/juce_TemporaryFile.h | 59 +- .../files}/juce_WildcardFileFilter.cpp | 35 +- .../files}/juce_WildcardFileFilter.h | 39 +- .../{json => javascript}/juce_JSON.cpp | 351 +- .../{json => javascript}/juce_JSON.h | 69 +- .../juce_core/javascript/juce_Javascript.cpp | 1710 ++++++ .../juce_core/javascript/juce_Javascript.h | 105 + .../modules/juce_core/juce_core.cpp | 75 +- JuceLibraryCode/modules/juce_core/juce_core.h | 487 +- .../modules/juce_core/juce_core.mm | 29 +- .../modules/juce_core/juce_module_info | 10 +- .../juce_core/logging/juce_FileLogger.cpp | 136 +- .../juce_core/logging/juce_FileLogger.h | 109 +- .../modules/juce_core/logging/juce_Logger.cpp | 58 +- .../modules/juce_core/logging/juce_Logger.h | 50 +- .../juce_core/maths/juce_BigInteger.cpp | 45 +- .../modules/juce_core/maths/juce_BigInteger.h | 120 +- .../juce_core/maths/juce_Expression.cpp | 177 +- .../modules/juce_core/maths/juce_Expression.h | 59 +- .../juce_core/maths/juce_MathsFunctions.h | 94 +- .../modules/juce_core/maths/juce_Random.cpp | 86 +- .../modules/juce_core/maths/juce_Random.h | 58 +- .../modules/juce_core/maths/juce_Range.h | 108 +- .../modules/juce_core/memory/juce_Atomic.h | 81 +- .../modules/juce_core/memory/juce_ByteOrder.h | 147 +- .../memory/juce_ContainerDeletePolicy.h | 53 + .../modules/juce_core/memory/juce_HeapBlock.h | 80 +- .../memory/juce_LeakedObjectDetector.h | 43 +- .../modules/juce_core/memory/juce_Memory.h | 92 +- .../juce_core/memory/juce_MemoryBlock.cpp | 158 +- .../juce_core/memory/juce_MemoryBlock.h | 77 +- .../memory/juce_OptionalScopedPointer.h | 75 +- .../memory/juce_ReferenceCountedObject.h | 272 +- .../juce_core/memory/juce_ScopedPointer.h | 85 +- .../memory/juce_SharedResourcePointer.h | 152 + .../modules/juce_core/memory/juce_Singleton.h | 35 +- .../juce_core/memory/juce_WeakReference.h | 43 +- .../modules/juce_core/misc/juce_Result.cpp | 36 +- .../modules/juce_core/misc/juce_Result.h | 50 +- .../modules/juce_core/misc/juce_Uuid.cpp | 80 +- .../modules/juce_core/misc/juce_Uuid.h | 66 +- .../juce_core/misc/juce_WindowsRegistry.h | 107 +- .../native/java/JuceAppActivity.java | 340 +- .../native/juce_BasicNativeHeaders.h | 64 +- .../juce_core/native/juce_android_Files.cpp | 243 +- .../native/juce_android_JNIHelpers.h | 149 +- .../juce_core/native/juce_android_Misc.cpp | 31 +- .../juce_core/native/juce_android_Network.cpp | 78 +- .../native/juce_android_SystemStats.cpp | 101 +- .../juce_core/native/juce_android_Threads.cpp | 41 +- .../native/juce_linux_CommonFile.cpp | 153 + .../juce_core/native/juce_linux_Files.cpp | 292 +- .../juce_core/native/juce_linux_Network.cpp | 166 +- .../native/juce_linux_SystemStats.cpp | 77 +- .../juce_core/native/juce_linux_Threads.cpp | 61 +- .../juce_core/native/juce_mac_Files.mm | 459 +- .../juce_core/native/juce_mac_Network.mm | 640 ++- .../juce_core/native/juce_mac_ObjCSuffix.h | 52 - .../juce_core/native/juce_mac_Strings.mm | 41 +- .../juce_core/native/juce_mac_SystemStats.mm | 171 +- .../juce_core/native/juce_mac_Threads.mm | 62 +- .../juce_core/native/juce_osx_ObjCHelpers.h | 151 +- .../juce_core/native/juce_posix_NamedPipe.cpp | 169 +- .../juce_core/native/juce_posix_SharedCode.h | 693 ++- .../juce_core/native/juce_win32_ComSmartPtr.h | 111 +- .../juce_core/native/juce_win32_Files.cpp | 529 +- .../juce_core/native/juce_win32_Network.cpp | 263 +- .../juce_core/native/juce_win32_Registry.cpp | 141 +- .../native/juce_win32_SystemStats.cpp | 245 +- .../juce_core/native/juce_win32_Threads.cpp | 256 +- .../juce_core/network/juce_IPAddress.cpp | 149 + .../juce_core/network/juce_IPAddress.h | 82 + .../juce_core/network/juce_MACAddress.cpp | 29 +- .../juce_core/network/juce_MACAddress.h | 52 +- .../juce_core/network/juce_NamedPipe.cpp | 49 +- .../juce_core/network/juce_NamedPipe.h | 59 +- .../modules/juce_core/network/juce_Socket.cpp | 111 +- .../modules/juce_core/network/juce_Socket.h | 51 +- .../modules/juce_core/network/juce_URL.cpp | 252 +- .../modules/juce_core/network/juce_URL.h | 120 +- .../streams/juce_BufferedInputStream.cpp | 32 +- .../streams/juce_BufferedInputStream.h | 53 +- .../streams/juce_FileInputSource.cpp | 33 +- .../juce_core/streams/juce_FileInputSource.h | 40 +- .../juce_core/streams/juce_InputSource.h | 42 +- .../juce_core/streams/juce_InputStream.cpp | 29 +- .../juce_core/streams/juce_InputStream.h | 87 +- .../streams/juce_MemoryInputStream.cpp | 46 +- .../streams/juce_MemoryInputStream.h | 60 +- .../streams/juce_MemoryOutputStream.cpp | 157 +- .../streams/juce_MemoryOutputStream.h | 73 +- .../juce_core/streams/juce_OutputStream.cpp | 172 +- .../juce_core/streams/juce_OutputStream.h | 121 +- .../streams/juce_SubregionStream.cpp | 62 +- .../juce_core/streams/juce_SubregionStream.h | 53 +- .../juce_core/system/juce_PlatformDefs.h | 153 +- .../juce_core/system/juce_StandardHeader.h | 135 +- .../juce_core/system/juce_SystemStats.cpp | 162 +- .../juce_core/system/juce_SystemStats.h | 146 +- .../juce_core/system/juce_TargetPlatform.h | 74 +- .../juce_core/text/juce_CharPointer_ASCII.h | 83 +- .../juce_core/text/juce_CharPointer_UTF16.h | 109 +- .../juce_core/text/juce_CharPointer_UTF32.h | 83 +- .../juce_core/text/juce_CharPointer_UTF8.h | 149 +- .../text/juce_CharacterFunctions.cpp | 31 +- .../juce_core/text/juce_CharacterFunctions.h | 182 +- .../juce_core/text/juce_Identifier.cpp | 76 +- .../modules/juce_core/text/juce_Identifier.h | 89 +- .../juce_core/text/juce_LocalisedStrings.cpp | 113 +- .../juce_core/text/juce_LocalisedStrings.h | 97 +- .../modules/juce_core/text/juce_NewLine.h | 43 +- .../modules/juce_core/text/juce_String.cpp | 1074 ++-- .../modules/juce_core/text/juce_String.h | 311 +- .../juce_core/text/juce_StringArray.cpp | 218 +- .../modules/juce_core/text/juce_StringArray.h | 162 +- .../juce_core/text/juce_StringPairArray.cpp | 40 +- .../juce_core/text/juce_StringPairArray.h | 53 +- .../juce_core/text/juce_StringPool.cpp | 205 +- .../modules/juce_core/text/juce_StringPool.h | 82 +- .../modules/juce_core/text/juce_StringRef.h | 138 + .../modules/juce_core/text/juce_TextDiff.cpp | 247 + .../modules/juce_core/text/juce_TextDiff.h | 80 + .../juce_core/threads/juce_ChildProcess.cpp | 53 +- .../juce_core/threads/juce_ChildProcess.h | 64 +- .../juce_core/threads/juce_CriticalSection.h | 148 +- .../juce_core/threads/juce_DynamicLibrary.h | 37 +- .../threads/juce_HighResolutionTimer.cpp | 36 + .../threads/juce_HighResolutionTimer.h | 109 + .../juce_core/threads/juce_InterProcessLock.h | 68 +- .../modules/juce_core/threads/juce_Process.h | 83 +- .../juce_core/threads/juce_ReadWriteLock.cpp | 111 +- .../juce_core/threads/juce_ReadWriteLock.h | 74 +- .../juce_core/threads/juce_ScopedLock.h | 41 +- .../juce_core/threads/juce_ScopedReadLock.h | 39 +- .../juce_core/threads/juce_ScopedWriteLock.h | 39 +- .../modules/juce_core/threads/juce_SpinLock.h | 39 +- .../modules/juce_core/threads/juce_Thread.cpp | 63 +- .../modules/juce_core/threads/juce_Thread.h | 66 +- .../juce_core/threads/juce_ThreadLocalValue.h | 50 +- .../juce_core/threads/juce_ThreadPool.cpp | 157 +- .../juce_core/threads/juce_ThreadPool.h | 62 +- .../threads/juce_TimeSliceThread.cpp | 44 +- .../juce_core/threads/juce_TimeSliceThread.h | 42 +- .../juce_core/threads/juce_WaitableEvent.h | 52 +- .../time/juce_PerformanceCounter.cpp | 154 +- .../juce_core/time/juce_PerformanceCounter.h | 74 +- .../juce_core/time/juce_RelativeTime.cpp | 156 +- .../juce_core/time/juce_RelativeTime.h | 86 +- .../modules/juce_core/time/juce_Time.cpp | 203 +- .../modules/juce_core/time/juce_Time.h | 61 +- .../juce_core/unit_tests/juce_UnitTest.cpp | 72 +- .../juce_core/unit_tests/juce_UnitTest.h | 68 +- .../juce_core/xml/juce_XmlDocument.cpp | 343 +- .../modules/juce_core/xml/juce_XmlDocument.h | 68 +- .../modules/juce_core/xml/juce_XmlElement.cpp | 392 +- .../modules/juce_core/xml/juce_XmlElement.h | 224 +- .../zip/juce_GZIPCompressorOutputStream.cpp | 68 +- .../zip/juce_GZIPCompressorOutputStream.h | 53 +- .../zip/juce_GZIPDecompressorInputStream.cpp | 75 +- .../zip/juce_GZIPDecompressorInputStream.h | 58 +- .../modules/juce_core/zip/juce_ZipFile.cpp | 141 +- .../modules/juce_core/zip/juce_ZipFile.h | 63 +- .../modules/juce_core/zip/zlib/crc32.c | 16 +- .../modules/juce_core/zip/zlib/deflate.c | 3 +- .../modules/juce_core/zip/zlib/inflate.c | 2 +- .../modules/juce_core/zip/zlib/trees.c | 2 +- .../juce_ApplicationProperties.cpp | 17 +- .../juce_ApplicationProperties.h | 34 +- .../app_properties/juce_PropertiesFile.cpp | 350 +- .../app_properties/juce_PropertiesFile.h | 48 +- .../juce_data_structures.cpp | 21 +- .../juce_data_structures.h | 49 +- .../juce_data_structures.mm | 17 +- .../juce_data_structures/juce_module_info | 2 +- .../undomanager/juce_UndoManager.cpp | 296 +- .../undomanager/juce_UndoManager.h | 59 +- .../undomanager/juce_UndoableAction.h | 23 +- .../values/juce_Value.cpp | 82 +- .../juce_data_structures/values/juce_Value.h | 41 +- .../values/juce_ValueTree.cpp | 420 +- .../values/juce_ValueTree.h | 92 +- .../broadcasters/juce_ActionBroadcaster.cpp | 28 +- .../broadcasters/juce_ActionBroadcaster.h | 29 +- .../broadcasters/juce_ActionListener.h | 28 +- .../broadcasters/juce_AsyncUpdater.cpp | 40 +- .../broadcasters/juce_AsyncUpdater.h | 32 +- .../broadcasters/juce_ChangeBroadcaster.cpp | 17 +- .../broadcasters/juce_ChangeBroadcaster.h | 31 +- .../broadcasters/juce_ChangeListener.h | 23 +- .../broadcasters/juce_ListenerList.h | 56 +- .../juce_ConnectedChildProcess.cpp | 259 + .../interprocess/juce_ConnectedChildProcess.h | 179 + .../juce_InterprocessConnection.cpp | 175 +- .../juce_InterprocessConnection.h | 64 +- .../juce_InterprocessConnectionServer.cpp | 25 +- .../juce_InterprocessConnectionServer.h | 29 +- .../modules/juce_events/juce_events.cpp | 24 +- .../modules/juce_events/juce_events.h | 97 +- .../modules/juce_events/juce_events.mm | 17 +- .../modules/juce_events/juce_module_info | 6 +- .../messages/juce_ApplicationBase.cpp | 264 +- .../messages/juce_ApplicationBase.h | 174 +- .../messages/juce_CallbackMessage.h | 27 +- .../messages/juce_DeletedAtShutdown.cpp | 19 +- .../messages/juce_DeletedAtShutdown.h | 29 +- .../messages/juce_Initialisation.h | 118 + .../juce_events/messages/juce_Message.h | 27 +- .../messages/juce_MessageListener.cpp | 20 +- .../messages/juce_MessageListener.h | 25 +- .../messages/juce_MessageManager.cpp | 99 +- .../messages/juce_MessageManager.h | 35 +- .../messages/juce_NotificationType.h | 42 + .../juce_events/native/juce_ScopedXLock.h | 23 +- .../native/juce_android_Messaging.cpp | 19 +- .../native/juce_ios_MessageManager.mm | 50 +- .../native/juce_linux_Messaging.cpp | 58 +- .../native/juce_mac_MessageManager.mm | 518 +- .../native/juce_osx_MessageQueue.h | 36 +- .../native/juce_win32_HiddenMessageWindow.h | 38 +- .../native/juce_win32_Messaging.cpp | 22 +- .../juce_events/timers/juce_MultiTimer.cpp | 102 +- .../juce_events/timers/juce_MultiTimer.h | 48 +- .../modules/juce_events/timers/juce_Timer.cpp | 98 +- .../modules/juce_events/timers/juce_Timer.h | 25 +- .../juce_graphics/colour/juce_Colour.cpp | 245 +- .../juce_graphics/colour/juce_Colour.h | 69 +- .../colour/juce_ColourGradient.cpp | 29 +- .../colour/juce_ColourGradient.h | 50 +- .../juce_graphics/colour/juce_Colours.cpp | 19 +- .../juce_graphics/colour/juce_Colours.h | 29 +- .../juce_graphics/colour/juce_FillType.cpp | 23 +- .../juce_graphics/colour/juce_FillType.h | 44 +- .../juce_graphics/colour/juce_PixelFormats.h | 156 +- .../contexts/juce_GraphicsContext.cpp | 490 +- .../contexts/juce_GraphicsContext.h | 290 +- .../contexts/juce_LowLevelGraphicsContext.h | 73 +- ...uce_LowLevelGraphicsPostScriptRenderer.cpp | 73 +- .../juce_LowLevelGraphicsPostScriptRenderer.h | 96 +- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 2229 +------- .../juce_LowLevelGraphicsSoftwareRenderer.h | 88 +- .../effects/juce_DropShadowEffect.cpp | 219 +- .../effects/juce_DropShadowEffect.h | 86 +- .../juce_graphics/effects/juce_GlowEffect.cpp | 23 +- .../juce_graphics/effects/juce_GlowEffect.h | 31 +- .../effects/juce_ImageEffectFilter.h | 29 +- .../fonts/juce_AttributedString.cpp | 53 +- .../fonts/juce_AttributedString.h | 51 +- .../fonts/juce_CustomTypeface.cpp | 180 +- .../juce_graphics/fonts/juce_CustomTypeface.h | 91 +- .../modules/juce_graphics/fonts/juce_Font.cpp | 435 +- .../modules/juce_graphics/fonts/juce_Font.h | 188 +- .../fonts/juce_GlyphArrangement.cpp | 301 +- .../fonts/juce_GlyphArrangement.h | 69 +- .../juce_graphics/fonts/juce_TextLayout.cpp | 234 +- .../juce_graphics/fonts/juce_TextLayout.h | 42 +- .../juce_graphics/fonts/juce_Typeface.cpp | 236 +- .../juce_graphics/fonts/juce_Typeface.h | 81 +- .../geometry/juce_AffineTransform.cpp | 47 +- .../geometry/juce_AffineTransform.h | 62 +- .../juce_graphics/geometry/juce_BorderSize.h | 27 +- .../juce_graphics/geometry/juce_EdgeTable.cpp | 416 +- .../juce_graphics/geometry/juce_EdgeTable.h | 57 +- .../juce_graphics/geometry/juce_Line.h | 110 +- .../juce_graphics/geometry/juce_Path.cpp | 306 +- .../juce_graphics/geometry/juce_Path.h | 160 +- .../geometry/juce_PathIterator.cpp | 77 +- .../geometry/juce_PathIterator.h | 27 +- .../geometry/juce_PathStrokeType.cpp | 35 +- .../geometry/juce_PathStrokeType.h | 42 +- .../juce_graphics/geometry/juce_Point.h | 194 +- .../juce_graphics/geometry/juce_Rectangle.h | 377 +- .../geometry/juce_RectangleList.cpp | 515 -- .../geometry/juce_RectangleList.h | 555 +- .../image_formats/jpglib/jcparam.c | 4 +- .../image_formats/juce_GIFLoader.cpp | 22 +- .../image_formats/juce_JPEGLoader.cpp | 59 +- .../image_formats/juce_PNGLoader.cpp | 297 +- .../juce_graphics/image_formats/pnglib/png.c | 4621 ++++++++++++++-- .../juce_graphics/image_formats/pnglib/png.h | 4579 ++++++++-------- .../image_formats/pnglib/pngconf.h | 1941 ++----- .../image_formats/pnglib/pngerror.c | 921 +++- .../image_formats/pnglib/pngget.c | 1148 ++-- .../image_formats/pnglib/pnginfo.h | 260 + .../image_formats/pnglib/pngmem.c | 705 +-- .../image_formats/pnglib/pngpread.c | 1379 ++--- .../image_formats/pnglib/pngpriv.h | 1904 +++++++ .../image_formats/pnglib/pngread.c | 4414 +++++++++++---- .../image_formats/pnglib/pngrio.c | 157 +- .../image_formats/pnglib/pngrtran.c | 4769 +++++++++------- .../image_formats/pnglib/pngrutil.c | 4863 +++++++++++------ .../image_formats/pnglib/pngset.c | 2013 ++++--- .../image_formats/pnglib/pngstruct.h | 489 ++ .../image_formats/pnglib/pngtrans.c | 702 ++- .../image_formats/pnglib/pngwio.c | 246 +- .../image_formats/pnglib/pngwrite.c | 2521 ++++++--- .../image_formats/pnglib/pngwtran.c | 293 +- .../image_formats/pnglib/pngwutil.c | 2898 +++++----- .../juce_graphics/images/juce_Image.cpp | 146 +- .../modules/juce_graphics/images/juce_Image.h | 95 +- .../juce_graphics/images/juce_ImageCache.cpp | 49 +- .../juce_graphics/images/juce_ImageCache.h | 31 +- .../images/juce_ImageConvolutionKernel.cpp | 64 +- .../images/juce_ImageConvolutionKernel.h | 27 +- .../images/juce_ImageFileFormat.cpp | 69 +- .../images/juce_ImageFileFormat.h | 73 +- .../modules/juce_graphics/juce_graphics.cpp | 88 +- .../modules/juce_graphics/juce_graphics.h | 171 +- .../modules/juce_graphics/juce_graphics.mm | 17 +- .../modules/juce_graphics/juce_module_info | 7 +- .../native/juce_RenderingHelpers.h | 2601 ++++++++- .../native/juce_android_Fonts.cpp | 237 +- .../native/juce_android_GraphicsContext.cpp | 19 +- .../native/juce_freetype_Fonts.cpp | 455 ++ .../juce_graphics/native/juce_linux_Fonts.cpp | 509 +- .../native/juce_mac_CoreGraphicsContext.h | 99 +- .../native/juce_mac_CoreGraphicsContext.mm | 334 +- .../native/juce_mac_CoreGraphicsHelpers.h | 32 +- .../juce_graphics/native/juce_mac_Fonts.mm | 956 ++-- .../juce_win32_Direct2DGraphicsContext.cpp | 294 +- .../juce_win32_DirectWriteTypeLayout.cpp | 280 +- .../native/juce_win32_DirectWriteTypeface.cpp | 168 +- .../juce_graphics/native/juce_win32_Fonts.cpp | 339 +- .../placement/juce_Justification.cpp | 45 - .../placement/juce_Justification.h | 50 +- .../placement/juce_RectanglePlacement.cpp | 19 +- .../placement/juce_RectanglePlacement.h | 57 +- .../application/juce_Application.cpp | 220 +- .../application/juce_Application.h | 164 +- .../application/juce_Initialisation.h | 135 - .../buttons/juce_ArrowButton.cpp | 70 +- .../buttons/juce_ArrowButton.h | 44 +- .../juce_gui_basics/buttons/juce_Button.cpp | 319 +- .../juce_gui_basics/buttons/juce_Button.h | 242 +- .../buttons/juce_DrawableButton.cpp | 179 +- .../buttons/juce_DrawableButton.h | 120 +- .../buttons/juce_HyperlinkButton.cpp | 36 +- .../buttons/juce_HyperlinkButton.h | 45 +- .../buttons/juce_ImageButton.cpp | 76 +- .../buttons/juce_ImageButton.h | 54 +- .../buttons/juce_ShapeButton.cpp | 84 +- .../buttons/juce_ShapeButton.h | 52 +- .../buttons/juce_TextButton.cpp | 63 +- .../juce_gui_basics/buttons/juce_TextButton.h | 79 +- .../buttons/juce_ToggleButton.cpp | 33 +- .../buttons/juce_ToggleButton.h | 48 +- .../buttons/juce_ToolbarButton.cpp | 30 +- .../buttons/juce_ToolbarButton.h | 47 +- .../commands/juce_ApplicationCommandID.h | 68 +- .../commands/juce_ApplicationCommandInfo.cpp | 24 +- .../commands/juce_ApplicationCommandInfo.h | 31 +- .../juce_ApplicationCommandManager.cpp | 92 +- .../commands/juce_ApplicationCommandManager.h | 75 +- .../juce_ApplicationCommandTarget.cpp | 55 +- .../commands/juce_ApplicationCommandTarget.h | 34 +- .../commands/juce_KeyPressMappingSet.cpp | 153 +- .../commands/juce_KeyPressMappingSet.h | 68 +- .../components/juce_CachedComponentImage.h | 39 +- .../components/juce_Component.cpp | 807 +-- .../components/juce_Component.h | 307 +- .../components/juce_ComponentListener.cpp | 17 +- .../components/juce_ComponentListener.h | 30 +- .../components/juce_Desktop.cpp | 348 +- .../juce_gui_basics/components/juce_Desktop.h | 218 +- .../components/juce_ModalComponentManager.cpp | 81 +- .../components/juce_ModalComponentManager.h | 66 +- .../drawables/juce_Drawable.cpp | 41 +- .../juce_gui_basics/drawables/juce_Drawable.h | 61 +- .../drawables/juce_DrawableComposite.cpp | 61 +- .../drawables/juce_DrawableComposite.h | 40 +- .../drawables/juce_DrawableImage.cpp | 21 +- .../drawables/juce_DrawableImage.h | 60 +- .../drawables/juce_DrawablePath.cpp | 61 +- .../drawables/juce_DrawablePath.h | 34 +- .../drawables/juce_DrawableRectangle.cpp | 21 +- .../drawables/juce_DrawableRectangle.h | 30 +- .../drawables/juce_DrawableShape.cpp | 51 +- .../drawables/juce_DrawableShape.h | 34 +- .../drawables/juce_DrawableText.cpp | 49 +- .../drawables/juce_DrawableText.h | 58 +- .../drawables/juce_SVGParser.cpp | 1022 ++-- ...juce_DirectoryContentsDisplayComponent.cpp | 17 +- .../juce_DirectoryContentsDisplayComponent.h | 29 +- .../juce_DirectoryContentsList.cpp | 84 +- .../filebrowser/juce_DirectoryContentsList.h | 89 +- .../filebrowser/juce_FileBrowserComponent.cpp | 79 +- .../filebrowser/juce_FileBrowserComponent.h | 108 +- .../filebrowser/juce_FileBrowserListener.h | 25 +- .../filebrowser/juce_FileChooser.cpp | 131 +- .../filebrowser/juce_FileChooser.h | 54 +- .../filebrowser/juce_FileChooserDialogBox.cpp | 104 +- .../filebrowser/juce_FileChooserDialogBox.h | 60 +- .../filebrowser/juce_FileFilter.cpp | 38 - .../filebrowser/juce_FileListComponent.cpp | 49 +- .../filebrowser/juce_FileListComponent.h | 57 +- .../filebrowser/juce_FilePreviewComponent.h | 27 +- .../juce_FileSearchPathListComponent.cpp | 29 +- .../juce_FileSearchPathListComponent.h | 53 +- .../filebrowser/juce_FileTreeComponent.cpp | 175 +- .../filebrowser/juce_FileTreeComponent.h | 37 +- .../filebrowser/juce_FilenameComponent.cpp | 59 +- .../filebrowser/juce_FilenameComponent.h | 82 +- .../juce_ImagePreviewComponent.cpp | 25 +- .../filebrowser/juce_ImagePreviewComponent.h | 33 +- .../juce_gui_basics/juce_gui_basics.cpp | 66 +- .../modules/juce_gui_basics/juce_gui_basics.h | 559 +- .../juce_gui_basics/juce_gui_basics.mm | 17 +- .../modules/juce_gui_basics/juce_module_info | 5 +- .../keyboard/juce_CaretComponent.cpp | 24 +- .../keyboard/juce_CaretComponent.h | 37 +- .../keyboard/juce_KeyListener.cpp | 17 +- .../keyboard/juce_KeyListener.h | 28 +- .../keyboard/juce_KeyPress.cpp | 70 +- .../juce_gui_basics/keyboard/juce_KeyPress.h | 42 +- .../keyboard/juce_KeyboardFocusTraverser.cpp | 22 +- .../keyboard/juce_KeyboardFocusTraverser.h | 31 +- .../keyboard/juce_ModifierKeys.cpp | 36 +- .../keyboard/juce_ModifierKeys.h | 40 +- .../keyboard/juce_SystemClipboard.h | 23 +- .../keyboard/juce_TextEditorKeyMapper.h | 79 +- .../keyboard/juce_TextInputTarget.h | 40 +- .../layout/juce_AnimatedPosition.h | 209 + .../layout/juce_AnimatedPositionBehaviours.h | 151 + .../layout/juce_ComponentAnimator.cpp | 70 +- .../layout/juce_ComponentAnimator.h | 27 +- .../juce_ComponentBoundsConstrainer.cpp | 78 +- .../layout/juce_ComponentBoundsConstrainer.h | 29 +- .../layout/juce_ComponentBuilder.cpp | 201 +- .../layout/juce_ComponentBuilder.h | 64 +- .../layout/juce_ComponentMovementWatcher.cpp | 42 +- .../layout/juce_ComponentMovementWatcher.h | 35 +- .../layout/juce_ConcertinaPanel.cpp | 411 ++ .../layout/juce_ConcertinaPanel.h | 131 + .../layout/juce_GroupComponent.cpp | 54 +- .../layout/juce_GroupComponent.h | 47 +- .../layout/juce_MultiDocumentPanel.cpp | 121 +- .../layout/juce_MultiDocumentPanel.h | 60 +- .../layout/juce_ResizableBorderComponent.cpp | 31 +- .../layout/juce_ResizableBorderComponent.h | 83 +- .../layout/juce_ResizableCornerComponent.cpp | 21 +- .../layout/juce_ResizableCornerComponent.h | 39 +- .../layout/juce_ResizableEdgeComponent.cpp | 25 +- .../layout/juce_ResizableEdgeComponent.h | 35 +- .../juce_gui_basics/layout/juce_ScrollBar.cpp | 197 +- .../juce_gui_basics/layout/juce_ScrollBar.h | 191 +- .../layout/juce_StretchableLayoutManager.cpp | 52 +- .../layout/juce_StretchableLayoutManager.h | 27 +- .../juce_StretchableLayoutResizerBar.cpp | 33 +- .../layout/juce_StretchableLayoutResizerBar.h | 44 +- .../layout/juce_StretchableObjectResizer.cpp | 71 +- .../layout/juce_StretchableObjectResizer.h | 27 +- .../layout/juce_TabbedButtonBar.cpp | 307 +- .../layout/juce_TabbedButtonBar.h | 186 +- .../layout/juce_TabbedComponent.cpp | 50 +- .../layout/juce_TabbedComponent.h | 78 +- .../juce_gui_basics/layout/juce_Viewport.cpp | 168 +- .../juce_gui_basics/layout/juce_Viewport.h | 79 +- .../lookandfeel/juce_LookAndFeel.cpp | 2986 +--------- .../lookandfeel/juce_LookAndFeel.h | 660 +-- .../lookandfeel/juce_LookAndFeel_V1.cpp} | 148 +- .../lookandfeel/juce_LookAndFeel_V1.h | 101 + .../lookandfeel/juce_LookAndFeel_V2.cpp | 2875 ++++++++++ .../lookandfeel/juce_LookAndFeel_V2.h | 346 ++ .../lookandfeel/juce_LookAndFeel_V3.cpp | 633 +++ .../lookandfeel/juce_LookAndFeel_V3.h | 96 + .../menus/juce_MenuBarComponent.cpp | 21 +- .../menus/juce_MenuBarComponent.h | 58 +- .../menus/juce_MenuBarModel.cpp | 17 +- .../juce_gui_basics/menus/juce_MenuBarModel.h | 50 +- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 1135 ++-- .../juce_gui_basics/menus/juce_PopupMenu.h | 239 +- .../misc/juce_BubbleComponent.cpp | 152 +- .../misc/juce_BubbleComponent.h | 70 +- .../misc/juce_DropShadower.cpp | 304 +- .../juce_gui_basics/misc/juce_DropShadower.h | 70 +- .../mouse/juce_ComponentDragger.cpp | 19 +- .../mouse/juce_ComponentDragger.h | 34 +- .../mouse/juce_DragAndDropContainer.cpp | 231 +- .../mouse/juce_DragAndDropContainer.h | 42 +- .../mouse/juce_DragAndDropTarget.h | 29 +- .../mouse/juce_FileDragAndDropTarget.h | 27 +- .../mouse/juce_LassoComponent.h | 71 +- .../mouse/juce_MouseCursor.cpp | 92 +- .../juce_gui_basics/mouse/juce_MouseCursor.h | 69 +- .../juce_gui_basics/mouse/juce_MouseEvent.cpp | 49 +- .../juce_gui_basics/mouse/juce_MouseEvent.h | 132 +- .../mouse/juce_MouseInactivityDetector.cpp | 73 + .../mouse/juce_MouseInactivityDetector.h | 106 + .../mouse/juce_MouseInputSource.cpp | 391 +- .../mouse/juce_MouseInputSource.h | 70 +- .../mouse/juce_MouseListener.cpp | 19 +- .../mouse/juce_MouseListener.h | 91 +- .../mouse/juce_SelectedItemSet.h | 89 +- .../mouse/juce_TextDragAndDropTarget.h | 105 + .../mouse/juce_TooltipClient.h | 23 +- .../native/juce_MultiTouchMapper.h | 25 +- .../native/juce_android_FileChooser.cpp | 17 +- .../native/juce_android_Windowing.cpp | 352 +- .../native/juce_ios_UIViewComponentPeer.mm | 617 +-- .../native/juce_ios_Windowing.mm | 165 +- .../native/juce_linux_Clipboard.cpp | 161 +- .../native/juce_linux_FileChooser.cpp | 143 +- .../native/juce_linux_Windowing.cpp | 1712 +++--- .../native/juce_mac_FileChooser.mm | 337 +- .../native/juce_mac_MainMenu.mm | 536 +- .../native/juce_mac_MouseCursor.mm | 173 +- .../native/juce_mac_NSViewComponentPeer.mm | 3032 +++++----- .../native/juce_mac_Windowing.mm | 353 +- .../native/juce_win32_DragAndDrop.cpp | 25 +- .../native/juce_win32_FileChooser.cpp | 92 +- .../native/juce_win32_Windowing.cpp | 1594 +++--- .../positioning/juce_MarkerList.cpp | 38 +- .../positioning/juce_MarkerList.h | 40 +- .../positioning/juce_RelativeCoordinate.cpp | 21 +- .../positioning/juce_RelativeCoordinate.h | 35 +- .../juce_RelativeCoordinatePositioner.cpp | 103 +- .../juce_RelativeCoordinatePositioner.h | 35 +- .../juce_RelativeParallelogram.cpp | 25 +- .../positioning/juce_RelativeParallelogram.h | 37 +- .../positioning/juce_RelativePoint.cpp | 23 +- .../positioning/juce_RelativePoint.h | 35 +- .../positioning/juce_RelativePointPath.cpp | 19 +- .../positioning/juce_RelativePointPath.h | 47 +- .../positioning/juce_RelativeRectangle.cpp | 27 +- .../positioning/juce_RelativeRectangle.h | 29 +- .../juce_BooleanPropertyComponent.cpp | 25 +- .../juce_BooleanPropertyComponent.h | 34 +- .../juce_ButtonPropertyComponent.cpp | 19 +- .../properties/juce_ButtonPropertyComponent.h | 28 +- .../juce_ChoicePropertyComponent.cpp | 62 +- .../properties/juce_ChoicePropertyComponent.h | 37 +- .../properties/juce_PropertyComponent.cpp | 38 +- .../properties/juce_PropertyComponent.h | 65 +- .../properties/juce_PropertyPanel.cpp | 101 +- .../properties/juce_PropertyPanel.h | 59 +- .../juce_SliderPropertyComponent.cpp | 23 +- .../properties/juce_SliderPropertyComponent.h | 28 +- .../properties/juce_TextPropertyComponent.cpp | 67 +- .../properties/juce_TextPropertyComponent.h | 51 +- .../juce_gui_basics/widgets/juce_ComboBox.cpp | 218 +- .../juce_gui_basics/widgets/juce_ComboBox.h | 147 +- .../widgets/juce_ImageComponent.cpp | 48 +- .../widgets/juce_ImageComponent.h | 45 +- .../juce_gui_basics/widgets/juce_Label.cpp | 124 +- .../juce_gui_basics/widgets/juce_Label.h | 146 +- .../juce_gui_basics/widgets/juce_ListBox.cpp | 331 +- .../juce_gui_basics/widgets/juce_ListBox.h | 103 +- .../widgets/juce_ProgressBar.cpp | 17 +- .../widgets/juce_ProgressBar.h | 55 +- .../juce_gui_basics/widgets/juce_Slider.cpp | 2771 +++++----- .../juce_gui_basics/widgets/juce_Slider.h | 466 +- .../widgets/juce_TableHeaderComponent.cpp | 107 +- .../widgets/juce_TableHeaderComponent.h | 67 +- .../widgets/juce_TableListBox.cpp | 125 +- .../widgets/juce_TableListBox.h | 67 +- .../widgets/juce_TextEditor.cpp | 770 ++- .../juce_gui_basics/widgets/juce_TextEditor.h | 319 +- .../juce_gui_basics/widgets/juce_Toolbar.cpp | 273 +- .../juce_gui_basics/widgets/juce_Toolbar.h | 76 +- .../widgets/juce_ToolbarItemComponent.cpp | 75 +- .../widgets/juce_ToolbarItemComponent.h | 37 +- .../widgets/juce_ToolbarItemFactory.h | 25 +- .../widgets/juce_ToolbarItemPalette.cpp | 48 +- .../widgets/juce_ToolbarItemPalette.h | 36 +- .../juce_gui_basics/widgets/juce_TreeView.cpp | 862 +-- .../juce_gui_basics/widgets/juce_TreeView.h | 233 +- .../windows/juce_AlertWindow.cpp | 205 +- .../windows/juce_AlertWindow.h | 111 +- .../windows/juce_CallOutBox.cpp | 183 +- .../juce_gui_basics/windows/juce_CallOutBox.h | 109 +- .../windows/juce_ComponentPeer.cpp | 481 +- .../windows/juce_ComponentPeer.h | 168 +- .../windows/juce_DialogWindow.cpp | 150 +- .../windows/juce_DialogWindow.h | 143 +- .../windows/juce_DocumentWindow.cpp | 135 +- .../windows/juce_DocumentWindow.h | 89 +- .../windows/juce_NativeMessageBox.h | 61 +- .../windows/juce_ResizableWindow.cpp | 121 +- .../windows/juce_ResizableWindow.h | 109 +- .../windows/juce_ThreadWithProgressWindow.cpp | 83 +- .../windows/juce_ThreadWithProgressWindow.h | 64 +- .../windows/juce_TooltipWindow.cpp | 120 +- .../windows/juce_TooltipWindow.h | 62 +- .../windows/juce_TopLevelWindow.cpp | 186 +- .../windows/juce_TopLevelWindow.h | 57 +- .../juce_CPlusPlusCodeTokeniser.cpp | 607 +- .../code_editor/juce_CPlusPlusCodeTokeniser.h | 54 +- .../juce_CPlusPlusCodeTokeniserFunctions.h | 541 ++ .../code_editor/juce_CodeDocument.cpp | 476 +- .../code_editor/juce_CodeDocument.h | 116 +- .../code_editor/juce_CodeEditorComponent.cpp | 1170 ++-- .../code_editor/juce_CodeEditorComponent.h | 263 +- .../code_editor/juce_CodeTokeniser.h | 42 +- .../code_editor/juce_LuaCodeTokeniser.cpp | 233 + .../code_editor/juce_LuaCodeTokeniser.h | 63 + .../code_editor/juce_XMLCodeTokeniser.cpp | 166 + .../code_editor/juce_XMLCodeTokeniser.h | 62 + .../documents/juce_FileBasedDocument.cpp | 152 +- .../documents/juce_FileBasedDocument.h | 60 +- .../embedding/juce_ActiveXControlComponent.h | 31 +- .../embedding/juce_NSViewComponent.h | 28 +- .../embedding/juce_UIViewComponent.h | 29 +- .../modules/juce_gui_extra/juce_gui_extra.cpp | 38 +- .../modules/juce_gui_extra/juce_gui_extra.h | 110 +- .../modules/juce_gui_extra/juce_gui_extra.mm | 17 +- .../modules/juce_gui_extra/juce_module_info | 2 +- .../lookandfeel/juce_OldSchoolLookAndFeel.h | 157 - .../juce_gui_extra/misc/juce_AppleRemote.h | 25 +- .../misc/juce_BubbleMessageComponent.cpp | 87 +- .../misc/juce_BubbleMessageComponent.h | 39 +- .../misc/juce_ColourSelector.cpp | 155 +- .../juce_gui_extra/misc/juce_ColourSelector.h | 43 +- .../misc/juce_KeyMappingEditorComponent.cpp | 177 +- .../misc/juce_KeyMappingEditorComponent.h | 42 +- .../misc/juce_LiveConstantEditor.cpp | 466 ++ .../misc/juce_LiveConstantEditor.h | 298 + .../misc/juce_PreferencesPanel.cpp | 32 +- .../misc/juce_PreferencesPanel.h | 33 +- .../misc/juce_RecentlyOpenedFilesList.cpp | 32 +- .../misc/juce_RecentlyOpenedFilesList.h | 31 +- .../juce_gui_extra/misc/juce_SplashScreen.cpp | 93 +- .../juce_gui_extra/misc/juce_SplashScreen.h | 179 +- .../misc/juce_SystemTrayIconComponent.cpp | 19 +- .../misc/juce_SystemTrayIconComponent.h | 56 +- .../misc/juce_WebBrowserComponent.h | 57 +- .../juce_android_WebBrowserComponent.cpp | 34 +- .../native/juce_ios_UIViewComponent.mm | 37 +- .../native/juce_linux_SystemTrayIcon.cpp | 46 +- .../native/juce_linux_WebBrowserComponent.cpp | 34 +- .../native/juce_mac_AppleRemote.mm | 17 +- .../juce_mac_CarbonViewWrapperComponent.h | 121 +- .../native/juce_mac_NSViewComponent.mm | 128 +- .../native/juce_mac_SystemTrayIcon.cpp | 241 + .../native/juce_mac_WebBrowserComponent.mm | 174 +- .../native/juce_win32_ActiveXComponent.cpp | 71 +- .../native/juce_win32_SystemTrayIcon.cpp | 101 +- .../native/juce_win32_WebBrowserComponent.cpp | 84 +- OpenShotLibrary.jucer | 38 +- 891 files changed, 103385 insertions(+), 70181 deletions(-) create mode 100644 JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp create mode 100644 JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h create mode 100644 JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp create mode 100644 JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h create mode 100644 JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h create mode 100644 JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h create mode 100644 JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h create mode 100644 JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp create mode 100644 JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h create mode 100644 JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp create mode 100644 JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h create mode 100644 JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h delete mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/juce_DirectXPluginFormat.h create mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp create mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h create mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h create mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp create mode 100644 JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h create mode 100644 JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp rename JuceLibraryCode/modules/{juce_gui_basics/filebrowser => juce_core/files}/juce_FileFilter.h (54%) rename JuceLibraryCode/modules/{juce_gui_basics/filebrowser => juce_core/files}/juce_WildcardFileFilter.cpp (50%) rename JuceLibraryCode/modules/{juce_gui_basics/filebrowser => juce_core/files}/juce_WildcardFileFilter.h (59%) rename JuceLibraryCode/modules/juce_core/{json => javascript}/juce_JSON.cpp (71%) rename JuceLibraryCode/modules/juce_core/{json => javascript}/juce_JSON.h (55%) create mode 100644 JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp create mode 100644 JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h create mode 100644 JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h create mode 100644 JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h create mode 100644 JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp delete mode 100644 JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCSuffix.h create mode 100644 JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp create mode 100644 JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h create mode 100644 JuceLibraryCode/modules/juce_core/text/juce_StringRef.h create mode 100644 JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp create mode 100644 JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h create mode 100644 JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp create mode 100644 JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h create mode 100644 JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp create mode 100644 JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h create mode 100644 JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h create mode 100644 JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h delete mode 100644 JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.cpp create mode 100644 JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pnginfo.h create mode 100644 JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpriv.h create mode 100644 JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngstruct.h create mode 100644 JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp delete mode 100644 JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.cpp delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/application/juce_Initialisation.h delete mode 100644 JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileFilter.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h create mode 100644 JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h create mode 100644 JuceLibraryCode/modules/juce_gui_basics/layout/juce_ConcertinaPanel.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_basics/layout/juce_ConcertinaPanel.h rename JuceLibraryCode/modules/{juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.cpp => juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp} (72%) create mode 100644 JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h create mode 100644 JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h create mode 100644 JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h create mode 100644 JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h create mode 100644 JuceLibraryCode/modules/juce_gui_basics/mouse/juce_TextDragAndDropTarget.h create mode 100644 JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CPlusPlusCodeTokeniserFunctions.h create mode 100644 JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.h create mode 100644 JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.h delete mode 100644 JuceLibraryCode/modules/juce_gui_extra/lookandfeel/juce_OldSchoolLookAndFeel.h create mode 100644 JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.cpp create mode 100644 JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.h create mode 100644 JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_SystemTrayIcon.cpp diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h index cc2ff36..396f698 100644 --- a/JuceLibraryCode/AppConfig.h +++ b/JuceLibraryCode/AppConfig.h @@ -83,6 +83,10 @@ #define JUCE_USE_MP3AUDIOFORMAT 0 #endif +#ifndef JUCE_USE_LAME_AUDIO_FORMAT + //#define JUCE_USE_LAME_AUDIO_FORMAT +#endif + #ifndef JUCE_USE_WINDOWS_MEDIA_FORMAT #define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 #endif @@ -94,6 +98,10 @@ //#define JUCE_PLUGINHOST_VST #endif +#ifndef JUCE_PLUGINHOST_VST3 + //#define JUCE_PLUGINHOST_VST3 +#endif + #ifndef JUCE_PLUGINHOST_AU //#define JUCE_PLUGINHOST_AU #endif @@ -117,6 +125,10 @@ //#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #endif +#ifndef JUCE_INCLUDE_ZLIB_CODE + //#define JUCE_INCLUDE_ZLIB_CODE +#endif + //============================================================================== // juce_graphics flags: @@ -154,5 +166,9 @@ #define JUCE_WEB_BROWSER 0 #endif +#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR + //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR +#endif + #endif // __JUCE_APPCONFIG_IIUU5J__ diff --git a/JuceLibraryCode/JuceHeader.h b/JuceLibraryCode/JuceHeader.h index c0dd914..ad33f86 100644 --- a/JuceLibraryCode/JuceHeader.h +++ b/JuceLibraryCode/JuceHeader.h @@ -34,8 +34,8 @@ namespace ProjectInfo { const char* const projectName = "libopenshot-audio"; - const char* const versionString = "0.1.0"; - const int versionNumber = 0x100; + const char* const versionString = "0.0.1"; + const int versionNumber = 0x1; } #endif // __APPHEADERFILE_IIUU5J__ diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp index 0bd62d5..95d228e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -483,17 +482,16 @@ public: template struct Test5 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - test (unitTest, false); - test (unitTest, true); + test (unitTest, false, r); + test (unitTest, true, r); } - static void test (UnitTest& unitTest, bool inPlace) + static void test (UnitTest& unitTest, bool inPlace, Random& r) { const int numSamples = 2048; int32 original [numSamples], converted [numSamples], reversed [numSamples]; - Random r; { AudioData::Pointer d (original); @@ -550,49 +548,50 @@ public: template struct Test3 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - Test5 ::test (unitTest); - Test5 ::test (unitTest); + Test5 ::test (unitTest, r); + Test5 ::test (unitTest, r); } }; template struct Test2 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); } }; template struct Test1 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - Test2 ::test (unitTest); - Test2 ::test (unitTest); + Test2 ::test (unitTest, r); + Test2 ::test (unitTest, r); } }; void runTest() { + Random r = getRandom(); beginTest ("Round-trip conversion: Int8"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int16"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int24"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int32"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Float32"); - Test1 ::test (*this); + Test1 ::test (*this, r); } }; diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h index e705514..b3f49a6 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioDataConverters.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ -#define __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ +#ifndef JUCE_AUDIODATACONVERTERS_H_INCLUDED +#define JUCE_AUDIODATACONVERTERS_H_INCLUDED //============================================================================== @@ -72,9 +71,9 @@ public: { public: template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatBE(); } - template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); } + template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatBE (newValue); } template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32BE(); } - template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); } + template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32BE (newValue); } template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromBE (source); } enum { isBigEndian = 1 }; }; @@ -83,9 +82,9 @@ public: { public: template static inline float getAsFloat (SampleFormatType& s) noexcept { return s.getAsFloatLE(); } - template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); } + template static inline void setAsFloat (SampleFormatType& s, float newValue) noexcept { s.setAsFloatLE (newValue); } template static inline int32 getAsInt32 (SampleFormatType& s) noexcept { return s.getAsInt32LE(); } - template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); } + template static inline void setAsInt32 (SampleFormatType& s, int32 newValue) noexcept { s.setAsInt32LE (newValue); } template static inline void copyFrom (DestType& dest, SourceType& source) noexcept { dest.copyFromLE (source); } enum { isBigEndian = 0 }; }; @@ -100,7 +99,7 @@ public: class Int8 { public: - inline Int8 (void* data_) noexcept : data (static_cast (data_)) {} + inline Int8 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -125,7 +124,7 @@ public: class UInt8 { public: - inline UInt8 (void* data_) noexcept : data (static_cast (data_)) {} + inline UInt8 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -150,7 +149,7 @@ public: class Int16 { public: - inline Int16 (void* data_) noexcept : data (static_cast (data_)) {} + inline Int16 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -175,7 +174,7 @@ public: class Int24 { public: - inline Int24 (void* data_) noexcept : data (static_cast (data_)) {} + inline Int24 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { data += 3; } inline void skip (int numSamples) noexcept { data += 3 * numSamples; } @@ -200,17 +199,17 @@ public: class Int32 { public: - inline Int32 (void* data_) noexcept : data (static_cast (data_)) {} + inline Int32 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } - inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } + inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } - inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); } + inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data); } inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data); } - inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); } + inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue); } inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue); } inline void clear() noexcept { *data = 0; } inline void clearMultiple (int num) noexcept { zeromem (data, (size_t) (num * bytesPerSample)) ;} @@ -222,10 +221,31 @@ public: enum { bytesPerSample = 4, maxValue = 0x7fffffff, resolution = 1, isFloat = 0 }; }; + /** A 32-bit integer type, of which only the bottom 24 bits are used. */ + class Int24in32 : public Int32 + { + public: + inline Int24in32 (void* d) noexcept : Int32 (d) {} + + inline float getAsFloatLE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfBigEndian (*data)); } + inline float getAsFloatBE() const noexcept { return (float) ((1.0 / (1.0 + maxValue)) * (int32) ByteOrder::swapIfLittleEndian (*data)); } + inline void setAsFloatLE (float newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } + inline void setAsFloatBE (float newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) (maxValue * jlimit (-1.0, 1.0, (double) newValue))); } + inline int32 getAsInt32LE() const noexcept { return (int32) ByteOrder::swapIfBigEndian (*data) << 8; } + inline int32 getAsInt32BE() const noexcept { return (int32) ByteOrder::swapIfLittleEndian (*data) << 8; } + inline void setAsInt32LE (int32 newValue) noexcept { *data = ByteOrder::swapIfBigEndian ((uint32) newValue >> 8); } + inline void setAsInt32BE (int32 newValue) noexcept { *data = ByteOrder::swapIfLittleEndian ((uint32) newValue >> 8); } + template inline void copyFromLE (SourceType& source) noexcept { setAsInt32LE (source.getAsInt32()); } + template inline void copyFromBE (SourceType& source) noexcept { setAsInt32BE (source.getAsInt32()); } + inline void copyFromSameType (Int24in32& source) noexcept { *data = *source.data; } + + enum { bytesPerSample = 4, maxValue = 0x7fffff, resolution = (1 << 8), isFloat = 0 }; + }; + class Float32 { public: - inline Float32 (void* data_) noexcept : data (static_cast (data_)) {} + inline Float32 (void* d) noexcept : data (static_cast (d)) {} inline void advance() noexcept { ++data; } inline void skip (int numSamples) noexcept { data += numSamples; } @@ -275,7 +295,7 @@ public: public: inline Interleaved() noexcept : numInterleavedChannels (1) {} inline Interleaved (const Interleaved& other) noexcept : numInterleavedChannels (other.numInterleavedChannels) {} - inline Interleaved (const int numInterleavedChannels_) noexcept : numInterleavedChannels (numInterleavedChannels_) {} + inline Interleaved (const int numInterleavedChans) noexcept : numInterleavedChannels (numInterleavedChans) {} inline void copyFrom (const Interleaved& other) noexcept { numInterleavedChannels = other.numInterleavedChannels; } template inline void advanceData (SampleFormatType& s) noexcept { s.skip (numInterleavedChannels); } template inline void advanceDataBy (SampleFormatType& s, int numSamples) noexcept { s.skip (numInterleavedChannels * numSamples); } @@ -351,8 +371,8 @@ public: /** Creates a pointer from some raw data in the appropriate format with the specified number of interleaved channels. For non-interleaved data, use the other constructor. */ - Pointer (typename Constness::VoidType* sourceData, int numInterleavedChannels) noexcept - : InterleavingType (numInterleavedChannels), data (Constness::toVoidPtr (sourceData)) + Pointer (typename Constness::VoidType* sourceData, int numInterleaved) noexcept + : InterleavingType (numInterleaved), data (Constness::toVoidPtr (sourceData)) { } @@ -422,8 +442,7 @@ public: { static_jassert (Constness::isConst == 0); // trying to write to a const pointer! For a writeable one, use AudioData::NonConst instead! - Pointer dest (*this); - while (--numSamples >= 0) + for (Pointer dest (*this); --numSamples >= 0;) { dest.data.copyFromSameType (source.data); dest.advance(); @@ -467,6 +486,55 @@ public: dest.clear (dest.data, numSamples); } + /** Scans a block of data, returning the lowest and highest levels as floats */ + void findMinAndMax (size_t numSamples, float& minValue, float& maxValue) const noexcept + { + if (numSamples == 0) + { + minValue = maxValue = 0; + return; + } + + Pointer dest (*this); + + if (isFloatingPoint()) + { + float mn = dest.getAsFloat(); + dest.advance(); + float mx = mn; + + while (--numSamples > 0) + { + const float v = dest.getAsFloat(); + dest.advance(); + + if (mx < v) mx = v; + if (v < mn) mn = v; + } + + minValue = mn; + maxValue = mx; + } + else + { + int32 mn = dest.getAsInt32(); + dest.advance(); + int32 mx = mn; + + while (--numSamples > 0) + { + const int v = dest.getAsInt32(); + dest.advance(); + + if (mx < v) mx = v; + if (v < mn) mn = v; + } + + minValue = mn * (float) (1.0 / (1.0 + Int32::maxValue)); + maxValue = mx * (float) (1.0 / (1.0 + Int32::maxValue)); + } + } + /** Returns true if the pointer is using a floating-point format. */ static bool isFloatingPoint() noexcept { return (bool) SampleFormat::isFloat; } @@ -544,9 +612,7 @@ public: : sourceChannels (numSourceChannels), destChannels (numDestChannels) {} - ~ConverterInstance() {} - - void convertSamples (void* dest, const void* source, int numSamples) const + void convertSamples (void* dest, const void* source, int numSamples) const override { SourceSampleType s (source, sourceChannels); DestSampleType d (dest, destChannels); @@ -554,7 +620,7 @@ public: } void convertSamples (void* dest, int destSubChannel, - const void* source, int sourceSubChannel, int numSamples) const + const void* source, int sourceSubChannel, int numSamples) const override { jassert (destSubChannel < destChannels && sourceSubChannel < sourceChannels); @@ -564,7 +630,7 @@ public: } private: - JUCE_DECLARE_NON_COPYABLE (ConverterInstance); + JUCE_DECLARE_NON_COPYABLE (ConverterInstance) const int sourceChannels, destChannels; }; @@ -637,8 +703,8 @@ public: private: AudioDataConverters(); - JUCE_DECLARE_NON_COPYABLE (AudioDataConverters); + JUCE_DECLARE_NON_COPYABLE (AudioDataConverters) }; -#endif // __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ +#endif // JUCE_AUDIODATACONVERTERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp index 2d46482..72d7f0d 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp @@ -1,48 +1,68 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioSampleBuffer::AudioSampleBuffer (const int numChannels_, +AudioSampleBuffer::AudioSampleBuffer() noexcept + : numChannels (0), size (0), allocatedBytes (0), + channels (static_cast (preallocatedChannelSpace)), + isClear (false) +{ +} + +AudioSampleBuffer::AudioSampleBuffer (const int numChans, const int numSamples) noexcept - : numChannels (numChannels_), + : numChannels (numChans), size (numSamples) { jassert (numSamples >= 0); - jassert (numChannels_ > 0); + jassert (numChans >= 0); allocateData(); } AudioSampleBuffer::AudioSampleBuffer (const AudioSampleBuffer& other) noexcept : numChannels (other.numChannels), - size (other.size) + size (other.size), + allocatedBytes (other.allocatedBytes) { - allocateData(); - const size_t numBytes = sizeof (float) * (size_t) size; + if (allocatedBytes == 0) + { + allocateChannels (other.channels, 0); + } + else + { + allocateData(); - for (int i = 0; i < numChannels; ++i) - memcpy (channels[i], other.channels[i], numBytes); + if (other.isClear) + { + clear(); + } + else + { + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::copy (channels[i], other.channels[i], size); + } + } } void AudioSampleBuffer::allocateData() @@ -50,7 +70,7 @@ void AudioSampleBuffer::allocateData() const size_t channelListSize = sizeof (float*) * (size_t) (numChannels + 1); allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (float) + channelListSize + 32; allocatedData.malloc (allocatedBytes); - channels = reinterpret_cast (allocatedData.getData()); + channels = reinterpret_cast (allocatedData.getData()); float* chan = (float*) (allocatedData + channelListSize); for (int i = 0; i < numChannels; ++i) @@ -59,29 +79,33 @@ void AudioSampleBuffer::allocateData() chan += size; } - channels [numChannels] = 0; + channels [numChannels] = nullptr; + isClear = false; } -AudioSampleBuffer::AudioSampleBuffer (float** dataToReferTo, - const int numChannels_, +AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo, + const int numChans, const int numSamples) noexcept - : numChannels (numChannels_), + : numChannels (numChans), size (numSamples), allocatedBytes (0) { - jassert (numChannels_ > 0); + jassert (dataToReferTo != nullptr); + jassert (numChans >= 0); allocateChannels (dataToReferTo, 0); } -AudioSampleBuffer::AudioSampleBuffer (float** dataToReferTo, - const int numChannels_, +AudioSampleBuffer::AudioSampleBuffer (float* const* dataToReferTo, + const int numChans, const int startSample, const int numSamples) noexcept - : numChannels (numChannels_), + : numChannels (numChans), size (numSamples), - allocatedBytes (0) + allocatedBytes (0), + isClear (false) { - jassert (numChannels_ > 0); + jassert (dataToReferTo != nullptr); + jassert (numChans >= 0); allocateChannels (dataToReferTo, startSample); } @@ -89,7 +113,8 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, const int newNumChannels, const int newNumSamples) noexcept { - jassert (newNumChannels > 0); + jassert (dataToReferTo != nullptr); + jassert (newNumChannels >= 0); allocatedBytes = 0; allocatedData.free(); @@ -98,19 +123,20 @@ void AudioSampleBuffer::setDataToReferTo (float** dataToReferTo, size = newNumSamples; allocateChannels (dataToReferTo, 0); + jassert (! isClear); } -void AudioSampleBuffer::allocateChannels (float** const dataToReferTo, int offset) +void AudioSampleBuffer::allocateChannels (float* const* const dataToReferTo, int offset) { // (try to avoid doing a malloc here, as that'll blow up things like Pro-Tools) if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) { - channels = static_cast (preallocatedChannelSpace); + channels = static_cast (preallocatedChannelSpace); } else { allocatedData.malloc ((size_t) numChannels + 1, sizeof (float*)); - channels = reinterpret_cast (allocatedData.getData()); + channels = reinterpret_cast (allocatedData.getData()); } for (int i = 0; i < numChannels; ++i) @@ -121,7 +147,8 @@ void AudioSampleBuffer::allocateChannels (float** const dataToReferTo, int offse channels[i] = dataToReferTo[i] + offset; } - channels [numChannels] = 0; + channels [numChannels] = nullptr; + isClear = false; } AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) noexcept @@ -130,10 +157,15 @@ AudioSampleBuffer& AudioSampleBuffer::operator= (const AudioSampleBuffer& other) { setSize (other.getNumChannels(), other.getNumSamples(), false, false, false); - const size_t numBytes = sizeof (float) * (size_t) size; - - for (int i = 0; i < numChannels; ++i) - memcpy (channels[i], other.channels[i], numBytes); + if (other.isClear) + { + clear(); + } + else + { + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::copy (channels[i], other.channels[i], size); + } } return *this; @@ -149,33 +181,38 @@ void AudioSampleBuffer::setSize (const int newNumChannels, const bool clearExtraSpace, const bool avoidReallocating) noexcept { - jassert (newNumChannels > 0); + jassert (newNumChannels >= 0); jassert (newNumSamples >= 0); if (newNumSamples != size || newNumChannels != numChannels) { - const size_t channelListSize = sizeof (float*) * (size_t) (newNumChannels + 1); - const size_t newTotalBytes = ((size_t) newNumChannels * (size_t) newNumSamples * sizeof (float)) + channelListSize + 32; + const size_t allocatedSamplesPerChannel = ((size_t) newNumSamples + 3) & ~3u; + const size_t channelListSize = ((sizeof (float*) * (size_t) (newNumChannels + 1)) + 15) & ~15u; + const size_t newTotalBytes = ((size_t) newNumChannels * (size_t) allocatedSamplesPerChannel * sizeof (float)) + + channelListSize + 32; if (keepExistingContent) { - HeapBlock newData; - newData.allocate (newTotalBytes, clearExtraSpace); + HeapBlock newData; + newData.allocate (newTotalBytes, clearExtraSpace || isClear); - const size_t numBytesToCopy = sizeof (float) * (size_t) jmin (newNumSamples, size); + const size_t numSamplesToCopy = (size_t) jmin (newNumSamples, size); - float** const newChannels = reinterpret_cast (newData.getData()); - float* newChan = reinterpret_cast (newData + channelListSize); + float** const newChannels = reinterpret_cast (newData.getData()); + float* newChan = reinterpret_cast (newData + channelListSize); for (int j = 0; j < newNumChannels; ++j) { newChannels[j] = newChan; - newChan += newNumSamples; + newChan += allocatedSamplesPerChannel; } - const int numChansToCopy = jmin (numChannels, newNumChannels); - for (int i = 0; i < numChansToCopy; ++i) - memcpy (newChannels[i], channels[i], numBytesToCopy); + if (! isClear) + { + const int numChansToCopy = jmin (numChannels, newNumChannels); + for (int i = 0; i < numChansToCopy; ++i) + FloatVectorOperations::copy (newChannels[i], channels[i], (int) numSamplesToCopy); + } allocatedData.swapWith (newData); allocatedBytes = newTotalBytes; @@ -185,21 +222,21 @@ void AudioSampleBuffer::setSize (const int newNumChannels, { if (avoidReallocating && allocatedBytes >= newTotalBytes) { - if (clearExtraSpace) + if (clearExtraSpace || isClear) allocatedData.clear (newTotalBytes); } else { allocatedBytes = newTotalBytes; - allocatedData.allocate (newTotalBytes, clearExtraSpace); - channels = reinterpret_cast (allocatedData.getData()); + allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); + channels = reinterpret_cast (allocatedData.getData()); } - float* chan = reinterpret_cast (allocatedData + channelListSize); + float* chan = reinterpret_cast (allocatedData + channelListSize); for (int i = 0; i < newNumChannels; ++i) { channels[i] = chan; - chan += newNumSamples; + chan += allocatedSamplesPerChannel; } } @@ -211,8 +248,13 @@ void AudioSampleBuffer::setSize (const int newNumChannels, void AudioSampleBuffer::clear() noexcept { - for (int i = 0; i < numChannels; ++i) - zeromem (channels[i], sizeof (float) * (size_t) size); + if (! isClear) + { + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::clear (channels[i], size); + + isClear = true; + } } void AudioSampleBuffer::clear (const int startSample, @@ -220,8 +262,14 @@ void AudioSampleBuffer::clear (const int startSample, { jassert (startSample >= 0 && startSample + numSamples <= size); - for (int i = 0; i < numChannels; ++i) - zeromem (channels [i] + startSample, sizeof (float) * (size_t) numSamples); + if (! isClear) + { + if (startSample == 0 && numSamples == size) + isClear = true; + + for (int i = 0; i < numChannels; ++i) + FloatVectorOperations::clear (channels[i] + startSample, numSamples); + } } void AudioSampleBuffer::clear (const int channel, @@ -231,7 +279,31 @@ void AudioSampleBuffer::clear (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - zeromem (channels [channel] + startSample, sizeof (float) * (size_t) numSamples); + if (! isClear) + FloatVectorOperations::clear (channels [channel] + startSample, numSamples); +} + +float AudioSampleBuffer::getSample (int channel, int index) const noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (isPositiveAndBelow (index, size)); + return *(channels [channel] + index); +} + +void AudioSampleBuffer::setSample (int channel, int index, float newValue) noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (isPositiveAndBelow (index, size)); + *(channels [channel] + index) = newValue; + isClear = false; +} + +void AudioSampleBuffer::addSample (int channel, int index, float valueToAdd) noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (isPositiveAndBelow (index, size)); + *(channels [channel] + index) += valueToAdd; + isClear = false; } void AudioSampleBuffer::applyGain (const int channel, @@ -242,19 +314,14 @@ void AudioSampleBuffer::applyGain (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - if (gain != 1.0f) + if (gain != 1.0f && ! isClear) { - float* d = channels [channel] + startSample; + float* const d = channels [channel] + startSample; if (gain == 0.0f) - { - zeromem (d, sizeof (float) * (size_t) numSamples); - } + FloatVectorOperations::clear (d, numSamples); else - { - while (--numSamples >= 0) - *d++ *= gain; - } + FloatVectorOperations::multiply (d, gain, numSamples); } } @@ -264,34 +331,47 @@ void AudioSampleBuffer::applyGainRamp (const int channel, float startGain, float endGain) noexcept { - if (startGain == endGain) + if (! isClear) { - applyGain (channel, startSample, numSamples, startGain); - } - else - { - jassert (isPositiveAndBelow (channel, numChannels)); - jassert (startSample >= 0 && startSample + numSamples <= size); - - const float increment = (endGain - startGain) / numSamples; - float* d = channels [channel] + startSample; - - while (--numSamples >= 0) + if (startGain == endGain) { - *d++ *= startGain; - startGain += increment; + applyGain (channel, startSample, numSamples, startGain); + } + else + { + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (startSample >= 0 && startSample + numSamples <= size); + + const float increment = (endGain - startGain) / numSamples; + float* d = channels [channel] + startSample; + + while (--numSamples >= 0) + { + *d++ *= startGain; + startGain += increment; + } } } } -void AudioSampleBuffer::applyGain (const int startSample, - const int numSamples, - const float gain) noexcept +void AudioSampleBuffer::applyGain (int startSample, int numSamples, float gain) noexcept { for (int i = 0; i < numChannels; ++i) applyGain (i, startSample, numSamples, gain); } +void AudioSampleBuffer::applyGain (const float gain) noexcept +{ + applyGain (0, size, gain); +} + +void AudioSampleBuffer::applyGainRamp (int startSample, int numSamples, + float startGain, float endGain) noexcept +{ + for (int i = 0; i < numChannels; ++i) + applyGainRamp (i, startSample, numSamples, startGain, endGain); +} + void AudioSampleBuffer::addFrom (const int destChannel, const int destStartSample, const AudioSampleBuffer& source, @@ -306,20 +386,26 @@ void AudioSampleBuffer::addFrom (const int destChannel, jassert (isPositiveAndBelow (sourceChannel, source.numChannels)); jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); - if (gain != 0.0f && numSamples > 0) + if (gain != 0.0f && numSamples > 0 && ! source.isClear) { - float* d = channels [destChannel] + destStartSample; - const float* s = source.channels [sourceChannel] + sourceStartSample; + float* const d = channels [destChannel] + destStartSample; + const float* const s = source.channels [sourceChannel] + sourceStartSample; - if (gain != 1.0f) + if (isClear) { - while (--numSamples >= 0) - *d++ += gain * *s++; + isClear = false; + + if (gain != 1.0f) + FloatVectorOperations::copyWithMultiply (d, s, gain, numSamples); + else + FloatVectorOperations::copy (d, s, numSamples); } else { - while (--numSamples >= 0) - *d++ += *s++; + if (gain != 1.0f) + FloatVectorOperations::addWithMultiply (d, s, gain, numSamples); + else + FloatVectorOperations::add (d, s, numSamples); } } } @@ -336,17 +422,23 @@ void AudioSampleBuffer::addFrom (const int destChannel, if (gain != 0.0f && numSamples > 0) { - float* d = channels [destChannel] + destStartSample; + float* const d = channels [destChannel] + destStartSample; - if (gain != 1.0f) + if (isClear) { - while (--numSamples >= 0) - *d++ += gain * *source++; + isClear = false; + + if (gain != 1.0f) + FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); + else + FloatVectorOperations::copy (d, source, numSamples); } else { - while (--numSamples >= 0) - *d++ += *source++; + if (gain != 1.0f) + FloatVectorOperations::addWithMultiply (d, source, gain, numSamples); + else + FloatVectorOperations::add (d, source, numSamples); } } } @@ -364,16 +456,13 @@ void AudioSampleBuffer::addFromWithRamp (const int destChannel, if (startGain == endGain) { - addFrom (destChannel, - destStartSample, - source, - numSamples, - startGain); + addFrom (destChannel, destStartSample, source, numSamples, startGain); } else { if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f)) { + isClear = false; const float increment = (endGain - startGain) / numSamples; float* d = channels [destChannel] + destStartSample; @@ -401,9 +490,18 @@ void AudioSampleBuffer::copyFrom (const int destChannel, if (numSamples > 0) { - memcpy (channels [destChannel] + destStartSample, - source.channels [sourceChannel] + sourceStartSample, - sizeof (float) * (size_t) numSamples); + if (source.isClear) + { + if (! isClear) + FloatVectorOperations::clear (channels [destChannel] + destStartSample, numSamples); + } + else + { + isClear = false; + FloatVectorOperations::copy (channels [destChannel] + destStartSample, + source.channels [sourceChannel] + sourceStartSample, + numSamples); + } } } @@ -418,9 +516,8 @@ void AudioSampleBuffer::copyFrom (const int destChannel, if (numSamples > 0) { - memcpy (channels [destChannel] + destStartSample, - source, - sizeof (float) * (size_t) numSamples); + isClear = false; + FloatVectorOperations::copy (channels [destChannel] + destStartSample, source, numSamples); } } @@ -436,23 +533,25 @@ void AudioSampleBuffer::copyFrom (const int destChannel, if (numSamples > 0) { - float* d = channels [destChannel] + destStartSample; + float* const d = channels [destChannel] + destStartSample; if (gain != 1.0f) { if (gain == 0) { - zeromem (d, sizeof (float) * (size_t) numSamples); + if (! isClear) + FloatVectorOperations::clear (d, numSamples); } else { - while (--numSamples >= 0) - *d++ = gain * *source++; + isClear = false; + FloatVectorOperations::copyWithMultiply (d, source, gain, numSamples); } } else { - memcpy (d, source, sizeof (float) * (size_t) numSamples); + isClear = false; + FloatVectorOperations::copy (d, source, numSamples); } } } @@ -470,16 +569,13 @@ void AudioSampleBuffer::copyFromWithRamp (const int destChannel, if (startGain == endGain) { - copyFrom (destChannel, - destStartSample, - source, - numSamples, - startGain); + copyFrom (destChannel, destStartSample, source, numSamples, startGain); } else { if (numSamples > 0 && (startGain != 0.0f || endGain != 0.0f)) { + isClear = false; const float increment = (endGain - startGain) / numSamples; float* d = channels [destChannel] + destStartSample; @@ -492,16 +588,33 @@ void AudioSampleBuffer::copyFromWithRamp (const int destChannel, } } -void AudioSampleBuffer::findMinMax (const int channel, - const int startSample, - int numSamples, - float& minVal, - float& maxVal) const noexcept +void AudioSampleBuffer::reverse (int channel, int startSample, int numSamples) const noexcept { jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - findMinAndMax (channels [channel] + startSample, numSamples, minVal, maxVal); + if (! isClear) + std::reverse (channels[channel] + startSample, + channels[channel] + startSample + numSamples); +} + +void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept +{ + for (int i = 0; i < numChannels; ++i) + reverse (i, startSample, numSamples); +} + +Range AudioSampleBuffer::findMinMax (const int channel, + const int startSample, + int numSamples) const noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (startSample >= 0 && startSample + numSamples <= size); + + if (isClear) + return Range(); + + return FloatVectorOperations::findMinAndMax (channels [channel] + startSample, numSamples); } float AudioSampleBuffer::getMagnitude (const int channel, @@ -511,19 +624,21 @@ float AudioSampleBuffer::getMagnitude (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - float mn, mx; - findMinMax (channel, startSample, numSamples, mn, mx); + if (isClear) + return 0.0f; - return jmax (mn, -mn, mx, -mx); + const Range r (findMinMax (channel, startSample, numSamples)); + + return jmax (r.getStart(), -r.getStart(), r.getEnd(), -r.getEnd()); } -float AudioSampleBuffer::getMagnitude (const int startSample, - const int numSamples) const noexcept +float AudioSampleBuffer::getMagnitude (int startSample, int numSamples) const noexcept { float mag = 0.0f; - for (int i = 0; i < numChannels; ++i) - mag = jmax (mag, getMagnitude (i, startSample, numSamples)); + if (! isClear) + for (int i = 0; i < numChannels; ++i) + mag = jmax (mag, getMagnitude (i, startSample, numSamples)); return mag; } @@ -535,7 +650,7 @@ float AudioSampleBuffer::getRMSLevel (const int channel, jassert (isPositiveAndBelow (channel, numChannels)); jassert (startSample >= 0 && startSample + numSamples <= size); - if (numSamples <= 0 || channel < 0 || channel >= numChannels) + if (numSamples <= 0 || channel < 0 || channel >= numChannels || isClear) return 0.0f; const float* const data = channels [channel] + startSample; diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index 485df1a..8b9a72c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ -#define __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ +#ifndef JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED +#define JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED //============================================================================== @@ -35,6 +34,10 @@ class JUCE_API AudioSampleBuffer { public: + //============================================================================== + /** Creates an empty buffer with 0 channels and 0 length. */ + AudioSampleBuffer() noexcept; + //============================================================================== /** Creates a buffer with a specified number of channels and samples. @@ -63,7 +66,7 @@ public: @param numSamples the number of samples to use - this must correspond to the size of the arrays passed in */ - AudioSampleBuffer (float** dataToReferTo, + AudioSampleBuffer (float* const* dataToReferTo, int numChannels, int numSamples) noexcept; @@ -83,7 +86,7 @@ public: @param numSamples the number of samples to use - this must correspond to the size of the arrays passed in */ - AudioSampleBuffer (float** dataToReferTo, + AudioSampleBuffer (float* const* dataToReferTo, int numChannels, int startSample, int numSamples) noexcept; @@ -94,55 +97,81 @@ public: using an external data buffer, in which case boths buffers will just point to the same shared block of data. */ - AudioSampleBuffer (const AudioSampleBuffer& other) noexcept; + AudioSampleBuffer (const AudioSampleBuffer&) noexcept; /** Copies another buffer onto this one. - This buffer's size will be changed to that of the other buffer. */ - AudioSampleBuffer& operator= (const AudioSampleBuffer& other) noexcept; + AudioSampleBuffer& operator= (const AudioSampleBuffer&) noexcept; /** Destructor. - This will free any memory allocated by the buffer. */ - virtual ~AudioSampleBuffer() noexcept; + ~AudioSampleBuffer() noexcept; //============================================================================== /** Returns the number of channels of audio data that this buffer contains. - @see getSampleData */ int getNumChannels() const noexcept { return numChannels; } /** Returns the number of samples allocated in each of the buffer's channels. - @see getSampleData */ int getNumSamples() const noexcept { return size; } - /** Returns a pointer one of the buffer's channels. - + /** Returns a pointer to an array of read-only samples in 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! + If you need to write to the data, do NOT call this method and const_cast the + result! Instead, you must call getWritePointer so that the buffer knows you're + planning on modifying the data. */ - float* getSampleData (const int channelNumber) const noexcept + const float* getReadPointer (int channelNumber) const noexcept { jassert (isPositiveAndBelow (channelNumber, 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! + /** Returns a pointer to an array of read-only samples in one of the buffer's channels. + For speed, this doesn't check whether the channel number or index are out of range, + so be careful when using it! + If you need to write to the data, do NOT call this method and const_cast the + result! Instead, you must call getWritePointer so that the buffer knows you're + planning on modifying the data. */ - float* getSampleData (const int channelNumber, - const int sampleOffset) const noexcept + const float* getReadPointer (int channelNumber, int sampleIndex) const noexcept { jassert (isPositiveAndBelow (channelNumber, numChannels)); - jassert (isPositiveAndBelow (sampleOffset, size)); - return channels [channelNumber] + sampleOffset; + jassert (isPositiveAndBelow (sampleIndex, size)); + return channels [channelNumber] + sampleIndex; + } + + /** Returns a writeable pointer to 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! + Note that if you're not planning on writing to the data, you should always + use getReadPointer instead. + */ + float* getWritePointer (int channelNumber) noexcept + { + jassert (isPositiveAndBelow (channelNumber, numChannels)); + isClear = false; + return channels [channelNumber]; + } + + /** Returns a writeable pointer to one of the buffer's channels. + For speed, this doesn't check whether the channel number or index are out of range, + so be careful when using it! + Note that if you're not planning on writing to the data, you should + use getReadPointer instead. + */ + float* getWritePointer (int channelNumber, int sampleIndex) noexcept + { + jassert (isPositiveAndBelow (channelNumber, numChannels)); + jassert (isPositiveAndBelow (sampleIndex, size)); + isClear = false; + return channels [channelNumber] + sampleIndex; } /** Returns an array of pointers to the channels in the buffer. @@ -150,7 +179,14 @@ public: 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 noexcept { return channels; } + const float** getArrayOfReadPointers() const noexcept { return const_cast (channels); } + + /** 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** getArrayOfWritePointers() noexcept { isClear = false; return channels; } //============================================================================== /** Changes the buffer's size or number of channels. @@ -222,6 +258,36 @@ public: int startSample, int numSamples) noexcept; + /** Returns true if the buffer has been entirely cleared. + Note that this does not actually measure the contents of the buffer - it simply + returns a flag that is set when the buffer is cleared, and which is reset whenever + functions like getWritePointer() are invoked. That means the method does not take + any time, but it may return false negatives when in fact the buffer is still empty. + */ + bool hasBeenCleared() const noexcept { return isClear; } + + //============================================================================== + /** Returns a sample from the buffer. + The channel and index are not checked - they are expected to be in-range. If not, + an assertion will be thrown, but in a release build, you're into 'undefined behaviour' + territory. + */ + float getSample (int channel, int sampleIndex) const noexcept; + + /** Sets a sample in the buffer. + The channel and index are not checked - they are expected to be in-range. If not, + an assertion will be thrown, but in a release build, you're into 'undefined behaviour' + territory. + */ + void setSample (int destChannel, int destSample, float newValue) noexcept; + + /** Adds a value to a sample in the buffer. + The channel and index are not checked - they are expected to be in-range. If not, + an assertion will be thrown, but in a release build, you're into 'undefined behaviour' + territory. + */ + void addSample (int destChannel, int destSample, float valueToAdd) noexcept; + /** Applies a gain multiple to a region of one channel. For speed, this doesn't check whether the channel and sample number @@ -241,6 +307,9 @@ public: int numSamples, float gain) noexcept; + /** Applies a gain multiple to all the audio data. */ + void applyGain (float gain) noexcept; + /** Applies a range of gains to a region of a channel. The gain that is applied to each sample will vary from @@ -256,6 +325,20 @@ public: float startGain, float endGain) noexcept; + /** Applies a range of gains to a region of all channels. + + 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 startSample, + int numSamples, + float startGain, + float endGain) noexcept; + /** Adds samples from another buffer to this one. @param destChannel the channel within this buffer to add the samples to @@ -381,50 +464,63 @@ public: float endGain) noexcept; - /** Finds the highest and lowest sample values in a given range. + /** Returns a Range indicating the lowest and highest sample values in a given section. @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 noexcept; + Range findMinMax (int channel, + int startSample, + int numSamples) const noexcept; - /** Finds the highest absolute sample value within a region of a channel. - */ + /** Finds the highest absolute sample value within a region of a channel. */ float getMagnitude (int channel, int startSample, int numSamples) const noexcept; - /** Finds the highest absolute sample value within a region on all channels. - */ + /** Finds the highest absolute sample value within a region on all channels. */ float getMagnitude (int startSample, int numSamples) const noexcept; - /** Returns the root mean squared level for a region of a channel. - */ + /** Returns the root mean squared level for a region of a channel. */ float getRMSLevel (int channel, int startSample, int numSamples) const noexcept; + /** Reverses a part of a channel. */ + void reverse (int channel, int startSample, int numSamples) const noexcept; + + /** Reverses a part of the buffer. */ + void reverse (int startSample, int numSamples) const noexcept; + + //============================================================================== + #ifndef DOXYGEN + // Note that these methods have now been replaced by getReadPointer() and getWritePointer() + JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel) const, { return getReadPointer (channel); }) + JUCE_DEPRECATED_WITH_BODY (const float* getSampleData (int channel, int index) const, { return getReadPointer (channel, index); }) + JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel), { return getWritePointer (channel); }) + JUCE_DEPRECATED_WITH_BODY (float* getSampleData (int channel, int index), { return getWritePointer (channel, index); }) + + // These have been replaced by getArrayOfReadPointers() and getArrayOfWritePointers() + JUCE_DEPRECATED_WITH_BODY (const float** getArrayOfChannels() const, { return getArrayOfReadPointers(); }) + JUCE_DEPRECATED_WITH_BODY (float** getArrayOfChannels(), { return getArrayOfWritePointers(); }) + #endif + private: //============================================================================== int numChannels, size; size_t allocatedBytes; float** channels; - HeapBlock allocatedData; + HeapBlock allocatedData; float* preallocatedChannelSpace [32]; + bool isClear; void allocateData(); - void allocateChannels (float** dataToReferTo, int offset); + void allocateChannels (float* const*, int offset); - JUCE_LEAK_DETECTOR (AudioSampleBuffer); + JUCE_LEAK_DETECTOR (AudioSampleBuffer) }; -#endif // __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ +#endif // JUCE_AUDIOSAMPLEBUFFER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp new file mode 100644 index 0000000..a232ec9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -0,0 +1,774 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +namespace FloatVectorHelpers +{ + #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); + #define JUCE_INCREMENT_DEST dest += (16 / sizeof (*dest)); + + #if JUCE_USE_SSE_INTRINSICS + static bool sse2Present = false; + + static bool isSSE2Available() noexcept + { + if (sse2Present) + return true; + + sse2Present = SystemStats::hasSSE2(); + return sse2Present; + } + + inline static bool isAligned (const void* p) noexcept + { + return (((pointer_sized_int) p) & 15) == 0; + } + + struct BasicOps32 + { + typedef float Type; + typedef __m128 ParallelType; + enum { numParallel = 4 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_ps (&v); } + static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_ps (v); } + static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_ps (v); } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_ps (dest, a); } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_ps (dest, a); } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_ps (a, b); } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_ps (a, b); } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_ps (a, b); } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_ps (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_ps (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } + static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } + }; + + struct BasicOps64 + { + typedef double Type; + typedef __m128d ParallelType; + enum { numParallel = 2 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return _mm_load1_pd (&v); } + static forcedinline ParallelType loadA (const Type* v) noexcept { return _mm_load_pd (v); } + static forcedinline ParallelType loadU (const Type* v) noexcept { return _mm_loadu_pd (v); } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { _mm_store_pd (dest, a); } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { _mm_storeu_pd (dest, a); } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return _mm_add_pd (a, b); } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return _mm_sub_pd (a, b); } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return _mm_mul_pd (a, b); } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return _mm_max_pd (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return _mm_min_pd (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1]); } + static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1]); } + }; + + #define JUCE_BEGIN_VEC_OP \ + typedef FloatVectorHelpers::ModeType::Mode Mode; \ + if (FloatVectorHelpers::isSSE2Available()) \ + { \ + const int numLongOps = num / Mode::numParallel; + + #define JUCE_FINISH_VEC_OP(normalOp) \ + num &= (Mode::numParallel - 1); \ + if (num == 0) return; \ + } \ + for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + if (FloatVectorHelpers::isAligned (dest)) JUCE_VEC_LOOP (vecOp, dummy, Mode::loadA, Mode::storeA, locals, JUCE_INCREMENT_DEST) \ + else JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_VEC_OP (normalOp) + + #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + if (FloatVectorHelpers::isAligned (dest)) \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadA, Mode::storeA, locals, increment) \ + else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadA, Mode::storeA, locals, increment) \ + }\ + else \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_VEC_LOOP (vecOp, Mode::loadA, Mode::loadU, Mode::storeU, locals, increment) \ + else JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + } \ + JUCE_FINISH_VEC_OP (normalOp) + + //============================================================================== + #elif JUCE_USE_ARM_NEON + + struct BasicOps32 + { + typedef float Type; + typedef float32x4_t ParallelType; + enum { numParallel = 4 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return vld1q_dup_f32 (&v); } + static forcedinline ParallelType loadA (const Type* v) noexcept { return vld1q_f32 (v); } + static forcedinline ParallelType loadU (const Type* v) noexcept { return vld1q_f32 (v); } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { vst1q_f32 (dest, a); } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return vaddq_f32 (a, b); } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return vsubq_f32 (a, b); } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return vmulq_f32 (a, b); } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return vmaxq_f32 (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return vminq_f32 (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmax (v[0], v[1], v[2], v[3]); } + static forcedinline Type min (ParallelType a) noexcept { Type v[numParallel]; storeU (v, a); return jmin (v[0], v[1], v[2], v[3]); } + }; + + struct BasicOps64 + { + typedef double Type; + typedef double ParallelType; + enum { numParallel = 1 }; + + static forcedinline ParallelType load1 (Type v) noexcept { return v; } + static forcedinline ParallelType loadA (const Type* v) noexcept { return *v; } + static forcedinline ParallelType loadU (const Type* v) noexcept { return *v; } + static forcedinline void storeA (Type* dest, ParallelType a) noexcept { *dest = a; } + static forcedinline void storeU (Type* dest, ParallelType a) noexcept { *dest = a; } + + static forcedinline ParallelType add (ParallelType a, ParallelType b) noexcept { return a + b; } + static forcedinline ParallelType sub (ParallelType a, ParallelType b) noexcept { return a - b; } + static forcedinline ParallelType mul (ParallelType a, ParallelType b) noexcept { return a * b; } + static forcedinline ParallelType max (ParallelType a, ParallelType b) noexcept { return jmax (a, b); } + static forcedinline ParallelType min (ParallelType a, ParallelType b) noexcept { return jmin (a, b); } + + static forcedinline Type max (ParallelType a) noexcept { return a; } + static forcedinline Type min (ParallelType a) noexcept { return a; } + }; + + #define JUCE_BEGIN_VEC_OP \ + typedef FloatVectorHelpers::ModeType::Mode Mode; \ + if (Mode::numParallel > 1) \ + { \ + const int numLongOps = num / Mode::numParallel; + + #define JUCE_FINISH_VEC_OP(normalOp) \ + num &= (Mode::numParallel - 1); \ + if (num == 0) return; \ + } \ + for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + JUCE_VEC_LOOP (vecOp, dummy, Mode::loadU, Mode::storeU, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_VEC_OP (normalOp) + + #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ + JUCE_BEGIN_VEC_OP \ + setupOp \ + JUCE_VEC_LOOP (vecOp, Mode::loadU, Mode::loadU, Mode::storeU, locals, increment) \ + JUCE_FINISH_VEC_OP (normalOp) + + //============================================================================== + #else + #define JUCE_PERFORM_VEC_OP_DEST(normalOp, vecOp, locals, setupOp) \ + for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_PERFORM_VEC_OP_SRC_DEST(normalOp, vecOp, locals, increment, setupOp) \ + for (int i = 0; i < num; ++i) normalOp; + + #endif + + //============================================================================== + #define JUCE_VEC_LOOP(vecOp, srcLoad, dstLoad, dstStore, locals, increment) \ + for (int i = 0; i < numLongOps; ++i) \ + { \ + locals (srcLoad, dstLoad); \ + dstStore (dest, vecOp); \ + increment; \ + } + + #define JUCE_LOAD_NONE(srcLoad, dstLoad) + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const Mode::ParallelType s = srcLoad (src); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const Mode::ParallelType d = dstLoad (dest), s = srcLoad (src); + + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + template struct ModeType { typedef BasicOps32 Mode; }; + template<> struct ModeType<8> { typedef BasicOps64 Mode; }; + + template + struct MinMax + { + typedef typename Mode::Type Type; + typedef typename Mode::ParallelType ParallelType; + + static Type findMinOrMax (const Type* src, int num, const bool isMinimum) noexcept + { + int numLongOps = num / Mode::numParallel; + + #if JUCE_USE_SSE_INTRINSICS + if (numLongOps > 1 && isSSE2Available()) + #else + if (numLongOps > 1) + #endif + { + ParallelType val; + + #if ! JUCE_USE_ARM_NEON + if (isAligned (src)) + { + val = Mode::loadA (src); + + if (isMinimum) + { + while (--numLongOps > 0) + { + src += Mode::numParallel; + val = Mode::min (val, Mode::loadA (src)); + } + } + else + { + while (--numLongOps > 0) + { + src += Mode::numParallel; + val = Mode::max (val, Mode::loadA (src)); + } + } + } + else + #endif + { + val = Mode::loadU (src); + + if (isMinimum) + { + while (--numLongOps > 0) + { + src += Mode::numParallel; + val = Mode::min (val, Mode::loadU (src)); + } + } + else + { + while (--numLongOps > 0) + { + src += Mode::numParallel; + val = Mode::max (val, Mode::loadU (src)); + } + } + } + + Type result = isMinimum ? Mode::min (val) + : Mode::max (val); + + num &= (Mode::numParallel - 1); + src += Mode::numParallel; + + for (int i = 0; i < num; ++i) + result = isMinimum ? jmin (result, src[i]) + : jmax (result, src[i]); + + return result; + } + + return isMinimum ? juce::findMinimum (src, num) + : juce::findMaximum (src, num); + } + + static Range findMinAndMax (const Type* src, int num) noexcept + { + int numLongOps = num / Mode::numParallel; + + #if JUCE_USE_SSE_INTRINSICS + if (numLongOps > 1 && isSSE2Available()) + #else + if (numLongOps > 1) + #endif + { + ParallelType mn, mx; + + #if ! JUCE_USE_ARM_NEON + if (isAligned (src)) + { + mn = Mode::loadA (src); + mx = mn; + + while (--numLongOps > 0) + { + src += Mode::numParallel; + const ParallelType v = Mode::loadA (src); + mn = Mode::min (mn, v); + mx = Mode::max (mx, v); + } + } + else + #endif + { + mn = Mode::loadU (src); + mx = mn; + + while (--numLongOps > 0) + { + src += Mode::numParallel; + const ParallelType v = Mode::loadU (src); + mn = Mode::min (mn, v); + mx = Mode::max (mx, v); + } + } + + Range result (Mode::min (mn), + Mode::max (mx)); + + num &= (Mode::numParallel - 1); + src += Mode::numParallel; + + for (int i = 0; i < num; ++i) + result = result.getUnionWith (src[i]); + + return result; + } + + return Range::findMinAndMax (src, num); + } + }; + #endif +} + +//============================================================================== +void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclr (dest, 1, (size_t) num); + #else + zeromem (dest, num * sizeof (float)); + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::clear (double* dest, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vclrD (dest, 1, (size_t) num); + #else + zeromem (dest, num * sizeof (double)); + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vfill (&valueToFill, dest, 1, (size_t) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::fill (double* dest, double valueToFill, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vfillD (&valueToFill, dest, 1, (size_t) num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE, + const Mode::ParallelType val = Mode::load1 (valueToFill);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::copy (float* dest, const float* src, int num) noexcept +{ + memcpy (dest, src, (size_t) num * sizeof (float)); +} + +void JUCE_CALLTYPE FloatVectorOperations::copy (double* dest, const double* src, int num) noexcept +{ + memcpy (dest, src, (size_t) num * sizeof (double)); +} + +void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmul (src, 1, &multiplier, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (src, 1, &multiplier, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, Mode::mul (mult, s), + JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) +} + +void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, double amount, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_DEST (dest[i] += amount, Mode::add (d, amountToAdd), JUCE_LOAD_DEST, + const Mode::ParallelType amountToAdd = Mode::load1 (amount);) +} + +void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vadd (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::add (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vaddD (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i], Mode::add (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsub (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::subtract (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsubD (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] -= src[i], Mode::sub (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) +} + +void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (double* dest, const double* src, double multiplier, int num) noexcept +{ + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] += src[i] * multiplier, Mode::add (d, Mode::mul (mult, s)), + JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmul (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vmulD (src, 1, dest, 1, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] *= src[i], Mode::mul (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST, ) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplier, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmul (dest, 1, &multiplier, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::multiply (double* dest, double multiplier, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsmulD (dest, 1, &multiplier, dest, 1, num); + #else + JUCE_PERFORM_VEC_OP_DEST (dest[i] *= multiplier, Mode::mul (d, mult), JUCE_LOAD_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif +} + +void FloatVectorOperations::negate (float* dest, const float* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vneg ((float*) src, 1, dest, 1, (vDSP_Length) num); + #else + copyWithMultiply (dest, src, -1.0f, num); + #endif +} + +void FloatVectorOperations::negate (double* dest, const double* src, int num) noexcept +{ + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vnegD ((double*) src, 1, dest, 1, (vDSP_Length) num); + #else + copyWithMultiply (dest, src, -1.0f, num); + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept +{ + #if JUCE_USE_ARM_NEON + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), + JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, ) + #else + JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, + Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), + JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, + const Mode::ParallelType mult = Mode::load1 (multiplier);) + #endif +} + +Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); + #endif +} + +Range JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const double* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinAndMax (src, num); + #else + return Range::findMinAndMax (src, num); + #endif +} + +float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); + #else + return juce::findMinimum (src, num); + #endif +} + +double JUCE_CALLTYPE FloatVectorOperations::findMinimum (const double* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, true); + #else + return juce::findMinimum (src, num); + #endif +} + +float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); + #else + return juce::findMaximum (src, num); + #endif +} + +double JUCE_CALLTYPE FloatVectorOperations::findMaximum (const double* src, int num) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON + return FloatVectorHelpers::MinMax::findMinOrMax (src, num, false); + #else + return juce::findMaximum (src, num); + #endif +} + +void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnable) noexcept +{ + #if JUCE_USE_SSE_INTRINSICS + if (FloatVectorHelpers::isSSE2Available()) + _MM_SET_FLUSH_ZERO_MODE (shouldEnable ? _MM_FLUSH_ZERO_ON : _MM_FLUSH_ZERO_OFF); + #endif + (void) shouldEnable; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class FloatVectorOperationsTests : public UnitTest +{ +public: + FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations") {} + + template + struct TestRunner + { + static void runTest (UnitTest& u, Random random) + { + const int range = random.nextBool() ? 500 : 10; + const int num = random.nextInt (range) + 1; + + HeapBlock buffer1 (num + 16), buffer2 (num + 16); + HeapBlock buffer3 (num + 16); + + #if JUCE_ARM + ValueType* const data1 = buffer1; + ValueType* const data2 = buffer2; + int* const int1 = buffer3; + #else + ValueType* const data1 = addBytesToPointer (buffer1.getData(), random.nextInt (16)); + ValueType* const data2 = addBytesToPointer (buffer2.getData(), random.nextInt (16)); + int* const int1 = addBytesToPointer (buffer3.getData(), random.nextInt (16)); + #endif + + fillRandomly (random, data1, num); + fillRandomly (random, data2, num); + + Range minMax1 (FloatVectorOperations::findMinAndMax (data1, num)); + Range minMax2 (Range::findMinAndMax (data1, num)); + u.expect (minMax1 == minMax2); + + u.expect (valuesMatch (FloatVectorOperations::findMinimum (data1, num), juce::findMinimum (data1, num))); + u.expect (valuesMatch (FloatVectorOperations::findMaximum (data1, num), juce::findMaximum (data1, num))); + + u.expect (valuesMatch (FloatVectorOperations::findMinimum (data2, num), juce::findMinimum (data2, num))); + u.expect (valuesMatch (FloatVectorOperations::findMaximum (data2, num), juce::findMaximum (data2, num))); + + FloatVectorOperations::clear (data1, num); + u.expect (areAllValuesEqual (data1, num, 0)); + + FloatVectorOperations::fill (data1, (ValueType) 2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 2)); + + FloatVectorOperations::add (data1, (ValueType) 2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 4)); + + FloatVectorOperations::copy (data2, data1, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) 4)); + + FloatVectorOperations::add (data2, data1, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) 8)); + + FloatVectorOperations::copyWithMultiply (data2, data1, (ValueType) 4, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) 16)); + + FloatVectorOperations::addWithMultiply (data2, data1, (ValueType) 4, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) 32)); + + FloatVectorOperations::multiply (data1, (ValueType) 2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 8)); + + FloatVectorOperations::multiply (data1, data2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 256)); + + FloatVectorOperations::negate (data2, data1, num); + u.expect (areAllValuesEqual (data2, num, (ValueType) -256)); + + FloatVectorOperations::subtract (data1, data2, num); + u.expect (areAllValuesEqual (data1, num, (ValueType) 512)); + + fillRandomly (random, int1, num); + doConversionTest (u, data1, data2, int1, num); + } + + static void doConversionTest (UnitTest& u, float* data1, float* data2, int* const int1, int num) + { + FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num); + convertFixed (data2, int1, 2.0f, num); + u.expect (buffersMatch (data1, data2, num)); + } + + static void doConversionTest (UnitTest&, double*, double*, int*, int) {} + + static void fillRandomly (Random& random, ValueType* d, int num) + { + while (--num >= 0) + *d++ = (ValueType) (random.nextDouble() * 1000.0); + } + + static void fillRandomly (Random& random, int* d, int num) + { + while (--num >= 0) + *d++ = random.nextInt(); + } + + static void convertFixed (float* d, const int* s, ValueType multiplier, int num) + { + while (--num >= 0) + *d++ = *s++ * multiplier; + } + + static bool areAllValuesEqual (const ValueType* d, int num, ValueType target) + { + while (--num >= 0) + if (*d++ != target) + return false; + + return true; + } + + static bool buffersMatch (const ValueType* d1, const ValueType* d2, int num) + { + while (--num >= 0) + if (! valuesMatch (*d1++, *d2++)) + return false; + + return true; + } + + static bool valuesMatch (ValueType v1, ValueType v2) + { + return std::abs (v1 - v2) < std::numeric_limits::epsilon(); + } + }; + + void runTest() + { + beginTest ("FloatVectorOperations"); + + for (int i = 1000; --i >= 0;) + { + TestRunner::runTest (*this, getRandom()); + TestRunner::runTest (*this, getRandom()); + } + } +}; + +static FloatVectorOperationsTests vectorOpTests; + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h new file mode 100644 index 0000000..54821d2 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -0,0 +1,132 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_FLOATVECTOROPERATIONS_H_INCLUDED +#define JUCE_FLOATVECTOROPERATIONS_H_INCLUDED + + +//============================================================================== +/** + A collection of simple vector operations on arrays of floats, accelerated with + SIMD instructions where possible. +*/ +class JUCE_API FloatVectorOperations +{ +public: + //============================================================================== + /** Clears a vector of floats. */ + static void JUCE_CALLTYPE clear (float* dest, int numValues) noexcept; + + /** Clears a vector of doubles. */ + static void JUCE_CALLTYPE clear (double* dest, int numValues) noexcept; + + /** Copies a repeated value into a vector of floats. */ + static void JUCE_CALLTYPE fill (float* dest, float valueToFill, int numValues) noexcept; + + /** Copies a repeated value into a vector of doubles. */ + static void JUCE_CALLTYPE fill (double* dest, double valueToFill, int numValues) noexcept; + + /** Copies a vector of floats. */ + static void JUCE_CALLTYPE copy (float* dest, const float* src, int numValues) noexcept; + + /** Copies a vector of doubles. */ + static void JUCE_CALLTYPE copy (double* dest, const double* src, int numValues) noexcept; + + /** Copies a vector of floats, multiplying each value by a given multiplier */ + static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + + /** Copies a vector of doubles, multiplying each value by a given multiplier */ + static void JUCE_CALLTYPE copyWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; + + /** Adds a fixed value to the destination values. */ + static void JUCE_CALLTYPE add (float* dest, float amountToAdd, int numValues) noexcept; + + /** Adds a fixed value to the destination values. */ + static void JUCE_CALLTYPE add (double* dest, double amountToAdd, int numValues) noexcept; + + /** Adds the source values to the destination values. */ + static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; + + /** Adds the source values to the destination values. */ + static void JUCE_CALLTYPE add (double* dest, const double* src, int numValues) noexcept; + + /** Subtracts the source values from the destination values. */ + static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; + + /** Subtracts the source values from the destination values. */ + static void JUCE_CALLTYPE subtract (double* dest, const double* src, int numValues) noexcept; + + /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ + static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + + /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ + static void JUCE_CALLTYPE addWithMultiply (double* dest, const double* src, double multiplier, int numValues) noexcept; + + /** Multiplies the destination values by the source values. */ + static void JUCE_CALLTYPE multiply (float* dest, const float* src, int numValues) noexcept; + + /** Multiplies the destination values by the source values. */ + static void JUCE_CALLTYPE multiply (double* dest, const double* src, int numValues) noexcept; + + /** Multiplies each of the destination values by a fixed multiplier. */ + static void JUCE_CALLTYPE multiply (float* dest, float multiplier, int numValues) noexcept; + + /** Multiplies each of the destination values by a fixed multiplier. */ + static void JUCE_CALLTYPE multiply (double* dest, double multiplier, int numValues) noexcept; + + /** Copies a source vector to a destination, negating each value. */ + static void JUCE_CALLTYPE negate (float* dest, const float* src, int numValues) noexcept; + + /** Copies a source vector to a destination, negating each value. */ + static void JUCE_CALLTYPE negate (double* dest, const double* src, int numValues) noexcept; + + /** Converts a stream of integers to floats, multiplying each one by the given multiplier. */ + static void JUCE_CALLTYPE convertFixedToFloat (float* dest, const int* src, float multiplier, int numValues) noexcept; + + /** Finds the miniumum and maximum values in the given array. */ + static Range JUCE_CALLTYPE findMinAndMax (const float* src, int numValues) noexcept; + + /** Finds the miniumum and maximum values in the given array. */ + static Range JUCE_CALLTYPE findMinAndMax (const double* src, int numValues) noexcept; + + /** Finds the miniumum value in the given array. */ + static float JUCE_CALLTYPE findMinimum (const float* src, int numValues) noexcept; + + /** Finds the miniumum value in the given array. */ + static double JUCE_CALLTYPE findMinimum (const double* src, int numValues) noexcept; + + /** Finds the maximum value in the given array. */ + static float JUCE_CALLTYPE findMaximum (const float* src, int numValues) noexcept; + + /** Finds the maximum value in the given array. */ + static double JUCE_CALLTYPE findMaximum (const double* src, int numValues) noexcept; + + /** On Intel CPUs, this method enables or disables the SSE flush-to-zero mode. + Effectively, this is a wrapper around a call to _MM_SET_FLUSH_ZERO_MODE + */ + static void JUCE_CALLTYPE enableFlushToZeroMode (bool shouldEnable) noexcept; +}; + + +#endif // JUCE_FLOATVECTOROPERATIONS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h index f139be0..c63746c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h +++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Decibels.h @@ -1,30 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DECIBELS_JUCEHEADER__ -#define __JUCE_DECIBELS_JUCEHEADER__ +#ifndef JUCE_DECIBELS_H_INCLUDED +#define JUCE_DECIBELS_H_INCLUDED + //============================================================================== /** @@ -43,7 +43,7 @@ public: static Type decibelsToGain (const Type decibels, const Type minusInfinityDb = (Type) defaultMinusInfinitydB) { - return decibels > minusInfinityDb ? powf ((Type) 10.0, decibels * (Type) 0.05) + return decibels > minusInfinityDb ? std::pow ((Type) 10.0, decibels * (Type) 0.05) : Type(); } @@ -97,8 +97,8 @@ private: }; Decibels(); // This class can't be instantiated, it's just a holder for static methods.. - JUCE_DECLARE_NON_COPYABLE (Decibels); + JUCE_DECLARE_NON_COPYABLE (Decibels) }; -#endif // __JUCE_DECIBELS_JUCEHEADER__ +#endif // JUCE_DECIBELS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp index c09b41a..908a69e 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -30,85 +29,38 @@ #endif //============================================================================== -IIRFilter::IIRFilter() - : active (false) +IIRCoefficients::IIRCoefficients() noexcept { - reset(); + zeromem (coefficients, sizeof (coefficients)); } -IIRFilter::IIRFilter (const IIRFilter& other) - : active (other.active) +IIRCoefficients::~IIRCoefficients() noexcept {} + +IIRCoefficients::IIRCoefficients (const IIRCoefficients& other) noexcept { - const ScopedLock sl (other.processLock); memcpy (coefficients, other.coefficients, sizeof (coefficients)); - reset(); } -IIRFilter::~IIRFilter() +IIRCoefficients& IIRCoefficients::operator= (const IIRCoefficients& other) noexcept { + memcpy (coefficients, other.coefficients, sizeof (coefficients)); + return *this; } -//============================================================================== -void IIRFilter::reset() noexcept +IIRCoefficients::IIRCoefficients (double c1, double c2, double c3, + double c4, double c5, double c6) noexcept { - const ScopedLock sl (processLock); + const double a = 1.0 / c4; - x1 = 0; - x2 = 0; - y1 = 0; - y2 = 0; + coefficients[0] = (float) (c1 * a); + coefficients[1] = (float) (c2 * a); + coefficients[2] = (float) (c3 * a); + coefficients[3] = (float) (c5 * a); + coefficients[4] = (float) (c6 * a); } -float IIRFilter::processSingleSampleRaw (const float in) noexcept -{ - float out = coefficients[0] * in - + coefficients[1] * x1 - + coefficients[2] * x2 - - coefficients[4] * y1 - - coefficients[5] * y2; - - JUCE_SNAP_TO_ZERO (out); - - x2 = x1; - x1 = in; - y2 = y1; - y1 = out; - - return out; -} - -void IIRFilter::processSamples (float* const samples, - const int numSamples) noexcept -{ - const ScopedLock sl (processLock); - - if (active) - { - for (int i = 0; i < numSamples; ++i) - { - const float in = samples[i]; - - float out = coefficients[0] * in - + coefficients[1] * x1 - + coefficients[2] * x2 - - coefficients[4] * y1 - - coefficients[5] * y2; - - JUCE_SNAP_TO_ZERO (out); - - x2 = x1; - x1 = in; - y2 = y1; - y1 = out; - - samples[i] = out; - } - } -} - -//============================================================================== -void IIRFilter::makeLowPass (const double sampleRate, - const double frequency) noexcept +IIRCoefficients IIRCoefficients::makeLowPass (const double sampleRate, + const double frequency) noexcept { jassert (sampleRate > 0); @@ -116,38 +68,38 @@ void IIRFilter::makeLowPass (const double sampleRate, const double nSquared = n * n; const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); - setCoefficients (c1, - c1 * 2.0f, - c1, - 1.0, - c1 * 2.0 * (1.0 - nSquared), - c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); + return IIRCoefficients (c1, + c1 * 2.0, + c1, + 1.0, + c1 * 2.0 * (1.0 - nSquared), + c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } -void IIRFilter::makeHighPass (const double sampleRate, - const double frequency) noexcept +IIRCoefficients IIRCoefficients::makeHighPass (const double sampleRate, + const double frequency) noexcept { const double n = tan (double_Pi * frequency / sampleRate); const double nSquared = n * n; const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); - setCoefficients (c1, - c1 * -2.0f, - c1, - 1.0, - c1 * 2.0 * (nSquared - 1.0), - c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); + return IIRCoefficients (c1, + c1 * -2.0, + c1, + 1.0, + c1 * 2.0 * (nSquared - 1.0), + c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); } -void IIRFilter::makeLowShelf (const double sampleRate, - const double cutOffFrequency, - const double Q, - const float gainFactor) noexcept +IIRCoefficients IIRCoefficients::makeLowShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) noexcept { jassert (sampleRate > 0); jassert (Q > 0); - const double A = jmax (0.0f, gainFactor); + const double A = jmax (0.0f, std::sqrt (gainFactor)); const double aminus1 = A - 1.0; const double aplus1 = A + 1.0; const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; @@ -155,23 +107,23 @@ void IIRFilter::makeLowShelf (const double sampleRate, const double beta = std::sin (omega) * std::sqrt (A) / Q; const double aminus1TimesCoso = aminus1 * coso; - setCoefficients (A * (aplus1 - aminus1TimesCoso + beta), - A * 2.0 * (aminus1 - aplus1 * coso), - A * (aplus1 - aminus1TimesCoso - beta), - aplus1 + aminus1TimesCoso + beta, - -2.0 * (aminus1 + aplus1 * coso), - aplus1 + aminus1TimesCoso - beta); + return IIRCoefficients (A * (aplus1 - aminus1TimesCoso + beta), + A * 2.0 * (aminus1 - aplus1 * coso), + A * (aplus1 - aminus1TimesCoso - beta), + aplus1 + aminus1TimesCoso + beta, + -2.0 * (aminus1 + aplus1 * coso), + aplus1 + aminus1TimesCoso - beta); } -void IIRFilter::makeHighShelf (const double sampleRate, - const double cutOffFrequency, - const double Q, - const float gainFactor) noexcept +IIRCoefficients IIRCoefficients::makeHighShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) noexcept { jassert (sampleRate > 0); jassert (Q > 0); - const double A = jmax (0.0f, gainFactor); + const double A = jmax (0.0f, std::sqrt (gainFactor)); const double aminus1 = A - 1.0; const double aplus1 = A + 1.0; const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; @@ -179,74 +131,114 @@ void IIRFilter::makeHighShelf (const double sampleRate, const double beta = std::sin (omega) * std::sqrt (A) / Q; const double aminus1TimesCoso = aminus1 * coso; - setCoefficients (A * (aplus1 + aminus1TimesCoso + beta), - A * -2.0 * (aminus1 + aplus1 * coso), - A * (aplus1 + aminus1TimesCoso - beta), - aplus1 - aminus1TimesCoso + beta, - 2.0 * (aminus1 - aplus1 * coso), - aplus1 - aminus1TimesCoso - beta); + return IIRCoefficients (A * (aplus1 + aminus1TimesCoso + beta), + A * -2.0 * (aminus1 + aplus1 * coso), + A * (aplus1 + aminus1TimesCoso - beta), + aplus1 - aminus1TimesCoso + beta, + 2.0 * (aminus1 - aplus1 * coso), + aplus1 - aminus1TimesCoso - beta); } -void IIRFilter::makeBandPass (const double sampleRate, - const double centreFrequency, - const double Q, - const float gainFactor) noexcept +IIRCoefficients IIRCoefficients::makePeakFilter (const double sampleRate, + const double centreFrequency, + const double Q, + const float gainFactor) noexcept { jassert (sampleRate > 0); jassert (Q > 0); - const double A = jmax (0.0f, gainFactor); + const double A = jmax (0.0f, std::sqrt (gainFactor)); const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; const double alpha = 0.5 * std::sin (omega) / Q; const double c2 = -2.0 * std::cos (omega); const double alphaTimesA = alpha * A; const double alphaOverA = alpha / A; - setCoefficients (1.0 + alphaTimesA, - c2, - 1.0 - alphaTimesA, - 1.0 + alphaOverA, - c2, - 1.0 - alphaOverA); + return IIRCoefficients (1.0 + alphaTimesA, + c2, + 1.0 - alphaTimesA, + 1.0 + alphaOverA, + c2, + 1.0 - alphaOverA); } +//============================================================================== +IIRFilter::IIRFilter() noexcept + : v1 (0), v2 (0), active (false) +{ +} + +IIRFilter::IIRFilter (const IIRFilter& other) noexcept + : v1 (0), v2 (0), active (other.active) +{ + const SpinLock::ScopedLockType sl (other.processLock); + coefficients = other.coefficients; +} + +IIRFilter::~IIRFilter() noexcept +{ +} + +//============================================================================== void IIRFilter::makeInactive() noexcept { - const ScopedLock sl (processLock); + const SpinLock::ScopedLockType sl (processLock); active = false; } -//============================================================================== -void IIRFilter::copyCoefficientsFrom (const IIRFilter& other) noexcept +void IIRFilter::setCoefficients (const IIRCoefficients& newCoefficients) noexcept { - const ScopedLock sl (processLock); - - memcpy (coefficients, other.coefficients, sizeof (coefficients)); - active = other.active; -} - -//============================================================================== -void IIRFilter::setCoefficients (double c1, double c2, double c3, - double c4, double c5, double c6) noexcept -{ - const double a = 1.0 / c4; - - c1 *= a; - c2 *= a; - c3 *= a; - c5 *= a; - c6 *= a; - - const ScopedLock sl (processLock); - - coefficients[0] = (float) c1; - coefficients[1] = (float) c2; - coefficients[2] = (float) c3; - coefficients[3] = (float) c4; - coefficients[4] = (float) c5; - coefficients[5] = (float) c6; + const SpinLock::ScopedLockType sl (processLock); + coefficients = newCoefficients; active = true; } +//============================================================================== +void IIRFilter::reset() noexcept +{ + const SpinLock::ScopedLockType sl (processLock); + v1 = v2 = 0; +} + +float IIRFilter::processSingleSampleRaw (const float in) noexcept +{ + float out = coefficients.coefficients[0] * in + v1; + + JUCE_SNAP_TO_ZERO (out); + + v1 = coefficients.coefficients[1] * in - coefficients.coefficients[3] * out + v2; + v2 = coefficients.coefficients[2] * in - coefficients.coefficients[4] * out; + + return out; +} + +void IIRFilter::processSamples (float* const samples, const int numSamples) noexcept +{ + const SpinLock::ScopedLockType sl (processLock); + + if (active) + { + const float c0 = coefficients.coefficients[0]; + const float c1 = coefficients.coefficients[1]; + const float c2 = coefficients.coefficients[2]; + const float c3 = coefficients.coefficients[3]; + const float c4 = coefficients.coefficients[4]; + float lv1 = v1, lv2 = v2; + + for (int i = 0; i < numSamples; ++i) + { + const float in = samples[i]; + const float out = c0 * in + lv1; + samples[i] = out; + + lv1 = c1 * in - c3 * out + lv2; + lv2 = c2 * in - c4 * out; + } + + JUCE_SNAP_TO_ZERO (lv1); v1 = lv1; + JUCE_SNAP_TO_ZERO (lv2); v2 = lv2; + } +} + #undef JUCE_SNAP_TO_ZERO diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h index 78e2df6..8269cf5 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h +++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_IIRFilter.h @@ -1,38 +1,116 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IIRFILTER_JUCEHEADER__ -#define __JUCE_IIRFILTER_JUCEHEADER__ +#ifndef JUCE_IIRFILTER_H_INCLUDED +#define JUCE_IIRFILTER_H_INCLUDED +class IIRFilter; + +//============================================================================== +/** + A set of coefficients for use in an IIRFilter object. + + @see IIRFilter +*/ +class JUCE_API IIRCoefficients +{ +public: + //============================================================================== + /** Creates a null set of coefficients (which will produce silence). */ + IIRCoefficients() noexcept; + + /** Directly constructs an object from the raw coefficients. + Most people will want to use the static methods instead of this, but + the constructor is public to allow tinkerers to create their own custom + filters! + */ + IIRCoefficients (double c1, double c2, double c3, + double c4, double c5, double c6) noexcept; + + /** Creates a copy of another filter. */ + IIRCoefficients (const IIRCoefficients&) noexcept; + /** Creates a copy of another filter. */ + IIRCoefficients& operator= (const IIRCoefficients&) noexcept; + /** Destructor. */ + ~IIRCoefficients() noexcept; + + /** Returns the coefficients for a low-pass filter. */ + static IIRCoefficients makeLowPass (double sampleRate, + double frequency) noexcept; + + /** Returns the coefficients for a high-pass filter. */ + static IIRCoefficients makeHighPass (double sampleRate, + double frequency) noexcept; + + //============================================================================== + /** Returns the coefficients for 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. + */ + static IIRCoefficients makeLowShelf (double sampleRate, + double cutOffFrequency, + double Q, + float gainFactor) noexcept; + + /** Returns the coefficients for 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. + */ + static IIRCoefficients makeHighShelf (double sampleRate, + double cutOffFrequency, + double Q, + float gainFactor) noexcept; + + /** Returns the coefficients for a peak filter centred around a + given 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. + */ + static IIRCoefficients makePeakFilter (double sampleRate, + double centreFrequency, + double Q, + float gainFactor) noexcept; + + //============================================================================== + /** The raw coefficients. + You should leave these numbers alone unless you really know what you're doing. + */ + float coefficients[5]; +}; //============================================================================== /** An IIR filter that can perform low, high, or band-pass filtering on an audio signal. - @see IIRFilterAudioSource + @see IIRCoefficient, IIRFilterAudioSource */ class JUCE_API IIRFilter { @@ -41,16 +119,26 @@ 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. + you process with it. Use the setCoefficients() method to turn it into the + type of filter needed. */ - IIRFilter(); + IIRFilter() noexcept; /** Creates a copy of another filter. */ - IIRFilter (const IIRFilter& other); + IIRFilter (const IIRFilter&) noexcept; /** Destructor. */ - ~IIRFilter(); + ~IIRFilter() noexcept; + + //============================================================================== + /** Clears the filter so that any incoming data passes through unchanged. */ + void makeInactive() noexcept; + + /** Applies a set of coefficients to this filter. */ + void setCoefficients (const IIRCoefficients& newCoefficients) noexcept; + + /** Returns the coefficients that this filter is using. */ + IIRCoefficients getCoefficients() const noexcept { return coefficients; } //============================================================================== /** Resets the filter's processing pipeline, ready to start a new stream of data. @@ -61,10 +149,8 @@ public: */ void reset() noexcept; - /** Performs the filter operation on the given set of samples. - */ - void processSamples (float* samples, - int numSamples) noexcept; + /** Performs the filter operation on the given set of samples. */ + void processSamples (float* samples, int numSamples) noexcept; /** Processes a single sample, without any locking or checking. @@ -73,77 +159,16 @@ public: */ float processSingleSampleRaw (float sample) noexcept; - //============================================================================== - /** Sets the filter up to act as a low-pass filter. - */ - void makeLowPass (double sampleRate, - double frequency) noexcept; - - /** Sets the filter up to act as a high-pass filter. - */ - void makeHighPass (double sampleRate, - double frequency) noexcept; - - //============================================================================== - /** 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) noexcept; - - /** 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) noexcept; - - /** 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) noexcept; - - /** Clears the filter's coefficients so that it becomes inactive. - */ - void makeInactive() noexcept; - - //============================================================================== - /** Makes this filter duplicate the set-up of another one. - */ - void copyCoefficientsFrom (const IIRFilter& other) noexcept; - - protected: //============================================================================== - CriticalSection processLock; - - void setCoefficients (double c1, double c2, double c3, - double c4, double c5, double c6) noexcept; - + SpinLock processLock; + IIRCoefficients coefficients; + float v1, v2; bool active; - float coefficients[6]; - float x1, x2, y1, y2; - // (use the copyCoefficientsFrom() method instead of this operator) IIRFilter& operator= (const IIRFilter&); - JUCE_LEAK_DETECTOR (IIRFilter); + JUCE_LEAK_DETECTOR (IIRFilter) }; -#endif // __JUCE_IIRFILTER_JUCEHEADER__ +#endif // JUCE_IIRFILTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp new file mode 100644 index 0000000..7bab3a5 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp @@ -0,0 +1,200 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +namespace LagrangeHelpers +{ + template + struct ResampleHelper + { + static forcedinline void calc (float& a, float b) { a *= b * (1.0f / k); } + }; + + template<> + struct ResampleHelper <0> + { + static forcedinline void calc (float&, float) {} + }; + + template + static forcedinline float calcCoefficient (float input, const float offset) noexcept + { + ResampleHelper <0 - k>::calc (input, -2.0f - offset); + ResampleHelper <1 - k>::calc (input, -1.0f - offset); + ResampleHelper <2 - k>::calc (input, 0.0f - offset); + ResampleHelper <3 - k>::calc (input, 1.0f - offset); + ResampleHelper <4 - k>::calc (input, 2.0f - offset); + return input; + } + + static forcedinline float valueAtOffset (const float* const inputs, const float offset) noexcept + { + return calcCoefficient<0> (inputs[4], offset) + + calcCoefficient<1> (inputs[3], offset) + + calcCoefficient<2> (inputs[2], offset) + + calcCoefficient<3> (inputs[1], offset) + + calcCoefficient<4> (inputs[0], offset); + } + + static forcedinline void push (float* inputs, const float newValue) noexcept + { + inputs[4] = inputs[3]; + inputs[3] = inputs[2]; + inputs[2] = inputs[1]; + inputs[1] = inputs[0]; + inputs[0] = newValue; + } +} + +//============================================================================== +LagrangeInterpolator::LagrangeInterpolator() { reset(); } +LagrangeInterpolator::~LagrangeInterpolator() {} + +void LagrangeInterpolator::reset() noexcept +{ + subSamplePos = 1.0; + + for (int i = 0; i < numElementsInArray (lastInputSamples); ++i) + lastInputSamples[i] = 0; +} + +int LagrangeInterpolator::process (const double actualRatio, const float* in, + float* out, const int numOut) noexcept +{ + if (actualRatio == 1.0) + { + memcpy (out, in, (size_t) numOut * sizeof (float)); + + if (numOut >= 4) + { + memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float)); + } + else + { + for (int i = 0; i < numOut; ++i) + LagrangeHelpers::push (lastInputSamples, in[i]); + } + + return numOut; + } + + const float* const originalIn = in; + double pos = subSamplePos; + + if (actualRatio < 1.0) + { + for (int i = numOut; --i >= 0;) + { + if (pos >= 1.0) + { + LagrangeHelpers::push (lastInputSamples, *in++); + pos -= 1.0; + } + + *out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); + pos += actualRatio; + } + } + else + { + for (int i = numOut; --i >= 0;) + { + while (pos < actualRatio) + { + LagrangeHelpers::push (lastInputSamples, *in++); + pos += 1.0; + } + + pos -= actualRatio; + *out++ = LagrangeHelpers::valueAtOffset (lastInputSamples, 1.0f - (float) pos); + } + } + + subSamplePos = pos; + return (int) (in - originalIn); +} + +int LagrangeInterpolator::processAdding (const double actualRatio, const float* in, + float* out, const int numOut, const float gain) noexcept +{ + if (actualRatio == 1.0) + { + if (gain != 1.0f) + { + for (int i = 0; i < numOut; ++i) + out[i] += in[i] * gain; + } + else + { + for (int i = 0; i < numOut; ++i) + out[i] += in[i]; + } + + if (numOut >= 4) + { + memcpy (lastInputSamples, in + (numOut - 4), 4 * sizeof (float)); + } + else + { + for (int i = 0; i < numOut; ++i) + LagrangeHelpers::push (lastInputSamples, in[i]); + } + + return numOut; + } + + const float* const originalIn = in; + double pos = subSamplePos; + + if (actualRatio < 1.0) + { + for (int i = numOut; --i >= 0;) + { + if (pos >= 1.0) + { + LagrangeHelpers::push (lastInputSamples, *in++); + pos -= 1.0; + } + + *out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, (float) pos); + pos += actualRatio; + } + } + else + { + for (int i = numOut; --i >= 0;) + { + while (pos < actualRatio) + { + LagrangeHelpers::push (lastInputSamples, *in++); + pos += 1.0; + } + + pos -= actualRatio; + *out++ += gain * LagrangeHelpers::valueAtOffset (lastInputSamples, jmax (0.0f, 1.0f - (float) pos)); + } + } + + subSamplePos = pos; + return (int) (in - originalIn); +} diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h new file mode 100644 index 0000000..caa4802 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.h @@ -0,0 +1,94 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED +#define JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED + + +//============================================================================== +/** + Interpolator for resampling a stream of floats using 4-point lagrange interpolation. + + Note that the resampler is stateful, so when there's a break in the continuity + of the input stream you're feeding it, you should call reset() before feeding + it any new data. And like with any other stateful filter, if you're resampling + multiple channels, make sure each one uses its own LagrangeInterpolator + object. +*/ +class JUCE_API LagrangeInterpolator +{ +public: + LagrangeInterpolator(); + ~LagrangeInterpolator(); + + /** Resets the state of the interpolator. + Call this when there's a break in the continuity of the input data stream. + */ + void reset() noexcept; + + /** Resamples a stream of samples. + + @param speedRatio the number of input samples to use for each output sample + @param inputSamples the source data to read from. This must contain at + least (speedRatio * numOutputSamplesToProduce) samples. + @param outputSamples the buffer to write the results into + @param numOutputSamplesToProduce the number of output samples that should be created + + @returns the actual number of input samples that were used + */ + int process (double speedRatio, + const float* inputSamples, + float* outputSamples, + int numOutputSamplesToProduce) noexcept; + + /** Resamples a stream of samples, adding the results to the output data + with a gain. + + @param speedRatio the number of input samples to use for each output sample + @param inputSamples the source data to read from. This must contain at + least (speedRatio * numOutputSamplesToProduce) samples. + @param outputSamples the buffer to write the results to - the result values will be added + to any pre-existing data in this buffer after being multiplied by + the gain factor + @param numOutputSamplesToProduce the number of output samples that should be created + @param gain a gain factor to multiply the resulting samples by before + adding them to the destination buffer + + @returns the actual number of input samples that were used + */ + int processAdding (double speedRatio, + const float* inputSamples, + float* outputSamples, + int numOutputSamplesToProduce, + float gain) noexcept; + +private: + float lastInputSamples[5]; + double subSamplePos; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LagrangeInterpolator) +}; + + +#endif // JUCE_LAGRANGEINTERPOLATOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h index e4daa01..53457a6 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h +++ b/JuceLibraryCode/modules/juce_audio_basics/effects/juce_Reverb.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_REVERB_JUCEHEADER__ -#define __JUCE_REVERB_JUCEHEADER__ +#ifndef JUCE_REVERB_H_INCLUDED +#define JUCE_REVERB_H_INCLUDED //============================================================================== @@ -104,14 +103,13 @@ public: const int stereoSpread = 23; const int intSampleRate = (int) sampleRate; - int i; - for (i = 0; i < numCombs; ++i) + for (int i = 0; i < numCombs; ++i) { comb[0][i].setSize ((intSampleRate * combTunings[i]) / 44100); comb[1][i].setSize ((intSampleRate * (combTunings[i] + stereoSpread)) / 44100); } - for (i = 0; i < numAllPasses; ++i) + for (int i = 0; i < numAllPasses; ++i) { allPass[0][i].setSize ((intSampleRate * allPassTunings[i]) / 44100); allPass[1][i].setSize ((intSampleRate * (allPassTunings[i] + stereoSpread)) / 44100); @@ -125,11 +123,10 @@ public: { for (int j = 0; j < numChannels; ++j) { - int i; - for (i = 0; i < numCombs; ++i) + for (int i = 0; i < numCombs; ++i) comb[j][i].clear(); - for (i = 0; i < numAllPasses; ++i) + for (int i = 0; i < numAllPasses; ++i) allPass[j][i].clear(); } } @@ -148,14 +145,13 @@ public: const float input = (left[i] + right[i]) * gain; float outL = 0, outR = 0; - int j; - for (j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel + for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel { outL += comb[0][j].process (input); outR += comb[1][j].process (input); } - for (j = 0; j < numAllPasses; ++j) // run the allpass filters in series + for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series { outL = allPass[0][j].process (outL); outR = allPass[1][j].process (outR); @@ -179,14 +175,13 @@ public: const float input = samples[i] * gain; float output = 0; - int j; - for (j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel + for (int j = 0; j < numCombs; ++j) // accumulate the comb filters in parallel output += comb[0][j].process (input); - for (j = 0; j < numAllPasses; ++j) // run the allpass filters in series + for (int j = 0; j < numAllPasses; ++j) // run the allpass filters in series output = allPass[0][j].process (output); - samples[i] = output * wet1 + input * dry; + samples[i] = output * wet1 + samples[i] * dry; } } @@ -208,7 +203,7 @@ private: shouldUpdateDamping = false; if (isFrozen (parameters.freezeMode)) - setDamping (1.0f, 0.0f); + setDamping (0.0f, 1.0f); else setDamping (parameters.damping * dampScaleFactor, parameters.roomSize * roomScaleFactor + roomOffset); @@ -225,7 +220,10 @@ private: class CombFilter { public: - CombFilter() noexcept : bufferSize (0), bufferIndex (0) {} + CombFilter() noexcept + : bufferSize (0), bufferIndex (0), + feedback (0), last (0), damp1 (0), damp2 (0) + {} void setSize (const int size) { @@ -270,7 +268,7 @@ private: int bufferSize, bufferIndex; float feedback, last, damp1, damp2; - JUCE_DECLARE_NON_COPYABLE (CombFilter); + JUCE_DECLARE_NON_COPYABLE (CombFilter) }; //============================================================================== @@ -310,7 +308,7 @@ private: HeapBlock buffer; int bufferSize, bufferIndex; - JUCE_DECLARE_NON_COPYABLE (AllPassFilter); + JUCE_DECLARE_NON_COPYABLE (AllPassFilter) }; enum { numCombs = 8, numAllPasses = 4, numChannels = 2 }; @@ -318,8 +316,8 @@ private: CombFilter comb [numChannels][numCombs]; AllPassFilter allPass [numChannels][numAllPasses]; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Reverb) }; -#endif // __JUCE_REVERB_JUCEHEADER__ +#endif // JUCE_REVERB_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp index 45e87a6..dcd53cd 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_AUDIO_BASICS_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_AUDIO_BASICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -35,16 +34,47 @@ // Your project must contain an AppConfig.h file with your project-specific settings in it, // and your header search path must make it accessible to the module's files. #include "AppConfig.h" - #include "juce_audio_basics.h" +#if JUCE_MINGW && ! defined (__SSE2__) + #define JUCE_USE_SSE_INTRINSICS 0 +#endif + +#ifndef JUCE_USE_SSE_INTRINSICS + #define JUCE_USE_SSE_INTRINSICS 1 +#endif + +#if ! JUCE_INTEL + #undef JUCE_USE_SSE_INTRINSICS +#endif + +#if JUCE_USE_SSE_INTRINSICS + #include +#endif + +#ifndef JUCE_USE_VDSP_FRAMEWORK + #define JUCE_USE_VDSP_FRAMEWORK 1 +#endif + +#if (JUCE_MAC || JUCE_IOS) && JUCE_USE_VDSP_FRAMEWORK + #include +#else + #undef JUCE_USE_VDSP_FRAMEWORK +#endif + +#if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) + #define JUCE_USE_ARM_NEON 1 + #include +#endif + namespace juce { -// START_AUTOINCLUDE buffers/*.cpp, effects/*.cpp, midi/*.cpp, sources/*.cpp, synthesisers/*.cpp #include "buffers/juce_AudioDataConverters.cpp" #include "buffers/juce_AudioSampleBuffer.cpp" +#include "buffers/juce_FloatVectorOperations.cpp" #include "effects/juce_IIRFilter.cpp" +#include "effects/juce_LagrangeInterpolator.cpp" #include "midi/juce_MidiBuffer.cpp" #include "midi/juce_MidiFile.cpp" #include "midi/juce_MidiKeyboardState.cpp" @@ -58,6 +88,5 @@ namespace juce #include "sources/juce_ReverbAudioSource.cpp" #include "sources/juce_ToneGeneratorAudioSource.cpp" #include "synthesisers/juce_Synthesiser.cpp" -// END_AUTOINCLUDE } diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h index fe513ba..70c5432 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIO_BASICS_JUCEHEADER__ -#define __JUCE_AUDIO_BASICS_JUCEHEADER__ +#ifndef JUCE_AUDIO_BASICS_H_INCLUDED +#define JUCE_AUDIO_BASICS_H_INCLUDED #include "../juce_core/juce_core.h" @@ -32,69 +31,29 @@ namespace juce { -// START_AUTOINCLUDE buffers, effects, midi, sources, synthesisers -#ifndef __JUCE_AUDIODATACONVERTERS_JUCEHEADER__ - #include "buffers/juce_AudioDataConverters.h" -#endif -#ifndef __JUCE_AUDIOSAMPLEBUFFER_JUCEHEADER__ - #include "buffers/juce_AudioSampleBuffer.h" -#endif -#ifndef __JUCE_DECIBELS_JUCEHEADER__ - #include "effects/juce_Decibels.h" -#endif -#ifndef __JUCE_IIRFILTER_JUCEHEADER__ - #include "effects/juce_IIRFilter.h" -#endif -#ifndef __JUCE_REVERB_JUCEHEADER__ - #include "effects/juce_Reverb.h" -#endif -#ifndef __JUCE_MIDIBUFFER_JUCEHEADER__ - #include "midi/juce_MidiBuffer.h" -#endif -#ifndef __JUCE_MIDIFILE_JUCEHEADER__ - #include "midi/juce_MidiFile.h" -#endif -#ifndef __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ - #include "midi/juce_MidiKeyboardState.h" -#endif -#ifndef __JUCE_MIDIMESSAGE_JUCEHEADER__ - #include "midi/juce_MidiMessage.h" -#endif -#ifndef __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ - #include "midi/juce_MidiMessageSequence.h" -#endif -#ifndef __JUCE_AUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_AudioSource.h" -#endif -#ifndef __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_BufferingAudioSource.h" -#endif -#ifndef __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_ChannelRemappingAudioSource.h" -#endif -#ifndef __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_IIRFilterAudioSource.h" -#endif -#ifndef __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_MixerAudioSource.h" -#endif -#ifndef __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_PositionableAudioSource.h" -#endif -#ifndef __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_ResamplingAudioSource.h" -#endif -#ifndef __JUCE_REVERBAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_ReverbAudioSource.h" -#endif -#ifndef __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ - #include "sources/juce_ToneGeneratorAudioSource.h" -#endif -#ifndef __JUCE_SYNTHESISER_JUCEHEADER__ - #include "synthesisers/juce_Synthesiser.h" -#endif -// END_AUTOINCLUDE +#include "buffers/juce_AudioDataConverters.h" +#include "buffers/juce_AudioSampleBuffer.h" +#include "buffers/juce_FloatVectorOperations.h" +#include "effects/juce_Decibels.h" +#include "effects/juce_IIRFilter.h" +#include "effects/juce_LagrangeInterpolator.h" +#include "effects/juce_Reverb.h" +#include "midi/juce_MidiMessage.h" +#include "midi/juce_MidiBuffer.h" +#include "midi/juce_MidiMessageSequence.h" +#include "midi/juce_MidiFile.h" +#include "midi/juce_MidiKeyboardState.h" +#include "sources/juce_AudioSource.h" +#include "sources/juce_PositionableAudioSource.h" +#include "sources/juce_BufferingAudioSource.h" +#include "sources/juce_ChannelRemappingAudioSource.h" +#include "sources/juce_IIRFilterAudioSource.h" +#include "sources/juce_MixerAudioSource.h" +#include "sources/juce_ResamplingAudioSource.h" +#include "sources/juce_ReverbAudioSource.h" +#include "sources/juce_ToneGeneratorAudioSource.h" +#include "synthesisers/juce_Synthesiser.h" } -#endif // __JUCE_AUDIO_BASICS_JUCEHEADER__ +#endif // JUCE_AUDIO_BASICS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm index 3feb355..54083fd 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_module_info b/JuceLibraryCode/modules/juce_audio_basics/juce_module_info index 15121ef..79340c9 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_basics", "name": "JUCE audio and midi data classes", - "version": "2.0.21", + "version": "3.0.6", "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", @@ -17,5 +17,8 @@ "midi/*", "effects/*", "sources/*", - "synthesisers/*" ] + "synthesisers/*" ], + + "OSXFrameworks": "Accelerate", + "iOSFrameworks": "Accelerate" } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp index 51bf691..0a6a1f9 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -27,17 +26,17 @@ namespace MidiBufferHelpers { inline int getEventTime (const void* const d) noexcept { - return *static_cast (d); + return *static_cast (d); } inline uint16 getEventDataSize (const void* const d) noexcept { - return *reinterpret_cast (static_cast (d) + sizeof (int)); + return *reinterpret_cast (static_cast (d) + sizeof (int32)); } inline uint16 getEventTotalSize (const void* const d) noexcept { - return getEventDataSize (d) + sizeof (int) + sizeof (uint16); + return getEventDataSize (d) + sizeof (int32) + sizeof (uint16); } static int findActualEventLength (const uint8* const data, const int maxBytes) noexcept @@ -68,68 +67,44 @@ namespace MidiBufferHelpers return size; } + + static uint8* findEventAfter (uint8* d, uint8* endData, const int samplePosition) noexcept + { + while (d < endData && getEventTime (d) <= samplePosition) + d += getEventTotalSize (d); + + return d; + } } //============================================================================== -MidiBuffer::MidiBuffer() noexcept - : bytesUsed (0) +MidiBuffer::MidiBuffer() noexcept {} +MidiBuffer::~MidiBuffer() {} + +MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept : data (other.data) {} + +MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept { + data = other.data; + return *this; } MidiBuffer::MidiBuffer (const MidiMessage& message) noexcept - : bytesUsed (0) { addEvent (message, 0); } -MidiBuffer::MidiBuffer (const MidiBuffer& other) noexcept - : data (other.data), - bytesUsed (other.bytesUsed) -{ -} - -MidiBuffer& MidiBuffer::operator= (const MidiBuffer& other) noexcept -{ - bytesUsed = other.bytesUsed; - data = other.data; - - return *this; -} - -void MidiBuffer::swapWith (MidiBuffer& other) noexcept -{ - data.swapWith (other.data); - std::swap (bytesUsed, other.bytesUsed); -} - -MidiBuffer::~MidiBuffer() -{ -} - -inline uint8* MidiBuffer::getData() const noexcept -{ - return static_cast (data.getData()); -} - -void MidiBuffer::clear() noexcept -{ - bytesUsed = 0; -} +void MidiBuffer::swapWith (MidiBuffer& other) noexcept { data.swapWith (other.data); } +void MidiBuffer::clear() noexcept { data.clearQuick(); } +void MidiBuffer::ensureSize (size_t minimumNumBytes) { data.ensureStorageAllocated ((int) minimumNumBytes); } +bool MidiBuffer::isEmpty() const noexcept { return data.size() == 0; } void MidiBuffer::clear (const int startSample, const int numSamples) { - uint8* const start = findEventAfter (getData(), startSample - 1); - uint8* const end = findEventAfter (start, startSample + numSamples - 1); + uint8* const start = MidiBufferHelpers::findEventAfter (data.begin(), data.end(), startSample - 1); + uint8* const end = MidiBufferHelpers::findEventAfter (start, data.end(), startSample + numSamples - 1); - if (end > start) - { - const int bytesToMove = bytesUsed - (int) (end - getData()); - - if (bytesToMove > 0) - memmove (start, end, (size_t) bytesToMove); - - bytesUsed -= (int) (end - start); - } + data.removeRange ((int) (start - data.begin()), (int) (end - data.begin())); } void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) @@ -139,27 +114,19 @@ void MidiBuffer::addEvent (const MidiMessage& m, const int sampleNumber) void MidiBuffer::addEvent (const void* const newData, const int maxBytes, const int sampleNumber) { - const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); + const int numBytes = MidiBufferHelpers::findActualEventLength (static_cast (newData), maxBytes); if (numBytes > 0) { - size_t spaceNeeded = (size_t) bytesUsed + (size_t) numBytes + sizeof (int) + sizeof (uint16); - data.ensureSize ((spaceNeeded + spaceNeeded / 2 + 8) & ~(size_t) 7); + const size_t newItemSize = (size_t) numBytes + sizeof (int32) + sizeof (uint16); + const int offset = (int) (MidiBufferHelpers::findEventAfter (data.begin(), data.end(), sampleNumber) - data.begin()); - uint8* d = findEventAfter (getData(), sampleNumber); - const int bytesToMove = bytesUsed - (int) (d - getData()); + data.insertMultiple (offset, 0, (int) newItemSize); - if (bytesToMove > 0) - memmove (d + numBytes + sizeof (int) + sizeof (uint16), d, (size_t) bytesToMove); - - *reinterpret_cast (d) = sampleNumber; - d += sizeof (int); - *reinterpret_cast (d) = (uint16) numBytes; - d += sizeof (uint16); - - memcpy (d, newData, (size_t) numBytes); - - bytesUsed += sizeof (int) + sizeof (uint16) + (size_t) numBytes; + uint8* const d = data.begin() + offset; + *reinterpret_cast (d) = sampleNumber; + *reinterpret_cast (d + 4) = (uint16) numBytes; + memcpy (d + 6, newData, (size_t) numBytes); } } @@ -181,45 +148,30 @@ void MidiBuffer::addEvents (const MidiBuffer& otherBuffer, } } -void MidiBuffer::ensureSize (size_t minimumNumBytes) -{ - data.ensureSize (minimumNumBytes); -} - -bool MidiBuffer::isEmpty() const noexcept -{ - return bytesUsed == 0; -} - int MidiBuffer::getNumEvents() const noexcept { int n = 0; - const uint8* d = getData(); - const uint8* const end = d + bytesUsed; + const uint8* const end = data.end(); - while (d < end) - { + for (const uint8* d = data.begin(); d < end; ++n) d += MidiBufferHelpers::getEventTotalSize (d); - ++n; - } return n; } int MidiBuffer::getFirstEventTime() const noexcept { - return bytesUsed > 0 ? MidiBufferHelpers::getEventTime (data.getData()) : 0; + return data.size() > 0 ? MidiBufferHelpers::getEventTime (data.begin()) : 0; } int MidiBuffer::getLastEventTime() const noexcept { - if (bytesUsed == 0) + if (data.size() == 0) return 0; - const uint8* d = getData(); - const uint8* const endData = d + bytesUsed; + const uint8* const endData = data.end(); - for (;;) + for (const uint8* d = data.begin();;) { const uint8* const nextOne = d + MidiBufferHelpers::getEventTotalSize (d); @@ -230,20 +182,9 @@ int MidiBuffer::getLastEventTime() const noexcept } } -uint8* MidiBuffer::findEventAfter (uint8* d, const int samplePosition) const noexcept -{ - const uint8* const endData = getData() + bytesUsed; - - while (d < endData && MidiBufferHelpers::getEventTime (d) <= samplePosition) - d += MidiBufferHelpers::getEventTotalSize (d); - - return d; -} - //============================================================================== -MidiBuffer::Iterator::Iterator (const MidiBuffer& buffer_) noexcept - : buffer (buffer_), - data (buffer_.getData()) +MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept + : buffer (b), data (b.data.begin()) { } @@ -251,11 +192,10 @@ MidiBuffer::Iterator::~Iterator() noexcept { } -//============================================================================== void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept { - data = buffer.getData(); - const uint8* dataEnd = data + buffer.bytesUsed; + data = buffer.data.begin(); + const uint8* const dataEnd = buffer.data.end(); while (data < dataEnd && MidiBufferHelpers::getEventTime (data) < samplePosition) data += MidiBufferHelpers::getEventTotalSize (data); @@ -263,28 +203,27 @@ void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noex bool MidiBuffer::Iterator::getNextEvent (const uint8* &midiData, int& numBytes, int& samplePosition) noexcept { - if (data >= buffer.getData() + buffer.bytesUsed) + if (data >= buffer.data.end()) return false; samplePosition = MidiBufferHelpers::getEventTime (data); - numBytes = MidiBufferHelpers::getEventDataSize (data); - data += sizeof (int) + sizeof (uint16); - midiData = data; - data += numBytes; + const int itemSize = MidiBufferHelpers::getEventDataSize (data); + numBytes = itemSize; + midiData = data + sizeof (int32) + sizeof (uint16); + data += sizeof (int32) + sizeof (uint16) + itemSize; return true; } bool MidiBuffer::Iterator::getNextEvent (MidiMessage& result, int& samplePosition) noexcept { - if (data >= buffer.getData() + buffer.bytesUsed) + if (data >= buffer.data.end()) return false; samplePosition = MidiBufferHelpers::getEventTime (data); - const int numBytes = MidiBufferHelpers::getEventDataSize (data); - data += sizeof (int) + sizeof (uint16); - result = MidiMessage (data, numBytes, samplePosition); - data += numBytes; + const int itemSize = MidiBufferHelpers::getEventDataSize (data); + result = MidiMessage (data + sizeof (int32) + sizeof (uint16), itemSize, samplePosition); + data += sizeof (int32) + sizeof (uint16) + itemSize; return true; } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h index c30d725..7dfc458 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiBuffer.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIBUFFER_JUCEHEADER__ -#define __JUCE_MIDIBUFFER_JUCEHEADER__ - -#include "juce_MidiMessage.h" +#ifndef JUCE_MIDIBUFFER_H_INCLUDED +#define JUCE_MIDIBUFFER_H_INCLUDED //============================================================================== @@ -36,6 +33,11 @@ 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. + If you're working with a sequence of midi events that may need to be manipulated + or read/written to a midi file, then MidiMessageSequence is probably a more + appropriate container. MidiBuffer is designed for lower-level streams of raw + midi data. + @see MidiMessage */ class JUCE_API MidiBuffer @@ -49,10 +51,10 @@ public: explicit MidiBuffer (const MidiMessage& message) noexcept; /** Creates a copy of another MidiBuffer. */ - MidiBuffer (const MidiBuffer& other) noexcept; + MidiBuffer (const MidiBuffer&) noexcept; /** Makes a copy of another MidiBuffer. */ - MidiBuffer& operator= (const MidiBuffer& other) noexcept; + MidiBuffer& operator= (const MidiBuffer&) noexcept; /** Destructor */ ~MidiBuffer(); @@ -69,7 +71,6 @@ public: void clear (int start, int numSamples); /** Returns true if the buffer is empty. - To actually retrieve the events, use a MidiBuffer::Iterator object */ bool isEmpty() const noexcept; @@ -135,13 +136,11 @@ public: int sampleDeltaToAdd); /** Returns the sample number of the first event in the buffer. - If the buffer's empty, this will just return 0. */ int getFirstEventTime() const noexcept; /** Returns the sample number of the last event in the buffer. - If the buffer's empty, this will just return 0. */ int getLastEventTime() const noexcept; @@ -152,7 +151,7 @@ public: 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) noexcept; + void swapWith (MidiBuffer&) noexcept; /** Preallocates some memory for the buffer to use. This helps to avoid needing to reallocate space when the buffer has messages @@ -174,7 +173,7 @@ public: public: //============================================================================== /** Creates an Iterator for this MidiBuffer. */ - Iterator (const MidiBuffer& buffer) noexcept; + Iterator (const MidiBuffer&) noexcept; /** Destructor. */ ~Iterator() noexcept; @@ -217,20 +216,18 @@ public: const MidiBuffer& buffer; const uint8* data; - JUCE_DECLARE_NON_COPYABLE (Iterator); + JUCE_DECLARE_NON_COPYABLE (Iterator) }; + /** The raw data holding this buffer. + Obviously access to this data is provided at your own risk. Its internal format could + change in future, so don't write code that relies on it! + */ + Array data; + private: - //============================================================================== - friend class MidiBuffer::Iterator; - MemoryBlock data; - int bytesUsed; - - uint8* getData() const noexcept; - uint8* findEventAfter (uint8*, int samplePosition) const noexcept; - - JUCE_LEAK_DETECTOR (MidiBuffer); + JUCE_LEAK_DETECTOR (MidiBuffer) }; -#endif // __JUCE_MIDIBUFFER_JUCEHEADER__ +#endif // JUCE_MIDIBUFFER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp index 043775a..e798cc0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -92,47 +91,43 @@ namespace MidiFileHelpers const MidiMessageSequence& tempoEvents, const int timeFormat) { - if (timeFormat > 0) + if (timeFormat < 0) + return time / (-(timeFormat >> 8) * (timeFormat & 0xff)); + + double lastTime = 0.0, correctedTime = 0.0; + const double tickLen = 1.0 / (timeFormat & 0x7fff); + double secsPerTick = 0.5 * tickLen; + const int numEvents = tempoEvents.getNumEvents(); + + for (int i = 0; i < numEvents; ++i) { - double lastTime = 0.0, correctedTime = 0.0; - const double tickLen = 1.0 / (timeFormat & 0x7fff); - double secsPerTick = 0.5 * tickLen; - const int numEvents = tempoEvents.getNumEvents(); + const MidiMessage& m = tempoEvents.getEventPointer(i)->message; + const double eventTime = m.getTimeStamp(); - for (int i = 0; i < numEvents; ++i) + if (eventTime >= time) + break; + + correctedTime += (eventTime - lastTime) * secsPerTick; + lastTime = eventTime; + + if (m.isTempoMetaEvent()) + secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote(); + + while (i + 1 < numEvents) { - const MidiMessage& m = tempoEvents.getEventPointer(i)->message; - const double eventTime = m.getTimeStamp(); + const MidiMessage& m2 = tempoEvents.getEventPointer(i + 1)->message; - if (eventTime >= time) + if (m2.getTimeStamp() != eventTime) break; - correctedTime += (eventTime - lastTime) * secsPerTick; - lastTime = eventTime; + if (m2.isTempoMetaEvent()) + secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote(); - if (m.isTempoMetaEvent()) - secsPerTick = tickLen * m.getTempoSecondsPerQuarterNote(); - - while (i + 1 < numEvents) - { - const MidiMessage& m2 = tempoEvents.getEventPointer(i + 1)->message; - - if (m2.getTimeStamp() != eventTime) - break; - - if (m2.isTempoMetaEvent()) - secsPerTick = tickLen * m2.getTempoSecondsPerQuarterNote(); - - ++i; - } + ++i; } + } - return correctedTime + (time - lastTime) * secsPerTick; - } - else - { - return time / (((timeFormat & 0x7fff) >> 8) * (timeFormat & 0xff)); - } + return correctedTime + (time - lastTime) * secsPerTick; } // a comparator that puts all the note-offs before note-ons that have the same time @@ -151,6 +146,26 @@ namespace MidiFileHelpers return 0; } }; + + template + static void findAllMatchingEvents (const OwnedArray& tracks, + MidiMessageSequence& results, + MethodType method) + { + for (int i = 0; i < tracks.size(); ++i) + { + const MidiMessageSequence& track = *tracks.getUnchecked(i); + const int numEvents = track.getNumEvents(); + + for (int j = 0; j < numEvents; ++j) + { + const MidiMessage& m = track.getEventPointer(j)->message; + + if ((m.*method)()) + results.addEvent (m); + } + } + } } //============================================================================== @@ -202,36 +217,19 @@ void MidiFile::setSmpteTimeFormat (const int framesPerSecond, } //============================================================================== -void MidiFile::findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const +void MidiFile::findAllTempoEvents (MidiMessageSequence& results) const { - for (int i = tracks.size(); --i >= 0;) - { - const int numEvents = tracks.getUnchecked(i)->getNumEvents(); - - for (int j = 0; j < numEvents; ++j) - { - const MidiMessage& m = tracks.getUnchecked(i)->getEventPointer (j)->message; - - if (m.isTempoMetaEvent()) - tempoChangeEvents.addEvent (m); - } - } + MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTempoMetaEvent); } -void MidiFile::findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const +void MidiFile::findAllTimeSigEvents (MidiMessageSequence& results) const { - for (int i = tracks.size(); --i >= 0;) - { - const int numEvents = tracks.getUnchecked(i)->getNumEvents(); + MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTimeSignatureMetaEvent); +} - for (int j = 0; j < numEvents; ++j) - { - const MidiMessage& m = tracks.getUnchecked(i)->getEventPointer (j)->message; - - if (m.isTimeSignatureMetaEvent()) - timeSigEvents.addEvent (m); - } - } +void MidiFile::findAllKeySigEvents (MidiMessageSequence& results) const +{ + MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isKeySignatureMetaEvent); } double MidiFile::getLastTimestamp() const @@ -325,9 +323,8 @@ void MidiFile::readNextTrack (const uint8* data, int size) MidiFileHelpers::Sorter sorter; result.list.sort (sorter, true); - result.updateMatchedPairs(); - addTrack (result); + tracks.getLast()->updateMatchedPairs(); } //============================================================================== @@ -337,27 +334,29 @@ void MidiFile::convertTimestampTicksToSeconds() findAllTempoEvents (tempoEvents); findAllTimeSigEvents (tempoEvents); - for (int i = 0; i < tracks.size(); ++i) + if (timeFormat != 0) { - const MidiMessageSequence& ms = *tracks.getUnchecked(i); - - for (int j = ms.getNumEvents(); --j >= 0;) + for (int i = 0; i < tracks.size(); ++i) { - MidiMessage& m = ms.getEventPointer(j)->message; + const MidiMessageSequence& ms = *tracks.getUnchecked(i); - m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), - tempoEvents, - timeFormat)); + for (int j = ms.getNumEvents(); --j >= 0;) + { + MidiMessage& m = ms.getEventPointer(j)->message; + m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat)); + } } } } //============================================================================== -bool MidiFile::writeTo (OutputStream& out) +bool MidiFile::writeTo (OutputStream& out, int midiFileType) { + jassert (midiFileType >= 0 && midiFileType <= 2); + out.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MThd")); out.writeIntBigEndian (6); - out.writeShortBigEndian (1); // type + out.writeShortBigEndian ((short) midiFileType); out.writeShortBigEndian ((short) tracks.size()); out.writeShortBigEndian (timeFormat); @@ -375,50 +374,52 @@ void MidiFile::writeTrack (OutputStream& mainOut, const int trackNum) int lastTick = 0; uint8 lastStatusByte = 0; + bool endOfTrackEventWritten = false; for (int i = 0; i < ms.getNumEvents(); ++i) { const MidiMessage& mm = ms.getEventPointer(i)->message; - if (! mm.isEndOfTrackMetaEvent()) + if (mm.isEndOfTrackMetaEvent()) + endOfTrackEventWritten = true; + + const int tick = roundToInt (mm.getTimeStamp()); + const int delta = jmax (0, tick - lastTick); + MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta); + lastTick = tick; + + const uint8* data = mm.getRawData(); + int dataSize = mm.getRawDataSize(); + + const uint8 statusByte = data[0]; + + if (statusByte == lastStatusByte + && (statusByte & 0xf0) != 0xf0 + && dataSize > 1 + && i > 0) { - const int tick = roundToInt (mm.getTimeStamp()); - const int delta = jmax (0, tick - lastTick); - MidiFileHelpers::writeVariableLengthInt (out, (uint32) delta); - lastTick = tick; - - const uint8* data = mm.getRawData(); - int dataSize = mm.getRawDataSize(); - - const uint8 statusByte = data[0]; - - if (statusByte == lastStatusByte - && (statusByte & 0xf0) != 0xf0 - && dataSize > 1 - && i > 0) - { - ++data; - --dataSize; - } - else if (statusByte == 0xf0) // Write sysex message with length bytes. - { - out.writeByte ((char) statusByte); - - ++data; - --dataSize; - - MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize); - } - - out.write (data, dataSize); - lastStatusByte = statusByte; + ++data; + --dataSize; } + else if (statusByte == 0xf0) // Write sysex message with length bytes. + { + out.writeByte ((char) statusByte); + + ++data; + --dataSize; + + MidiFileHelpers::writeVariableLengthInt (out, (uint32) dataSize); + } + + out.write (data, (size_t) dataSize); + lastStatusByte = statusByte; } + if (! endOfTrackEventWritten) { out.writeByte (0); // (tick delta) const MidiMessage m (MidiMessage::endOfTrack()); - out.write (m.getRawData(), m.getRawDataSize()); + out.write (m.getRawData(), (size_t) m.getRawDataSize()); } mainOut.writeIntBigEndian ((int) ByteOrder::bigEndianInt ("MTrk")); diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h index b98cf84..2a69937 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiFile.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIFILE_JUCEHEADER__ -#define __JUCE_MIDIFILE_JUCEHEADER__ - -#include "juce_MidiMessageSequence.h" +#ifndef JUCE_MIDIFILE_H_INCLUDED +#define JUCE_MIDIFILE_H_INCLUDED //============================================================================== @@ -55,28 +52,23 @@ public: //============================================================================== /** Returns the number of tracks in the file. - @see getTrack, addTrack */ int getNumTracks() const noexcept; /** 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 + @returns a pointer to the track, or nullptr if the index is out-of-range @see getNumTracks, addTrack */ const MidiMessageSequence* getTrack (int index) const noexcept; /** 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(); @@ -123,23 +115,23 @@ public: //============================================================================== /** 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. + /** Makes a list of all the time-signature meta-events from all tracks in the midi file. + @param keySigEvents a list to which all the events will be added + */ + void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const; + /** Returns the latest timestamp in any of the tracks. (Useful for finding the length of the file). */ double getLastTimestamp() const; @@ -159,10 +151,11 @@ public: bool readFrom (InputStream& sourceStream); /** Writes the midi tracks as a standard midi file. - + The midiFileType value is written as the file's format type, which can be 0, 1 + or 2 - see the midi file spec for more info about that. @returns true if the operation succeeded. */ - bool writeTo (OutputStream& destStream); + bool writeTo (OutputStream& destStream, int midiFileType = 1); /** Converts the timestamp of all the midi events from midi ticks to seconds. @@ -174,14 +167,14 @@ public: private: //============================================================================== - OwnedArray tracks; + OwnedArray tracks; short timeFormat; - void readNextTrack (const uint8* data, int size); - void writeTrack (OutputStream& mainOut, int trackNum); + void readNextTrack (const uint8*, int size); + void writeTrack (OutputStream&, int trackNum); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiFile); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiFile) }; -#endif // __JUCE_MIDIFILE_JUCEHEADER__ +#endif // JUCE_MIDIFILE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp index d3e3711..a40262b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -180,5 +179,5 @@ void MidiKeyboardState::addListener (MidiKeyboardStateListener* const listener) void MidiKeyboardState::removeListener (MidiKeyboardStateListener* const listener) { const ScopedLock sl (lock); - listeners.removeValue (listener); + listeners.removeFirstMatchingValue (listener); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h index 90910ff..885b7d8 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiKeyboardState.h @@ -1,32 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ -#define __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ +#ifndef JUCE_MIDIKEYBOARDSTATE_H_INCLUDED +#define JUCE_MIDIKEYBOARDSTATE_H_INCLUDED -#include "juce_MidiBuffer.h" class MidiKeyboardState; @@ -181,13 +179,11 @@ public: //============================================================================== /** Registers a listener for callbacks when keys go up or down. - @see removeListener */ void addListener (MidiKeyboardStateListener* listener); /** Deregisters a listener. - @see addListener */ void removeListener (MidiKeyboardStateListener* listener); @@ -202,8 +198,8 @@ private: void noteOnInternal (int midiChannel, int midiNoteNumber, float velocity); void noteOffInternal (int midiChannel, int midiNoteNumber); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiKeyboardState) }; -#endif // __JUCE_MIDIKEYBOARDSTATE_JUCEHEADER__ +#endif // JUCE_MIDIKEYBOARDSTATE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index d8125b6..18733a3 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -40,8 +39,7 @@ namespace MidiHelpers int MidiMessage::readVariableLengthVal (const uint8* data, int& numBytesUsed) noexcept { numBytesUsed = 0; - int v = 0; - int i; + int v = 0, i; do { @@ -77,28 +75,9 @@ int MidiMessage::getMessageLengthFromFirstByte (const uint8 firstByte) noexcept return messageLengths [firstByte & 0x7f]; } -//============================================================================== -inline void MidiMessage::setToUseInternalData() noexcept -{ - data = static_cast (preallocatedData.asBytes); -} - -inline bool MidiMessage::usesAllocatedData() const noexcept -{ - return data != static_cast (preallocatedData.asBytes); -} - -inline void MidiMessage::freeData() noexcept -{ - if (usesAllocatedData()) - delete[] data; -} - //============================================================================== MidiMessage::MidiMessage() noexcept - : timeStamp (0), - data (static_cast (preallocatedData.asBytes)), - size (2) + : timeStamp (0), size (2) { preallocatedData.asBytes[0] = 0xf0; preallocatedData.asBytes[1] = 0xf7; @@ -109,22 +88,14 @@ MidiMessage::MidiMessage (const void* const d, const int dataSize, const double size (dataSize) { jassert (dataSize > 0); - - if (dataSize <= 4) - setToUseInternalData(); - else - data = new uint8 [dataSize]; - - memcpy (data, d, (size_t) dataSize); + memcpy (allocateSpace (dataSize), d, (size_t) dataSize); // check that the length matches the data.. - jassert (size > 3 || data[0] >= 0xf0 || getMessageLengthFromFirstByte (data[0]) == size); + jassert (size > 3 || *(uint8*)d >= 0xf0 || getMessageLengthFromFirstByte (*(uint8*)d) == size); } MidiMessage::MidiMessage (const int byte1, const double t) noexcept - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)), - size (1) + : timeStamp (t), size (1) { preallocatedData.asBytes[0] = (uint8) byte1; @@ -133,9 +104,7 @@ MidiMessage::MidiMessage (const int byte1, const double t) noexcept } MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noexcept - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)), - size (2) + : timeStamp (t), size (2) { preallocatedData.asBytes[0] = (uint8) byte1; preallocatedData.asBytes[1] = (uint8) byte2; @@ -145,9 +114,7 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const double t) noex } MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, const double t) noexcept - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)), - size (3) + : timeStamp (t), size (3) { preallocatedData.asBytes[0] = (uint8) byte1; preallocatedData.asBytes[1] = (uint8) byte2; @@ -158,42 +125,38 @@ MidiMessage::MidiMessage (const int byte1, const int byte2, const int byte3, con } MidiMessage::MidiMessage (const MidiMessage& other) - : timeStamp (other.timeStamp), - size (other.size) + : timeStamp (other.timeStamp), size (other.size) { - if (other.usesAllocatedData()) + if (other.allocatedData != nullptr) { - data = new uint8 [size]; - memcpy (data, other.data, (size_t) size); + allocatedData.malloc (size); + memcpy (allocatedData, other.allocatedData, (size_t) size); } else { - setToUseInternalData(); preallocatedData.asInt32 = other.preallocatedData.asInt32; } } MidiMessage::MidiMessage (const MidiMessage& other, const double newTimeStamp) - : timeStamp (newTimeStamp), - size (other.size) + : timeStamp (newTimeStamp), size (other.size) { - if (other.usesAllocatedData()) + if (other.allocatedData != nullptr) { - data = new uint8 [size]; - memcpy (data, other.data, (size_t) size); + allocatedData.malloc (size); + memcpy (allocatedData, other.allocatedData, (size_t) size); } else { - setToUseInternalData(); preallocatedData.asInt32 = other.preallocatedData.asInt32; } } -MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uint8 lastStatusByte, double t) - : timeStamp (t), - data (static_cast (preallocatedData.asBytes)) +MidiMessage::MidiMessage (const void* srcData, int sz, int& numBytesUsed, const uint8 lastStatusByte, + double t, bool sysexHasEmbeddedLength) + : timeStamp (t) { - const uint8* src = static_cast (src_); + const uint8* src = static_cast (srcData); unsigned int byte = (unsigned int) *src; if (byte < 0x80) @@ -213,7 +176,7 @@ MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uin if (byte == 0xf0) { const uint8* d = src; - bool haveReadAllLengthBytes = false; + bool haveReadAllLengthBytes = ! sysexHasEmbeddedLength; int numVariableLengthSysexBytes = 0; while (d < src + sz) @@ -230,11 +193,8 @@ MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uin break; // bytes, assume it's the end of the sysex ++numVariableLengthSysexBytes; - ++d; - continue; } - - if (! haveReadAllLengthBytes) + else if (! haveReadAllLengthBytes) { haveReadAllLengthBytes = true; ++numVariableLengthSysexBytes; @@ -243,11 +203,14 @@ MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uin ++d; } + src += numVariableLengthSysexBytes; size = 1 + (int) (d - src); - data = new uint8 [size - numVariableLengthSysexBytes]; - *data = (uint8) byte; - memcpy (data + 1, src + numVariableLengthSysexBytes, (size_t) (size - numVariableLengthSysexBytes - 1)); + uint8* dest = allocateSpace (size); + *dest = (uint8) byte; + memcpy (dest + 1, src, (size_t) (size - 1)); + + numBytesUsed += numVariableLengthSysexBytes; // (these aren't counted in the size) } else if (byte == 0xff) { @@ -255,22 +218,22 @@ MidiMessage::MidiMessage (const void* src_, int sz, int& numBytesUsed, const uin const int bytesLeft = readVariableLengthVal (src + 1, n); size = jmin (sz + 1, n + 2 + bytesLeft); - data = new uint8 [size]; - *data = (uint8) byte; - memcpy (data + 1, src, (size_t) size - 1); + uint8* dest = allocateSpace (size); + *dest = (uint8) byte; + memcpy (dest + 1, src, (size_t) size - 1); } else { preallocatedData.asInt32 = 0; size = getMessageLengthFromFirstByte ((uint8) byte); - data[0] = (uint8) byte; + preallocatedData.asBytes[0] = (uint8) byte; if (size > 1) { - data[1] = src[0]; + preallocatedData.asBytes[1] = src[0]; if (size > 2) - data[2] = src[1]; + preallocatedData.asBytes[2] = src[1]; } } @@ -290,16 +253,14 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other) timeStamp = other.timeStamp; size = other.size; - freeData(); - - if (other.usesAllocatedData()) + if (other.allocatedData != nullptr) { - data = new uint8 [size]; - memcpy (data, other.data, (size_t) size); + allocatedData.malloc (size); + memcpy (allocatedData, other.allocatedData, (size_t) size); } else { - setToUseInternalData(); + allocatedData.free(); preallocatedData.asInt32 = other.preallocatedData.asInt32; } } @@ -309,19 +270,12 @@ MidiMessage& MidiMessage::operator= (const MidiMessage& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MidiMessage::MidiMessage (MidiMessage&& other) noexcept - : timeStamp (other.timeStamp), - size (other.size) + : timeStamp (other.timeStamp), size (other.size) { - if (other.usesAllocatedData()) - { - data = other.data; - other.setToUseInternalData(); - } + if (other.allocatedData != nullptr) + allocatedData.swapWith (other.allocatedData); else - { - setToUseInternalData(); preallocatedData.asInt32 = other.preallocatedData.asInt32; - } } MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept @@ -330,31 +284,30 @@ MidiMessage& MidiMessage::operator= (MidiMessage&& other) noexcept timeStamp = other.timeStamp; size = other.size; - - freeData(); - - if (other.usesAllocatedData()) - { - data = other.data; - other.setToUseInternalData(); - } - else - { - setToUseInternalData(); - preallocatedData.asInt32 = other.preallocatedData.asInt32; - } + allocatedData.swapWith (other.allocatedData); + preallocatedData.asInt32 = other.preallocatedData.asInt32; return *this; } #endif -MidiMessage::~MidiMessage() +MidiMessage::~MidiMessage() {} + +uint8* MidiMessage::allocateSpace (int bytes) { - freeData(); + if (bytes > 4) + { + allocatedData.malloc (bytes); + return allocatedData; + } + + return preallocatedData.asBytes; } int MidiMessage::getChannel() const noexcept { + const uint8* const data = getRawData(); + if ((data[0] & 0xf0) != 0xf0) return (data[0] & 0xf) + 1; @@ -365,6 +318,8 @@ bool MidiMessage::isForChannel (const int channel) const noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 + const uint8* const data = getRawData(); + return ((data[0] & 0xf) == channel - 1) && ((data[0] & 0xf0) != 0xf0); } @@ -373,6 +328,8 @@ void MidiMessage::setChannel (const int channel) noexcept { jassert (channel > 0 && channel <= 16); // valid channels are numbered 1 to 16 + uint8* const data = getData(); + if ((data[0] & 0xf0) != (uint8) 0xf0) data[0] = (uint8) ((data[0] & (uint8) 0xf0) | (uint8)(channel - 1)); @@ -380,37 +337,43 @@ void MidiMessage::setChannel (const int channel) noexcept bool MidiMessage::isNoteOn (const bool returnTrueForVelocity0) const noexcept { + const uint8* const data = getRawData(); + return ((data[0] & 0xf0) == 0x90) && (returnTrueForVelocity0 || data[2] != 0); } bool MidiMessage::isNoteOff (const bool returnTrueForNoteOnVelocity0) const noexcept { + const uint8* const data = getRawData(); + return ((data[0] & 0xf0) == 0x80) || (returnTrueForNoteOnVelocity0 && (data[2] == 0) && ((data[0] & 0xf0) == 0x90)); } bool MidiMessage::isNoteOnOrOff() const noexcept { + const uint8* const data = getRawData(); + const int d = data[0] & 0xf0; return (d == 0x90) || (d == 0x80); } int MidiMessage::getNoteNumber() const noexcept { - return data[1]; + return getRawData()[1]; } void MidiMessage::setNoteNumber (const int newNoteNumber) noexcept { if (isNoteOnOrOff()) - data[1] = (uint8) (newNoteNumber & 127); + getData()[1] = (uint8) (newNoteNumber & 127); } uint8 MidiMessage::getVelocity() const noexcept { if (isNoteOnOrOff()) - return data[2]; + return getRawData()[2]; return 0; } @@ -423,24 +386,27 @@ float MidiMessage::getFloatVelocity() const noexcept void MidiMessage::setVelocity (const float newVelocity) noexcept { if (isNoteOnOrOff()) - data[2] = MidiHelpers::validVelocity (roundToInt (newVelocity * 127.0f)); + getData()[2] = MidiHelpers::validVelocity (roundToInt (newVelocity * 127.0f)); } void MidiMessage::multiplyVelocity (const float scaleFactor) noexcept { if (isNoteOnOrOff()) + { + uint8* const data = getData(); data[2] = MidiHelpers::validVelocity (roundToInt (scaleFactor * data[2])); + } } bool MidiMessage::isAftertouch() const noexcept { - return (data[0] & 0xf0) == 0xa0; + return (getRawData()[0] & 0xf0) == 0xa0; } int MidiMessage::getAfterTouchValue() const noexcept { jassert (isAftertouch()); - return data[2]; + return getRawData()[2]; } MidiMessage MidiMessage::aftertouchChange (const int channel, @@ -458,13 +424,13 @@ MidiMessage MidiMessage::aftertouchChange (const int channel, bool MidiMessage::isChannelPressure() const noexcept { - return (data[0] & 0xf0) == 0xd0; + return (getRawData()[0] & 0xf0) == 0xd0; } int MidiMessage::getChannelPressureValue() const noexcept { jassert (isChannelPressure()); - return data[1]; + return getRawData()[1]; } MidiMessage MidiMessage::channelPressureChange (const int channel, const int pressure) noexcept @@ -475,25 +441,25 @@ MidiMessage MidiMessage::channelPressureChange (const int channel, const int pre return MidiMessage (MidiHelpers::initialByte (0xd0, channel), pressure & 0x7f); } -bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && data[2] >= 64; } -bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && data[2] < 64; } +bool MidiMessage::isSustainPedalOn() const noexcept { return isControllerOfType (0x40) && getRawData()[2] >= 64; } +bool MidiMessage::isSustainPedalOff() const noexcept { return isControllerOfType (0x40) && getRawData()[2] < 64; } -bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && data[2] >= 64; } -bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && data[2] < 64; } +bool MidiMessage::isSostenutoPedalOn() const noexcept { return isControllerOfType (0x42) && getRawData()[2] >= 64; } +bool MidiMessage::isSostenutoPedalOff() const noexcept { return isControllerOfType (0x42) && getRawData()[2] < 64; } -bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && data[2] >= 64; } -bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && data[2] < 64; } +bool MidiMessage::isSoftPedalOn() const noexcept { return isControllerOfType (0x43) && getRawData()[2] >= 64; } +bool MidiMessage::isSoftPedalOff() const noexcept { return isControllerOfType (0x43) && getRawData()[2] < 64; } bool MidiMessage::isProgramChange() const noexcept { - return (data[0] & 0xf0) == 0xc0; + return (getRawData()[0] & 0xf0) == 0xc0; } int MidiMessage::getProgramChangeNumber() const noexcept { jassert (isProgramChange()); - return data[1]; + return getRawData()[1]; } MidiMessage MidiMessage::programChange (const int channel, const int programNumber) noexcept @@ -505,12 +471,13 @@ MidiMessage MidiMessage::programChange (const int channel, const int programNumb bool MidiMessage::isPitchWheel() const noexcept { - return (data[0] & 0xf0) == 0xe0; + return (getRawData()[0] & 0xf0) == 0xe0; } int MidiMessage::getPitchWheelValue() const noexcept { jassert (isPitchWheel()); + const uint8* const data = getRawData(); return data[1] | (data[2] << 7); } @@ -525,24 +492,25 @@ MidiMessage MidiMessage::pitchWheel (const int channel, const int position) noex bool MidiMessage::isController() const noexcept { - return (data[0] & 0xf0) == 0xb0; + return (getRawData()[0] & 0xf0) == 0xb0; } bool MidiMessage::isControllerOfType (const int controllerType) const noexcept { + const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == controllerType; } int MidiMessage::getControllerNumber() const noexcept { jassert (isController()); - return data[1]; + return getRawData()[1]; } int MidiMessage::getControllerValue() const noexcept { jassert (isController()); - return data[2]; + return getRawData()[2]; } MidiMessage MidiMessage::controllerEvent (const int channel, const int controllerType, const int value) noexcept @@ -584,6 +552,7 @@ MidiMessage MidiMessage::allNotesOff (const int channel) noexcept bool MidiMessage::isAllNotesOff() const noexcept { + const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == 123; } @@ -594,6 +563,7 @@ MidiMessage MidiMessage::allSoundOff (const int channel) noexcept bool MidiMessage::isAllSoundOff() const noexcept { + const uint8* const data = getRawData(); return (data[0] & 0xf0) == 0xb0 && data[1] == 120; } @@ -617,10 +587,10 @@ MidiMessage MidiMessage::masterVolume (const float volume) //============================================================================== bool MidiMessage::isSysEx() const noexcept { - return *data == 0xf0; + return *getRawData() == 0xf0; } -MidiMessage MidiMessage::createSysExMessage (const uint8* sysexData, const int dataSize) +MidiMessage MidiMessage::createSysExMessage (const void* sysexData, const int dataSize) { HeapBlock m ((size_t) dataSize + 2); @@ -642,16 +612,18 @@ int MidiMessage::getSysExDataSize() const noexcept } //============================================================================== -bool MidiMessage::isMetaEvent() const noexcept { return *data == 0xff; } -bool MidiMessage::isActiveSense() const noexcept { return *data == 0xfe; } +bool MidiMessage::isMetaEvent() const noexcept { return *getRawData() == 0xff; } +bool MidiMessage::isActiveSense() const noexcept { return *getRawData() == 0xfe; } int MidiMessage::getMetaEventType() const noexcept { + const uint8* const data = getRawData(); return *data != 0xff ? -1 : data[1]; } int MidiMessage::getMetaEventLength() const noexcept { + const uint8* const data = getRawData(); if (*data == 0xff) { int n; @@ -666,7 +638,7 @@ const uint8* MidiMessage::getMetaEventData() const noexcept jassert (isMetaEvent()); int n; - const uint8* d = data + 2; + const uint8* d = getRawData() + 2; readVariableLengthVal (d, n); return d + n; } @@ -682,17 +654,49 @@ bool MidiMessage::isTextMetaEvent() const noexcept String MidiMessage::getTextFromTextMetaEvent() const { - return String (reinterpret_cast (getMetaEventData()), (size_t) getMetaEventLength()); + const char* const textData = reinterpret_cast (getMetaEventData()); + return String (CharPointer_UTF8 (textData), + CharPointer_UTF8 (textData + getMetaEventLength())); } -bool MidiMessage::isTrackNameEvent() const noexcept { return (data[1] == 3) && (*data == 0xff); } -bool MidiMessage::isTempoMetaEvent() const noexcept { return (data[1] == 81) && (*data == 0xff); } -bool MidiMessage::isMidiChannelMetaEvent() const noexcept { return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } +MidiMessage MidiMessage::textMetaEvent (int type, StringRef text) +{ + jassert (type > 0 && type < 16) + + MidiMessage result; + + const size_t textSize = text.text.sizeInBytes() - 1; + + uint8 header[8]; + size_t n = sizeof (header); + + header[--n] = (uint8) (textSize & 0x7f); + + for (size_t i = textSize; (i >>= 7) != 0;) + header[--n] = (uint8) ((i & 0x7f) | 0x80); + + header[--n] = (uint8) type; + header[--n] = 0xff; + + const size_t headerLen = sizeof (header) - n; + + uint8* const dest = result.allocateSpace ((int) (headerLen + textSize)); + result.size = (int) (headerLen + textSize); + + memcpy (dest, header + n, headerLen); + memcpy (dest + headerLen, text.text.getAddress(), textSize); + + return result; +} + +bool MidiMessage::isTrackNameEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 3) && (*data == 0xff); } +bool MidiMessage::isTempoMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 81) && (*data == 0xff); } +bool MidiMessage::isMidiChannelMetaEvent() const noexcept { const uint8* data = getRawData(); return (data[1] == 0x20) && (*data == 0xff) && (data[2] == 1); } int MidiMessage::getMidiChannelMetaEventChannel() const noexcept { jassert (isMidiChannelMetaEvent()); - return data[3] + 1; + return getRawData()[3] + 1; } double MidiMessage::getTempoSecondsPerQuarterNote() const noexcept @@ -747,6 +751,7 @@ MidiMessage MidiMessage::tempoMetaEvent (int microsecondsPerQuarterNote) noexcep bool MidiMessage::isTimeSignatureMetaEvent() const noexcept { + const uint8* const data = getRawData(); return (data[1] == 0x58) && (*data == (uint8) 0xff); } @@ -776,27 +781,37 @@ MidiMessage MidiMessage::timeSignatureMetaEvent (const int numerator, const int ++powerOfTwo; } - const uint8 d[] = { 0xff, 0x58, 0x04, (uint8) numerator, - (uint8) powerOfTwo, 1, 96 }; - + const uint8 d[] = { 0xff, 0x58, 0x04, (uint8) numerator, (uint8) powerOfTwo, 1, 96 }; return MidiMessage (d, 7, 0.0); } MidiMessage MidiMessage::midiChannelMetaEvent (const int channel) noexcept { const uint8 d[] = { 0xff, 0x20, 0x01, (uint8) jlimit (0, 0xff, channel - 1) }; - return MidiMessage (d, 4, 0.0); } bool MidiMessage::isKeySignatureMetaEvent() const noexcept { - return getMetaEventType() == 89; + return getMetaEventType() == 0x59; } int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept { - return (int) *getMetaEventData(); + return (int) getMetaEventData()[0]; +} + +bool MidiMessage::isKeySignatureMajorKey() const noexcept +{ + return getMetaEventData()[1] == 0; +} + +MidiMessage MidiMessage::keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey) +{ + jassert (numberOfSharpsOrFlats >= -7 && numberOfSharpsOrFlats <= 7); + + const uint8 d[] = { 0xff, 0x59, 0x02, (uint8) numberOfSharpsOrFlats, isMinorKey ? (uint8) 1 : (uint8) 0 }; + return MidiMessage (d, 5, 0.0); } MidiMessage MidiMessage::endOfTrack() noexcept @@ -805,8 +820,8 @@ MidiMessage MidiMessage::endOfTrack() noexcept } //============================================================================== -bool MidiMessage::isSongPositionPointer() const noexcept { return *data == 0xf2; } -int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { return data[1] | (data[2] << 7); } +bool MidiMessage::isSongPositionPointer() const noexcept { return *getRawData() == 0xf2; } +int MidiMessage::getSongPositionPointerMidiBeat() const noexcept { const uint8* data = getRawData(); return data[1] | (data[2] << 7); } MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noexcept { @@ -815,21 +830,21 @@ MidiMessage MidiMessage::songPositionPointer (const int positionInMidiBeats) noe (positionInMidiBeats >> 7) & 127); } -bool MidiMessage::isMidiStart() const noexcept { return *data == 0xfa; } +bool MidiMessage::isMidiStart() const noexcept { return *getRawData() == 0xfa; } MidiMessage MidiMessage::midiStart() noexcept { return MidiMessage (0xfa); } -bool MidiMessage::isMidiContinue() const noexcept { return *data == 0xfb; } +bool MidiMessage::isMidiContinue() const noexcept { return *getRawData() == 0xfb; } MidiMessage MidiMessage::midiContinue() noexcept { return MidiMessage (0xfb); } -bool MidiMessage::isMidiStop() const noexcept { return *data == 0xfc; } +bool MidiMessage::isMidiStop() const noexcept { return *getRawData() == 0xfc; } MidiMessage MidiMessage::midiStop() noexcept { return MidiMessage (0xfc); } -bool MidiMessage::isMidiClock() const noexcept { return *data == 0xf8; } +bool MidiMessage::isMidiClock() const noexcept { return *getRawData() == 0xf8; } MidiMessage MidiMessage::midiClock() noexcept { return MidiMessage (0xf8); } -bool MidiMessage::isQuarterFrame() const noexcept { return *data == 0xf1; } -int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) data[1]) >> 4; } -int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) data[1]) & 0x0f; } +bool MidiMessage::isQuarterFrame() const noexcept { return *getRawData() == 0xf1; } +int MidiMessage::getQuarterFrameSequenceNumber() const noexcept { return ((int) getRawData()[1]) >> 4; } +int MidiMessage::getQuarterFrameValue() const noexcept { return ((int) getRawData()[1]) & 0x0f; } MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value) noexcept { @@ -838,6 +853,8 @@ MidiMessage MidiMessage::quarterFrame (const int sequenceNumber, const int value bool MidiMessage::isFullFrame() const noexcept { + const uint8* const data = getRawData(); + return data[0] == 0xf0 && data[1] == 0x7f && size >= 10 @@ -850,6 +867,7 @@ void MidiMessage::getFullFrameParameters (int& hours, int& minutes, int& seconds { jassert (isFullFrame()); + const uint8* const data = getRawData(); timecodeType = (SmpteTimecodeType) (data[5] >> 5); hours = data[5] & 0x1f; minutes = data[6]; @@ -873,6 +891,7 @@ MidiMessage MidiMessage::fullFrame (const int hours, const int minutes, bool MidiMessage::isMidiMachineControlMessage() const noexcept { + const uint8* const data = getRawData(); return data[0] == 0xf0 && data[1] == 0x7f && data[3] == 0x06 @@ -883,7 +902,7 @@ MidiMessage::MidiMachineControlCommand MidiMessage::getMidiMachineControlCommand { jassert (isMidiMachineControlMessage()); - return (MidiMachineControlCommand) data[4]; + return (MidiMachineControlCommand) getRawData()[4]; } MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineControlCommand command) @@ -896,6 +915,7 @@ MidiMessage MidiMessage::midiMachineControlCommand (MidiMessage::MidiMachineCont //============================================================================== bool MidiMessage::isMidiMachineControlGoto (int& hours, int& minutes, int& seconds, int& frames) const noexcept { + const uint8* const data = getRawData(); if (size >= 12 && data[0] == 0xf0 && data[1] == 0x7f @@ -947,102 +967,128 @@ String MidiMessage::getMidiNoteName (int note, bool useSharps, bool includeOctav return String::empty; } -const double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept +double MidiMessage::getMidiNoteInHertz (int noteNumber, const double frequencyOfA) noexcept { - noteNumber -= 12 * 6 + 9; // now 0 = A - return frequencyOfA * pow (2.0, noteNumber / 12.0); + return frequencyOfA * pow (2.0, (noteNumber - 69) / 12.0); } -String MidiMessage::getGMInstrumentName (const int n) +bool MidiMessage::isMidiNoteBlack (int noteNumber) noexcept { - const char* names[] = + return ((1 << (noteNumber % 12)) & 0x054a) != 0; +} + +const char* MidiMessage::getGMInstrumentName (const int n) +{ + static const char* names[] = { - "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", - "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", - "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Drawbar Organ", - "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", - "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", - "Electric Guitar (clean)", "Electric Guitar (mute)", "Overdriven Guitar", "Distortion Guitar", - "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", - "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", - "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", - "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", - "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", - "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", "Soprano Sax", - "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", - "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", - "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", - "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass+lead)", "Pad 1 (new age)", - "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", - "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", - "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", - "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", "Tinkle Bell", - "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", - "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", - "Applause", "Gunshot" + NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"), + NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"), + NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"), + NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"), + NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"), + NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"), + NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"), + NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"), + NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"), + NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"), + NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"), + NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"), + NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"), + NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"), + NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"), + NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"), + NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"), + NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"), + NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"), + NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"), + NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"), + NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"), + NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"), + NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"), + NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"), + NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"), + NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"), + NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"), + NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"), + NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"), + NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"), + NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot") }; - return isPositiveAndBelow (n, (int) 128) ? names[n] : (const char*) 0; + return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; } -String MidiMessage::getGMInstrumentBankName (const int n) +const char* MidiMessage::getGMInstrumentBankName (const int n) { - const char* names[] = + static const char* names[] = { - "Piano", "Chromatic Percussion", "Organ", "Guitar", - "Bass", "Strings", "Ensemble", "Brass", - "Reed", "Pipe", "Synth Lead", "Synth Pad", - "Synth Effects", "Ethnic", "Percussive", "Sound Effects" + NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"), + NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"), + NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"), + NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects") }; - return isPositiveAndBelow (n, (int) 16) ? names[n] : (const char*) 0; + return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; } -String MidiMessage::getRhythmInstrumentName (const int n) +const char* MidiMessage::getRhythmInstrumentName (const int n) { - const char* names[] = + static const char* names[] = { - "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", - "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", - "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi-Mid Tom", "Crash Cymbal 1", - "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", - "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", - "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", - "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", - "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", - "Mute Triangle", "Open Triangle" + NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"), + NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"), + NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"), + NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"), + NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"), + NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"), + NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"), + NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"), + NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"), + NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"), + NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"), + NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle") }; - return (n >= 35 && n <= 81) ? names [n - 35] : (const char*) nullptr; + return (n >= 35 && n <= 81) ? names [n - 35] : nullptr; } -String MidiMessage::getControllerName (const int n) +const char* MidiMessage::getControllerName (const int n) { - const char* names[] = + static const char* names[] = { - "Bank Select", "Modulation Wheel (coarse)", "Breath controller (coarse)", - 0, "Foot Pedal (coarse)", "Portamento Time (coarse)", - "Data Entry (coarse)", "Volume (coarse)", "Balance (coarse)", - 0, "Pan position (coarse)", "Expression (coarse)", "Effect Control 1 (coarse)", - "Effect Control 2 (coarse)", 0, 0, "General Purpose Slider 1", "General Purpose Slider 2", - "General Purpose Slider 3", "General Purpose Slider 4", 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, "Bank Select (fine)", "Modulation Wheel (fine)", "Breath controller (fine)", - 0, "Foot Pedal (fine)", "Portamento Time (fine)", "Data Entry (fine)", "Volume (fine)", - "Balance (fine)", 0, "Pan position (fine)", "Expression (fine)", "Effect Control 1 (fine)", - "Effect Control 2 (fine)", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - "Hold Pedal (on/off)", "Portamento (on/off)", "Sustenuto Pedal (on/off)", "Soft Pedal (on/off)", - "Legato Pedal (on/off)", "Hold 2 Pedal (on/off)", "Sound Variation", "Sound Timbre", - "Sound Release Time", "Sound Attack Time", "Sound Brightness", "Sound Control 6", - "Sound Control 7", "Sound Control 8", "Sound Control 9", "Sound Control 10", - "General Purpose Button 1 (on/off)", "General Purpose Button 2 (on/off)", - "General Purpose Button 3 (on/off)", "General Purpose Button 4 (on/off)", - 0, 0, 0, 0, 0, 0, 0, "Reverb Level", "Tremolo Level", "Chorus Level", "Celeste Level", - "Phaser Level", "Data Button increment", "Data Button decrement", "Non-registered Parameter (fine)", - "Non-registered Parameter (coarse)", "Registered Parameter (fine)", "Registered Parameter (coarse)", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "All Sound Off", "All Controllers Off", - "Local Keyboard (on/off)", "All Notes Off", "Omni Mode Off", "Omni Mode On", "Mono Operation", - "Poly Operation" + NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"), + nullptr, + NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"), + NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"), + nullptr, + NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"), + NEEDS_TRANS("Effect Control 2 (coarse)"), + nullptr, nullptr, + NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"), + NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"), + nullptr, + NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"), + NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"), + NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"), + NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"), + NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"), + NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"), + NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"), + NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"), + NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"), + NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"), + NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation") }; - return isPositiveAndBelow (n, (int) 128) ? names[n] : (const char*) nullptr; + return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h index 7813f6f..9002aa0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIMESSAGE_JUCEHEADER__ -#define __JUCE_MIDIMESSAGE_JUCEHEADER__ +#ifndef JUCE_MIDIMESSAGE_H_INCLUDED +#define JUCE_MIDIMESSAGE_H_INCLUDED //============================================================================== @@ -83,10 +82,14 @@ public: 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 + @param sysexHasEmbeddedLength when reading sysexes, this flag indicates whether + to expect the data to begin with a variable-length field + indicating its size */ MidiMessage (const void* data, int maxBytesToUse, int& numBytesUsed, uint8 lastStatusByte, - double timeStamp = 0); + double timeStamp = 0, + bool sysexHasEmbeddedLength = true); /** Creates an active-sense message. Since the MidiMessage has to contain a valid message, this default constructor @@ -95,10 +98,10 @@ public: MidiMessage() noexcept; /** Creates a copy of another midi message. */ - MidiMessage (const MidiMessage& other); + MidiMessage (const MidiMessage&); /** Creates a copy of another midi message, with a different timestamp. */ - MidiMessage (const MidiMessage& other, double newTimeStamp); + MidiMessage (const MidiMessage&, double newTimeStamp); /** Destructor. */ ~MidiMessage(); @@ -107,22 +110,20 @@ public: MidiMessage& operator= (const MidiMessage& other); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - MidiMessage (MidiMessage&& other) noexcept; - MidiMessage& operator= (MidiMessage&& other) noexcept; + MidiMessage (MidiMessage&&) noexcept; + MidiMessage& operator= (MidiMessage&&) noexcept; #endif //============================================================================== /** Returns a pointer to the raw midi data. - @see getRawDataSize */ - uint8* getRawData() const noexcept { return data; } + const uint8* getRawData() const noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } /** Returns the number of bytes of data in the message. - @see getRawData */ - int getRawDataSize() const noexcept { return size; } + int getRawDataSize() const noexcept { return size; } //============================================================================== /** Returns the timestamp associated with this message. @@ -141,21 +142,18 @@ public: @see setTimeStamp, addToTimeStamp */ - double getTimeStamp() const noexcept { return timeStamp; } + double getTimeStamp() const noexcept { 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) noexcept { timeStamp = newTimestamp; } + void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; } /** Adds a value to the message's timestamp. - The units for the timestamp will be application-specific. */ - void addToTimeStamp (double delta) noexcept { timeStamp += delta; } + void addToTimeStamp (double delta) noexcept { timeStamp += delta; } //============================================================================== /** Returns the midi channel associated with the message. @@ -174,9 +172,7 @@ public: bool isForChannel (int channelNumber) const noexcept; /** 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) noexcept; @@ -187,17 +183,13 @@ public: bool isSysEx() const noexcept; /** 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 noexcept; /** 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 noexcept; @@ -258,15 +250,12 @@ public: bool isNoteOnOrOff() const noexcept; /** 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 is undefined. - @see isNoteOff, getMidiNoteName, getMidiNoteInHertz, setNoteNumber */ int getNoteNumber() const noexcept; /** 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) noexcept; @@ -326,16 +315,12 @@ public: //============================================================================== /** Returns true if the message is a program (patch) change message. - @see getProgramChangeNumber, getGMInstrumentName */ bool isProgramChange() const noexcept; /** Returns the new program number of a program change message. - - If the message isn't a program change, the value returned will be - nonsense. - + If the message isn't a program change, the value returned is undefined. @see isProgramChange, getGMInstrumentName */ int getProgramChangeNumber() const noexcept; @@ -350,7 +335,6 @@ public: //============================================================================== /** Returns true if the message is a pitch-wheel move. - @see getPitchWheelValue, pitchWheel */ bool isPitchWheel() const noexcept; @@ -439,7 +423,6 @@ public: /** 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 @@ -449,7 +432,6 @@ public: /** 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 @@ -473,13 +455,11 @@ public: int value) noexcept; /** Checks whether this message is an all-notes-off message. - @see allNotesOff */ bool isAllNotesOff() const noexcept; /** Checks whether this message is an all-sound-off message. - @see allSoundOff */ bool isAllSoundOff() const noexcept; @@ -526,13 +506,11 @@ public: int getMetaEventType() const noexcept; /** Returns a pointer to the data in a meta-event. - @see isMetaEvent, getMetaEventLength */ const uint8* getMetaEventData() const noexcept; /** Returns the length of the data for a meta-event. - @see isMetaEvent, getMetaEventData */ int getMetaEventLength() const noexcept; @@ -545,32 +523,30 @@ public: bool isEndOfTrackMetaEvent() const noexcept; /** Creates an end-of-track meta-event. - @see isEndOfTrackMetaEvent */ static MidiMessage endOfTrack() noexcept; /** 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 noexcept; /** Returns true if this is a 'text' meta-event. - @see getTextFromTextMetaEvent */ bool isTextMetaEvent() const noexcept; /** Returns the text from a text meta-event. - @see isTextMetaEvent */ String getTextFromTextMetaEvent() const; + /** Creates a text meta-event. */ + static MidiMessage textMetaEvent (int type, StringRef text); + //============================================================================== /** Returns true if this is a 'tempo' meta-event. - @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote */ bool isTempoMetaEvent() const noexcept; @@ -584,49 +560,59 @@ public: double getTempoMetaEventTickLength (short timeFormat) const noexcept; /** Calculates the seconds-per-quarter-note from a tempo meta-event. - @see isTempoMetaEvent, getTempoMetaEventTickLength */ double getTempoSecondsPerQuarterNote() const noexcept; /** Creates a tempo meta-event. - @see isTempoMetaEvent */ static MidiMessage tempoMetaEvent (int microsecondsPerQuarterNote) noexcept; //============================================================================== /** Returns true if this is a 'time-signature' meta-event. - @see getTimeSignatureInfo */ bool isTimeSignatureMetaEvent() const noexcept; /** Returns the time-signature values from a time-signature meta-event. - @see isTimeSignatureMetaEvent */ void getTimeSignatureInfo (int& numerator, int& denominator) const noexcept; /** Creates a time-signature meta-event. - @see isTimeSignatureMetaEvent */ static MidiMessage timeSignatureMetaEvent (int numerator, int denominator); //============================================================================== /** Returns true if this is a 'key-signature' meta-event. - - @see getKeySignatureNumberOfSharpsOrFlats + @see getKeySignatureNumberOfSharpsOrFlats, isKeySignatureMajorKey */ bool isKeySignatureMetaEvent() const noexcept; /** Returns the key from a key-signature meta-event. - - @see isKeySignatureMetaEvent + This method must only be called if isKeySignatureMetaEvent() is true. + A positive number here indicates the number of sharps in the key signature, + and a negative number indicates a number of flats. So e.g. 3 = F# + C# + G#, + -2 = Bb + Eb + @see isKeySignatureMetaEvent, isKeySignatureMajorKey */ int getKeySignatureNumberOfSharpsOrFlats() const noexcept; + /** Returns true if this key-signature event is major, or false if it's minor. + This method must only be called if isKeySignatureMetaEvent() is true. + */ + bool isKeySignatureMajorKey() const noexcept; + + /** Creates a key-signature meta-event. + @param numberOfSharpsOrFlats if positive, this indicates the number of sharps + in the key; if negative, the number of flats + @param isMinorKey if true, the key is minor; if false, it is major + @see isKeySignatureMetaEvent + */ + static MidiMessage keySignatureMetaEvent (int numberOfSharpsOrFlats, bool isMinorKey); + //============================================================================== /** Returns true if this is a 'channel' meta-event. @@ -657,7 +643,6 @@ public: //============================================================================== /** Returns true if this is a midi start event. - @see midiStart */ bool isMidiStart() const noexcept; @@ -666,7 +651,6 @@ public: static MidiMessage midiStart() noexcept; /** Returns true if this is a midi continue event. - @see midiContinue */ bool isMidiContinue() const noexcept; @@ -675,7 +659,6 @@ public: static MidiMessage midiContinue() noexcept; /** Returns true if this is a midi stop event. - @see midiStop */ bool isMidiStop() const noexcept; @@ -684,7 +667,6 @@ public: static MidiMessage midiStop() noexcept; /** Returns true if this is a midi clock event. - @see midiClock, songPositionPointer */ bool isMidiClock() const noexcept; @@ -693,13 +675,11 @@ public: static MidiMessage midiClock() noexcept; /** Returns true if this is a song-position-pointer message. - @see getSongPositionPointerMidiBeat, songPositionPointer */ bool isSongPositionPointer() const noexcept; /** Returns the midi beat-number of a song-position-pointer message. - @see isSongPositionPointer, songPositionPointer */ int getSongPositionPointerMidiBeat() const noexcept; @@ -716,23 +696,18 @@ public: //============================================================================== /** Returns true if this is a quarter-frame midi timecode message. - @see quarterFrame, getQuarterFrameSequenceNumber, getQuarterFrameValue */ bool isQuarterFrame() const noexcept; /** 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 noexcept; /** 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 + This will be the lower nybble of the message's data-byte, a value between 0 and 15 */ int getQuarterFrameValue() const noexcept; @@ -744,7 +719,6 @@ public: static MidiMessage quarterFrame (int sequenceNumber, int value) noexcept; /** SMPTE timecode types. - Used by the getFullFrameParameters() and fullFrame() methods. */ enum SmpteTimecodeType @@ -755,8 +729,7 @@ public: fps30 = 3 }; - /** Returns true if this is a full-frame midi timecode message. - */ + /** Returns true if this is a full-frame midi timecode message. */ bool isFullFrame() const noexcept; /** Extracts the timecode information from a full-frame midi timecode message. @@ -770,8 +743,7 @@ public: int& frames, SmpteTimecodeType& timecodeType) const noexcept; - /** Creates a full-frame MTC message. - */ + /** Creates a full-frame MTC message. */ static MidiMessage fullFrame (int hours, int minutes, int seconds, @@ -796,7 +768,6 @@ public: }; /** Checks whether this is an MMC message. - If it is, you can use the getMidiMachineControlCommand() to find out its type. */ bool isMidiMachineControlMessage() const noexcept; @@ -808,14 +779,11 @@ public: */ MidiMachineControlCommand getMidiMachineControlCommand() const noexcept; - /** Creates an MMC message. - */ + /** Creates an MMC message. */ static 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, @@ -824,9 +792,7 @@ public: int& frames) const noexcept; /** Creates an MMC "goto" message. - This messages tells the device to go to a specific frame. - @see isMidiMachineControlGoto */ static MidiMessage midiMachineControlGoto (int hours, @@ -836,17 +802,15 @@ public: //============================================================================== /** Creates a master-volume change message. - @param volume the volume, 0 to 1.0 */ static 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 MidiMessage createSysExMessage (const uint8* sysexData, + static MidiMessage createSysExMessage (const void* sysexData, int dataSize); @@ -891,37 +855,37 @@ public: The frequencyOfA parameter is an optional frequency for 'A', normally 440-444Hz for concert pitch. @see getMidiNoteName */ - static const double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept; + static double getMidiNoteInHertz (int noteNumber, const double frequencyOfA = 440.0) noexcept; - /** Returns the standard name of a GM instrument. + /** Returns true if the given midi note number is a black key. */ + static bool isMidiNoteBlack (int noteNumber) noexcept; + + /** Returns the standard name of a GM instrument, or nullptr if unknown for this index. @param midiInstrumentNumber the program number 0 to 127 @see getProgramChangeNumber */ - static String getGMInstrumentName (int midiInstrumentNumber); - - /** Returns the name of a bank of GM instruments. + static const char* getGMInstrumentName (int midiInstrumentNumber); + /** Returns the name of a bank of GM instruments, or nullptr if unknown for this bank number. @param midiBankNumber the bank, 0 to 15 */ - static String getGMInstrumentBankName (int midiBankNumber); - - /** Returns the standard name of a channel 10 percussion sound. + static const char* getGMInstrumentBankName (int midiBankNumber); + /** Returns the standard name of a channel 10 percussion sound, or nullptr if unknown for this note number. @param midiNoteNumber the key number, 35 to 81 */ - static String getRhythmInstrumentName (int midiNoteNumber); - - /** Returns the name of a controller type number. + static const char* getRhythmInstrumentName (int midiNoteNumber); + /** Returns the name of a controller type number, or nullptr if unknown for this controller number. @see getControllerNumber */ - static String getControllerName (int controllerNumber); + static const char* getControllerName (int controllerNumber); private: //============================================================================== double timeStamp; - uint8* data; + HeapBlock allocatedData; int size; #ifndef DOXYGEN @@ -932,9 +896,8 @@ private: } preallocatedData; #endif - void freeData() noexcept; - void setToUseInternalData() noexcept; - bool usesAllocatedData() const noexcept; + inline uint8* getData() noexcept { return allocatedData != nullptr ? allocatedData.getData() : preallocatedData.asBytes; } + uint8* allocateSpace (int); }; -#endif // __JUCE_MIDIMESSAGE_JUCEHEADER__ +#endif // JUCE_MIDIMESSAGE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index 3493fb5..94a6b9b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -29,10 +28,8 @@ MidiMessageSequence::MidiMessageSequence() MidiMessageSequence::MidiMessageSequence (const MidiMessageSequence& other) { - list.ensureStorageAllocated (other.list.size()); - - for (int i = 0; i < other.list.size(); ++i) - list.add (new MidiEventHolder (other.list.getUnchecked(i)->message)); + list.addCopiesOf (other.list); + updateMatchedPairs(); } MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& other) @@ -44,7 +41,7 @@ MidiMessageSequence& MidiMessageSequence::operator= (const MidiMessageSequence& void MidiMessageSequence::swapWith (MidiMessageSequence& other) noexcept { - list.swapWithArray (other.list); + list.swapWith (other.list); } MidiMessageSequence::~MidiMessageSequence() @@ -56,39 +53,39 @@ void MidiMessageSequence::clear() list.clear(); } -int MidiMessageSequence::getNumEvents() const +int MidiMessageSequence::getNumEvents() const noexcept { return list.size(); } -MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const +MidiMessageSequence::MidiEventHolder* MidiMessageSequence::getEventPointer (const int index) const noexcept { return list [index]; } -double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const +double MidiMessageSequence::getTimeOfMatchingKeyUp (const int index) const noexcept { - const MidiEventHolder* const meh = list [index]; + if (const MidiEventHolder* const meh = list [index]) + if (meh->noteOffObject != nullptr) + return meh->noteOffObject->message.getTimeStamp(); - if (meh != nullptr && meh->noteOffObject != nullptr) - return meh->noteOffObject->message.getTimeStamp(); - else - return 0.0; + return 0.0; } -int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const +int MidiMessageSequence::getIndexOfMatchingKeyUp (const int index) const noexcept { - const MidiEventHolder* const meh = list [index]; + if (const MidiEventHolder* const meh = list [index]) + return list.indexOf (meh->noteOffObject); - return meh != nullptr ? list.indexOf (meh->noteOffObject) : -1; + return -1; } -int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const +int MidiMessageSequence::getIndexOf (MidiEventHolder* const event) const noexcept { return list.indexOf (event); } -int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const +int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const noexcept { const int numEvents = list.size(); @@ -101,25 +98,27 @@ int MidiMessageSequence::getNextIndexAtTime (const double timeStamp) const } //============================================================================== -double MidiMessageSequence::getStartTime() const +double MidiMessageSequence::getStartTime() const noexcept { return getEventTime (0); } -double MidiMessageSequence::getEndTime() const +double MidiMessageSequence::getEndTime() const noexcept { return getEventTime (list.size() - 1); } -double MidiMessageSequence::getEventTime (const int index) const +double MidiMessageSequence::getEventTime (const int index) const noexcept { - const MidiEventHolder* const e = list [index]; - return e != nullptr ? e->message.getTimeStamp() : 0.0; + if (const MidiEventHolder* const meh = list [index]) + return meh->message.getTimeStamp(); + + return 0.0; } //============================================================================== -void MidiMessageSequence::addEvent (const MidiMessage& newMessage, - double timeAdjustment) +MidiMessageSequence::MidiEventHolder* MidiMessageSequence::addEvent (const MidiMessage& newMessage, + double timeAdjustment) { MidiEventHolder* const newOne = new MidiEventHolder (newMessage); @@ -132,6 +131,7 @@ void MidiMessageSequence::addEvent (const MidiMessage& newMessage, break; list.insert (i + 1, newOne); + return newOne; } void MidiMessageSequence::deleteEvent (const int index, @@ -178,20 +178,26 @@ void MidiMessageSequence::addSequence (const MidiMessageSequence& other, } } + sort(); +} + +//============================================================================== +void MidiMessageSequence::sort() noexcept +{ MidiMessageSequenceSorter sorter; list.sort (sorter, true); } -//============================================================================== -void MidiMessageSequence::updateMatchedPairs() +void MidiMessageSequence::updateMatchedPairs() noexcept { for (int i = 0; i < list.size(); ++i) { - const MidiMessage& m1 = list.getUnchecked(i)->message; + MidiEventHolder* const meh = list.getUnchecked(i); + const MidiMessage& m1 = meh->message; if (m1.isNoteOn()) { - list.getUnchecked(i)->noteOffObject = nullptr; + meh->noteOffObject = nullptr; const int note = m1.getNoteNumber(); const int chan = m1.getChannel(); const int len = list.size(); @@ -204,14 +210,15 @@ void MidiMessageSequence::updateMatchedPairs() { if (m.isNoteOff()) { - list.getUnchecked(i)->noteOffObject = list[j]; + meh->noteOffObject = list[j]; break; } else if (m.isNoteOn()) { - list.insert (j, new MidiEventHolder (MidiMessage::noteOff (chan, note))); - list.getUnchecked(j)->message.setTimeStamp (m.getTimeStamp()); - list.getUnchecked(i)->noteOffObject = list[j]; + MidiEventHolder* const newEvent = new MidiEventHolder (MidiMessage::noteOff (chan, note)); + list.insert (j, newEvent); + newEvent->message.setTimeStamp (m.getTimeStamp()); + meh->noteOffObject = newEvent; break; } } @@ -220,11 +227,13 @@ void MidiMessageSequence::updateMatchedPairs() } } -void MidiMessageSequence::addTimeToMessages (const double delta) +void MidiMessageSequence::addTimeToMessages (const double delta) noexcept { for (int i = list.size(); --i >= 0;) - list.getUnchecked (i)->message.setTimeStamp (list.getUnchecked (i)->message.getTimeStamp() - + delta); + { + MidiMessage& mm = list.getUnchecked(i)->message; + mm.setTimeStamp (mm.getTimeStamp() + delta); + } } //============================================================================== @@ -236,11 +245,8 @@ void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToE { const MidiMessage& mm = list.getUnchecked(i)->message; - if (mm.isForChannel (channelNumberToExtract) - || (alsoIncludeMetaEvents && mm.isMetaEvent())) - { + if (mm.isForChannel (channelNumberToExtract) || (alsoIncludeMetaEvents && mm.isMetaEvent())) destSequence.addEvent (mm); - } } } @@ -276,15 +282,14 @@ void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumbe { bool doneProg = false; bool donePitchWheel = false; - Array doneControllers; + Array doneControllers; doneControllers.ensureStorageAllocated (32); for (int i = list.size(); --i >= 0;) { const MidiMessage& mm = list.getUnchecked(i)->message; - if (mm.isForChannel (channelNumber) - && mm.getTimeStamp() <= time) + if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time) { if (mm.isProgramChange()) { @@ -316,9 +321,8 @@ void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumbe //============================================================================== -MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& message_) - : message (message_), - noteOffObject (nullptr) +MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) + : message (mm), noteOffObject (nullptr) { } diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h index 942c716..b62940c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessageSequence.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ -#define __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ - -#include "juce_MidiMessage.h" +#ifndef JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED +#define JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED //============================================================================== @@ -46,10 +43,10 @@ public: MidiMessageSequence(); /** Creates a copy of another sequence. */ - MidiMessageSequence (const MidiMessageSequence& other); + MidiMessageSequence (const MidiMessageSequence&); /** Replaces this sequence with another one. */ - MidiMessageSequence& operator= (const MidiMessageSequence& other); + MidiMessageSequence& operator= (const MidiMessageSequence&); /** Destructor. */ ~MidiMessageSequence(); @@ -69,13 +66,12 @@ public: /** Destructor. */ ~MidiEventHolder(); - /** The message itself, whose timestamp is used to specify the event's time. - */ + /** 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. + If this isn't a note-on, this pointer will be nullptr. Use the MidiMessageSequence::updateMatchedPairs() method to keep these note-offs up-to-date after events have been moved around in the sequence @@ -86,8 +82,8 @@ public: private: //============================================================================== friend class MidiMessageSequence; - MidiEventHolder (const MidiMessage& message); - JUCE_LEAK_DETECTOR (MidiEventHolder); + MidiEventHolder (const MidiMessage&); + JUCE_LEAK_DETECTOR (MidiEventHolder) }; //============================================================================== @@ -95,55 +91,47 @@ public: void clear(); /** Returns the number of events in the sequence. */ - int getNumEvents() const; + int getNumEvents() const noexcept; /** Returns a pointer to one of the events. */ - MidiEventHolder* getEventPointer (int index) const; + MidiEventHolder* getEventPointer (int index) const noexcept; /** 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; + double getTimeOfMatchingKeyUp (int index) const noexcept; /** 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; + int getIndexOfMatchingKeyUp (int index) const noexcept; /** Returns the index of an event. */ - int getIndexOf (MidiEventHolder* event) const; + int getIndexOf (MidiEventHolder* event) const noexcept; /** 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; + int getNextIndexAtTime (double timeStamp) const noexcept; //============================================================================== /** Returns the timestamp of the first event in the sequence. - @see getEndTime */ - double getStartTime() const; + double getStartTime() const noexcept; /** Returns the timestamp of the last event in the sequence. - @see getStartTime */ - double getEndTime() const; + double getEndTime() const noexcept; /** 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; + double getEventTime (int index) const noexcept; //============================================================================== /** Inserts a midi message into the sequence. @@ -158,8 +146,8 @@ public: that will be inserted @see updateMatchedPairs */ - void addEvent (const MidiMessage& newMessage, - double timeAdjustment = 0); + MidiEventHolder* addEvent (const MidiMessage& newMessage, + double timeAdjustment = 0); /** Deletes one of the events in the sequence. @@ -193,12 +181,17 @@ public: //============================================================================== /** 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 + Call this after re-ordering messages 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(); + void updateMatchedPairs() noexcept; + /** Forces a sort of the sequence. + You may need to call this if you've manually modified the timestamps of some + events such that the overall order now needs updating. + */ + void sort() noexcept; //============================================================================== /** Copies all the messages for a particular midi channel to another sequence. @@ -214,7 +207,6 @@ public: 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 @@ -222,20 +214,17 @@ public: 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. - */ + /** 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); + void addTimeToMessages (double deltaTime) noexcept; //============================================================================== /** Scans through the sequence to determine the state of any midi controllers at @@ -262,15 +251,15 @@ public: //============================================================================== /** Swaps this sequence with another one. */ - void swapWith (MidiMessageSequence& other) noexcept; + void swapWith (MidiMessageSequence&) noexcept; private: //============================================================================== friend class MidiFile; - OwnedArray list; + OwnedArray list; - JUCE_LEAK_DETECTOR (MidiMessageSequence); + JUCE_LEAK_DETECTOR (MidiMessageSequence) }; -#endif // __JUCE_MIDIMESSAGESEQUENCE_JUCEHEADER__ +#endif // JUCE_MIDIMESSAGESEQUENCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h index 3fb1fea..cfbd59f 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_AudioSource.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOSOURCE_JUCEHEADER__ -#define __JUCE_AUDIOSOURCE_JUCEHEADER__ - -#include "../buffers/juce_AudioSampleBuffer.h" +#ifndef JUCE_AUDIOSOURCE_H_INCLUDED +#define JUCE_AUDIOSOURCE_H_INCLUDED //============================================================================== @@ -35,6 +32,31 @@ */ struct JUCE_API AudioSourceChannelInfo { + /** Creates an uninitialised AudioSourceChannelInfo. */ + AudioSourceChannelInfo() noexcept + { + } + + /** Creates an AudioSourceChannelInfo. */ + AudioSourceChannelInfo (AudioSampleBuffer* bufferToUse, + int startSampleOffset, int numSamplesToUse) noexcept + : buffer (bufferToUse), + startSample (startSampleOffset), + numSamples (numSamplesToUse) + { + } + + /** Creates an AudioSourceChannelInfo that uses the whole of a buffer. + Note that the buffer provided must not be deleted while the + AudioSourceChannelInfo is still using it. + */ + explicit AudioSourceChannelInfo (AudioSampleBuffer& bufferToUse) noexcept + : buffer (&bufferToUse), + startSample (0), + numSamples (bufferToUse.getNumSamples()) + { + } + /** The destination buffer to fill with audio data. When the AudioSource::getNextAudioBlock() method is called, the active section @@ -43,7 +65,7 @@ struct JUCE_API AudioSourceChannelInfo 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() + The contents of the buffer when it is passed to 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. @@ -156,4 +178,4 @@ public: }; -#endif // __JUCE_AUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_AUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp index 9d33d8a..21dc173 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp @@ -1,48 +1,47 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_, - TimeSliceThread& backgroundThread_, +BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s, + TimeSliceThread& thread, const bool deleteSourceWhenDeleted, - const int numberOfSamplesToBuffer_, - const int numberOfChannels_) - : source (source_, deleteSourceWhenDeleted), - backgroundThread (backgroundThread_), - numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)), - numberOfChannels (numberOfChannels_), - buffer (numberOfChannels_, 0), + const int bufferSizeSamples, + const int numChannels) + : source (s, deleteSourceWhenDeleted), + backgroundThread (thread), + numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)), + numberOfChannels (numChannels), bufferValidStart (0), bufferValidEnd (0), nextPlayPos (0), + sampleRate (0), wasSourceLooping (false), isPrepared (false) { - jassert (source_ != nullptr); + jassert (source != nullptr); - jassert (numberOfSamplesToBuffer_ > 1024); // not much point using this class if you're - // not using a larger buffer.. + jassert (numberOfSamplesToBuffer > 1024); // not much point using this class if you're + // not using a larger buffer.. } BufferingAudioSource::~BufferingAudioSource() @@ -51,20 +50,20 @@ BufferingAudioSource::~BufferingAudioSource() } //============================================================================== -void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate_) +void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate) { const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer); - if (sampleRate_ != sampleRate + if (newSampleRate != sampleRate || bufferSizeNeeded != buffer.getNumSamples() || ! isPrepared) { backgroundThread.removeTimeSliceClient (this); isPrepared = true; - sampleRate = sampleRate_; + sampleRate = newSampleRate; - source->prepareToPlay (samplesPerBlockExpected, sampleRate_); + source->prepareToPlay (samplesPerBlockExpected, newSampleRate); buffer.setSize (numberOfChannels, bufferSizeNeeded); buffer.clear(); @@ -74,7 +73,7 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sa backgroundThread.addTimeSliceClient (this); - while (bufferValidEnd - bufferValidStart < jmin (((int) sampleRate_) / 4, + while (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2)) { backgroundThread.moveToFrontOfQueue (this); @@ -146,9 +145,6 @@ void BufferingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info } nextPlayPos += info.numSamples; - - if (source->isLooping() && nextPlayPos > 0) - nextPlayPos %= source->getTotalLength(); } } @@ -212,42 +208,40 @@ bool BufferingAudioSource::readNextBufferChunk() } } - if (sectionToReadStart != sectionToReadEnd) + if (sectionToReadStart == sectionToReadEnd) + return false; + + jassert (buffer.getNumSamples() > 0); + const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples()); + const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples()); + + if (bufferIndexStart < bufferIndexEnd) { - jassert (buffer.getNumSamples() > 0); - const int bufferIndexStart = (int) (sectionToReadStart % buffer.getNumSamples()); - const int bufferIndexEnd = (int) (sectionToReadEnd % buffer.getNumSamples()); + readBufferSection (sectionToReadStart, + (int) (sectionToReadEnd - sectionToReadStart), + bufferIndexStart); + } + else + { + const int initialSize = buffer.getNumSamples() - bufferIndexStart; - if (bufferIndexStart < bufferIndexEnd) - { - readBufferSection (sectionToReadStart, - (int) (sectionToReadEnd - sectionToReadStart), - bufferIndexStart); - } - else - { - const int initialSize = buffer.getNumSamples() - bufferIndexStart; + readBufferSection (sectionToReadStart, + initialSize, + bufferIndexStart); - readBufferSection (sectionToReadStart, - initialSize, - bufferIndexStart); - - readBufferSection (sectionToReadStart + initialSize, - (int) (sectionToReadEnd - sectionToReadStart) - initialSize, - 0); - } + readBufferSection (sectionToReadStart + initialSize, + (int) (sectionToReadEnd - sectionToReadStart) - initialSize, + 0); + } + { const ScopedLock sl2 (bufferStartPosLock); bufferValidStart = newBVS; bufferValidEnd = newBVE; + } - return true; - } - else - { - return false; - } + return true; } void BufferingAudioSource::readBufferSection (const int64 start, const int length, const int bufferOffset) @@ -255,11 +249,7 @@ void BufferingAudioSource::readBufferSection (const int64 start, const int lengt if (source->getNextReadPosition() != start) source->setNextReadPosition (start); - AudioSourceChannelInfo info; - info.buffer = &buffer; - info.startSample = bufferOffset; - info.numSamples = length; - + AudioSourceChannelInfo info (&buffer, bufferOffset, length); source->getNextAudioBlock (info); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h index 834cb0d..b0ab4d2 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_BufferingAudioSource.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ - -#include "juce_PositionableAudioSource.h" +#ifndef JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED +#define JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -71,26 +68,26 @@ public: //============================================================================== /** Implementation of the AudioSource method. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ - void releaseResources(); + void releaseResources() override; /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void getNextAudioBlock (const AudioSourceChannelInfo&) override; //============================================================================== /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int64 newPosition); + void setNextReadPosition (int64 newPosition) override; /** Implements the PositionableAudioSource method. */ - int64 getNextReadPosition() const; + int64 getNextReadPosition() const override; /** Implements the PositionableAudioSource method. */ - int64 getTotalLength() const { return source->getTotalLength(); } + int64 getTotalLength() const override { return source->getTotalLength(); } /** Implements the PositionableAudioSource method. */ - bool isLooping() const { return source->isLooping(); } + bool isLooping() const override { return source->isLooping(); } private: //============================================================================== @@ -103,13 +100,12 @@ private: double volatile sampleRate; bool wasSourceLooping, isPrepared; - friend class SharedBufferingAudioSourceThread; bool readNextBufferChunk(); void readBufferSection (int64 start, int length, int bufferOffset); - int useTimeSlice(); + int useTimeSlice() override; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioSource) }; -#endif // __JUCE_BUFFERINGAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_BUFFERINGAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp index 0019a8a..7e0a58c 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,8 +25,7 @@ ChannelRemappingAudioSource::ChannelRemappingAudioSource (AudioSource* const source_, const bool deleteSourceWhenDeleted) : source (source_, deleteSourceWhenDeleted), - requiredNumberOfChannels (2), - buffer (2, 16) + requiredNumberOfChannels (2) { remappedInfo.buffer = &buffer; remappedInfo.startSample = 0; @@ -109,8 +107,7 @@ void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInf const int numChans = bufferToFill.buffer->getNumChannels(); - int i; - for (i = 0; i < buffer.getNumChannels(); ++i) + for (int i = 0; i < buffer.getNumChannels(); ++i) { const int remappedChan = getRemappedInputChannel (i); @@ -133,7 +130,7 @@ void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInf bufferToFill.clearActiveBufferRegion(); - for (i = 0; i < requiredNumberOfChannels; ++i) + for (int i = 0; i < requiredNumberOfChannels; ++i) { const int remappedChan = getRemappedOutputChannel (i); @@ -150,16 +147,14 @@ void ChannelRemappingAudioSource::getNextAudioBlock (const AudioSourceChannelInf XmlElement* ChannelRemappingAudioSource::createXml() const { XmlElement* e = new XmlElement ("MAPPINGS"); - String ins, outs; - int i; const ScopedLock sl (lock); - for (i = 0; i < remappedInputs.size(); ++i) + for (int i = 0; i < remappedInputs.size(); ++i) ins << remappedInputs.getUnchecked(i) << ' '; - for (i = 0; i < remappedOutputs.size(); ++i) + for (int i = 0; i < remappedOutputs.size(); ++i) outs << remappedOutputs.getUnchecked(i) << ' '; e->setAttribute ("inputs", ins.trimEnd()); @@ -180,11 +175,10 @@ void ChannelRemappingAudioSource::restoreFromXml (const XmlElement& e) ins.addTokens (e.getStringAttribute ("inputs"), false); outs.addTokens (e.getStringAttribute ("outputs"), false); - int i; - for (i = 0; i < ins.size(); ++i) + for (int i = 0; i < ins.size(); ++i) remappedInputs.add (ins[i].getIntValue()); - for (i = 0; i < outs.size(); ++i) + for (int i = 0; i < outs.size(); ++i) remappedOutputs.add (outs[i].getIntValue()); } } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h index e94dcd7..483fe99 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ChannelRemappingAudioSource.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ - -#include "juce_AudioSource.h" +#ifndef JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED +#define JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -114,36 +111,33 @@ public: //============================================================================== /** Returns an XML object to encapsulate the state of the mappings. - @see restoreFromXml */ XmlElement* createXml() const; /** Restores the mappings from an XML object created by createXML(). - @see createXml */ - void restoreFromXml (const XmlElement& e); + void restoreFromXml (const XmlElement&); //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); - void releaseResources(); - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; + void releaseResources() override; + void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== OptionalScopedPointer source; - Array remappedInputs, remappedOutputs; + Array remappedInputs, remappedOutputs; int requiredNumberOfChannels; AudioSampleBuffer buffer; AudioSourceChannelInfo remappedInfo; - CriticalSection lock; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelRemappingAudioSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChannelRemappingAudioSource) }; -#endif // __JUCE_CHANNELREMAPPINGAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_CHANNELREMAPPINGAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp index a2bad77..08e87f0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -36,10 +35,16 @@ IIRFilterAudioSource::IIRFilterAudioSource (AudioSource* const inputSource, IIRFilterAudioSource::~IIRFilterAudioSource() {} //============================================================================== -void IIRFilterAudioSource::setFilterParameters (const IIRFilter& newSettings) +void IIRFilterAudioSource::setCoefficients (const IIRCoefficients& newCoefficients) { for (int i = iirFilters.size(); --i >= 0;) - iirFilters.getUnchecked(i)->copyCoefficientsFrom (newSettings); + iirFilters.getUnchecked(i)->setCoefficients (newCoefficients); +} + +void IIRFilterAudioSource::makeInactive() +{ + for (int i = iirFilters.size(); --i >= 0;) + iirFilters.getUnchecked(i)->makeInactive(); } //============================================================================== @@ -67,6 +72,6 @@ void IIRFilterAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& buff for (int i = 0; i < numChannels; ++i) iirFilters.getUnchecked(i) - ->processSamples (bufferToFill.buffer->getSampleData (i, bufferToFill.startSample), + ->processSamples (bufferToFill.buffer->getWritePointer (i, bufferToFill.startSample), bufferToFill.numSamples); } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h index 0e04094..40844c5 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_IIRFilterAudioSource.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ - -#include "juce_AudioSource.h" -#include "../effects/juce_IIRFilter.h" +#ifndef JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED +#define JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -52,20 +48,23 @@ public: //============================================================================== /** Changes the filter to use the same parameters as the one being passed in. */ - void setFilterParameters (const IIRFilter& newSettings); + void setCoefficients (const IIRCoefficients& newCoefficients); + + /** Calls IIRFilter::makeInactive() on all the filters being used internally. */ + void makeInactive(); //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); - void releaseResources(); - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; + void releaseResources() override; + void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== OptionalScopedPointer input; - OwnedArray iirFilters; + OwnedArray iirFilters; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IIRFilterAudioSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IIRFilterAudioSource) }; -#endif // __JUCE_IIRFILTERAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_IIRFILTERAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp index 8858554..60ae191 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.cpp @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ MixerAudioSource::MixerAudioSource() - : tempBuffer (2, 0), - currentSampleRate (0.0), - bufferSizeExpected (0) + : currentSampleRate (0.0), bufferSizeExpected (0) { } @@ -75,7 +72,7 @@ void MixerAudioSource::removeInputSource (AudioSource* const input) if (inputsToDelete [index]) toDelete = input; - inputsToDelete.shiftBits (index, 1); + inputsToDelete.shiftBits (-1, index); inputs.remove (index); } @@ -140,10 +137,7 @@ void MixerAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& info) tempBuffer.setSize (jmax (1, info.buffer->getNumChannels()), info.buffer->getNumSamples()); - AudioSourceChannelInfo info2; - info2.buffer = &tempBuffer; - info2.numSamples = info.numSamples; - info2.startSample = 0; + AudioSourceChannelInfo info2 (&tempBuffer, 0, info.numSamples); for (int i = 1; i < inputs.size(); ++i) { diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h index cafdf01..f581ac3 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_MixerAudioSource.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ - -#include "juce_AudioSource.h" +#ifndef JUCE_MIXERAUDIOSOURCE_H_INCLUDED +#define JUCE_MIXERAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -77,28 +74,28 @@ public: /** Implementation of the AudioSource method. This will call prepareToPlay() on all its input sources. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. This will call releaseResources() on all its input sources. */ - void releaseResources(); + void releaseResources() override; /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== - Array inputs; + Array inputs; BigInteger inputsToDelete; CriticalSection lock; AudioSampleBuffer tempBuffer; double currentSampleRate; int bufferSizeExpected; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MixerAudioSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MixerAudioSource) }; -#endif // __JUCE_MIXERAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_MIXERAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h index 2ffe737..2213722 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_PositionableAudioSource.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ - -#include "juce_AudioSource.h" +#ifndef JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED +#define JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -78,4 +75,4 @@ public: }; -#endif // __JUCE_POSITIONABLEAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_POSITIONABLEAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp index 65b5d44..52fc730 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -29,11 +28,13 @@ ResamplingAudioSource::ResamplingAudioSource (AudioSource* const inputSource, : input (inputSource, deleteInputWhenDeleted), ratio (1.0), lastRatio (1.0), - buffer (numChannels_, 0), + bufferPos (0), sampsInBuffer (0), + subSampleOffset (0), numChannels (numChannels_) { jassert (input != nullptr); + zeromem (coefficients, sizeof (coefficients)); } ResamplingAudioSource::~ResamplingAudioSource() {} @@ -110,11 +111,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf int numToDo = jmin (sampsNeeded - sampsInBuffer, bufferSize - endOfBufferPos); - AudioSourceChannelInfo readInfo; - readInfo.buffer = &buffer; - readInfo.numSamples = numToDo; - readInfo.startSample = endOfBufferPos; - + AudioSourceChannelInfo readInfo (&buffer, endOfBufferPos, numToDo); input->getNextAudioBlock (readInfo); if (localRatio > 1.0001) @@ -122,7 +119,7 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf // for down-sampling, pre-apply the filter.. for (int i = channelsToProcess; --i >= 0;) - applyFilter (buffer.getSampleData (i, endOfBufferPos), numToDo, filterStates[i]); + applyFilter (buffer.getWritePointer (i, endOfBufferPos), numToDo, filterStates[i]); } sampsInBuffer += numToDo; @@ -131,18 +128,18 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf for (int channel = 0; channel < channelsToProcess; ++channel) { - destBuffers[channel] = info.buffer->getSampleData (channel, info.startSample); - srcBuffers[channel] = buffer.getSampleData (channel, 0); + destBuffers[channel] = info.buffer->getWritePointer (channel, info.startSample); + srcBuffers[channel] = buffer.getReadPointer (channel); } int nextPos = (bufferPos + 1) % bufferSize; for (int m = info.numSamples; --m >= 0;) { const float alpha = (float) subSampleOffset; - const float invAlpha = 1.0f - alpha; for (int channel = 0; channel < channelsToProcess; ++channel) - *destBuffers[channel]++ = srcBuffers[channel][bufferPos] * invAlpha + srcBuffers[channel][nextPos] * alpha; + *destBuffers[channel]++ = srcBuffers[channel][bufferPos] + + alpha * (srcBuffers[channel][nextPos] - srcBuffers[channel][bufferPos]); subSampleOffset += localRatio; @@ -164,14 +161,14 @@ void ResamplingAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& inf { // for up-sampling, apply the filter after transposing.. for (int i = channelsToProcess; --i >= 0;) - applyFilter (info.buffer->getSampleData (i, info.startSample), info.numSamples, filterStates[i]); + applyFilter (info.buffer->getWritePointer (i, info.startSample), info.numSamples, filterStates[i]); } else if (localRatio <= 1.0001 && info.numSamples > 0) { // if the filter's not currently being applied, keep it stoked with the last couple of samples to avoid discontinuities for (int i = channelsToProcess; --i >= 0;) { - const float* const endOfBuffer = info.buffer->getSampleData (i, info.startSample + info.numSamples - 1); + const float* const endOfBuffer = info.buffer->getReadPointer (i, info.startSample + info.numSamples - 1); FilterState& fs = filterStates[i]; if (info.numSamples > 1) diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h index 5ecea84..20886e6 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ResamplingAudioSource.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ - -#include "juce_AudioSource.h" +#ifndef JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED +#define JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -70,9 +67,9 @@ public: double getResamplingRatio() const noexcept { return ratio; } //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); - void releaseResources(); - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; + void releaseResources() override; + void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== @@ -84,7 +81,8 @@ private: double coefficients[6]; SpinLock ratioLock; const int numChannels; - HeapBlock destBuffers, srcBuffers; + HeapBlock destBuffers; + HeapBlock srcBuffers; void setFilterCoefficients (double c1, double c2, double c3, double c4, double c5, double c6); void createLowPass (double proportionalRate); @@ -99,8 +97,8 @@ private: void applyFilter (float* samples, int num, FilterState& fs); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResamplingAudioSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResamplingAudioSource) }; -#endif // __JUCE_RESAMPLINGAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_RESAMPLINGAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp index 55c16bc..d630075 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -49,12 +48,12 @@ void ReverbAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& bufferT if (! bypass) { - float* const firstChannel = bufferToFill.buffer->getSampleData (0, bufferToFill.startSample); + float* const firstChannel = bufferToFill.buffer->getWritePointer (0, bufferToFill.startSample); if (bufferToFill.buffer->getNumChannels() > 1) { reverb.processStereo (firstChannel, - bufferToFill.buffer->getSampleData (1, bufferToFill.startSample), + bufferToFill.buffer->getWritePointer (1, bufferToFill.startSample), bufferToFill.numSamples); } else diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h index b7765ff..6b90e18 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_REVERBAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_REVERBAUDIOSOURCE_JUCEHEADER__ - -#include "juce_AudioSource.h" -#include "../effects/juce_Reverb.h" +#ifndef JUCE_REVERBAUDIOSOURCE_H_INCLUDED +#define JUCE_REVERBAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -62,9 +58,9 @@ public: bool isBypassed() const noexcept { return bypass; } //============================================================================== - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); - void releaseResources(); - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; + void releaseResources() override; + void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: //============================================================================== @@ -73,8 +69,8 @@ private: Reverb reverb; volatile bool bypass; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbAudioSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbAudioSource) }; -#endif // __JUCE_REVERBAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_REVERBAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp index 5629e55..a0e04ef 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -49,12 +48,11 @@ void ToneGeneratorAudioSource::setFrequency (const double newFrequencyHz) } //============================================================================== -void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, - double sampleRate_) +void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, double rate) { currentPhase = 0.0; phasePerSample = 0.0; - sampleRate = sampleRate_; + sampleRate = rate; } void ToneGeneratorAudioSource::releaseResources() @@ -72,6 +70,6 @@ void ToneGeneratorAudioSource::getNextAudioBlock (const AudioSourceChannelInfo& currentPhase += phasePerSample; for (int j = info.buffer->getNumChannels(); --j >= 0;) - *info.buffer->getSampleData (j, info.startSample + i) = sample; + info.buffer->setSample (j, info.startSample + i, sample); } } diff --git a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h index f70d81d..5d09ad8 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h +++ b/JuceLibraryCode/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ -#define __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ - -#include "juce_AudioSource.h" +#ifndef JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED +#define JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED //============================================================================== @@ -54,13 +51,13 @@ public: //============================================================================== /** Implementation of the AudioSource method. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ - void releaseResources(); + void releaseResources() override; /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void getNextAudioBlock (const AudioSourceChannelInfo&) override; private: @@ -69,8 +66,8 @@ private: double currentPhase, phasePerSample; float amplitude; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToneGeneratorAudioSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToneGeneratorAudioSource) }; -#endif // __JUCE_TONEGENERATORAUDIOSOURCE_JUCEHEADER__ +#endif // JUCE_TONEGENERATORAUDIOSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index ef23024..7a72d00 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -SynthesiserSound::SynthesiserSound() -{ -} - -SynthesiserSound::~SynthesiserSound() -{ -} +SynthesiserSound::SynthesiserSound() {} +SynthesiserSound::~SynthesiserSound() {} //============================================================================== SynthesiserVoice::SynthesiserVoice() @@ -62,6 +56,13 @@ void SynthesiserVoice::clearCurrentNote() currentlyPlayingSound = nullptr; } +void SynthesiserVoice::aftertouchChanged (int) {} + +bool SynthesiserVoice::wasStartedBefore (const SynthesiserVoice& other) const noexcept +{ + return noteOnTime < other.noteOnTime; +} + //============================================================================== Synthesiser::Synthesiser() : sampleRate (0), @@ -89,10 +90,10 @@ void Synthesiser::clearVoices() voices.clear(); } -void Synthesiser::addVoice (SynthesiserVoice* const newVoice) +SynthesiserVoice* Synthesiser::addVoice (SynthesiserVoice* const newVoice) { const ScopedLock sl (lock); - voices.add (newVoice); + return voices.add (newVoice); } void Synthesiser::removeVoice (const int index) @@ -107,10 +108,10 @@ void Synthesiser::clearSounds() sounds.clear(); } -void Synthesiser::addSound (const SynthesiserSound::Ptr& newSound) +SynthesiserSound* Synthesiser::addSound (const SynthesiserSound::Ptr& newSound) { const ScopedLock sl (lock); - sounds.add (newSound); + return sounds.add (newSound); } void Synthesiser::removeSound (const int index) @@ -119,9 +120,9 @@ void Synthesiser::removeSound (const int index) sounds.remove (index); } -void Synthesiser::setNoteStealingEnabled (const bool shouldStealNotes_) +void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) { - shouldStealNotes = shouldStealNotes_; + shouldStealNotes = shouldSteal; } //============================================================================== @@ -140,10 +141,8 @@ void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) } } -void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, - const MidiBuffer& midiData, - int startSample, - int numSamples) +void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBuffer& midiData, + int startSample, int numSamples) { // must set the sample rate before using this! jassert (sampleRate != 0); @@ -181,15 +180,11 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) { if (m.isNoteOn()) { - noteOn (m.getChannel(), - m.getNoteNumber(), - m.getFloatVelocity()); + noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity()); } else if (m.isNoteOff()) { - noteOff (m.getChannel(), - m.getNoteNumber(), - true); + noteOff (m.getChannel(), m.getNoteNumber(), true); } else if (m.isAllNotesOff() || m.isAllSoundOff()) { @@ -203,11 +198,13 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) handlePitchWheel (channel, wheelPos); } + else if (m.isAftertouch()) + { + handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue()); + } else if (m.isController()) { - handleController (m.getChannel(), - m.getControllerNumber(), - m.getControllerValue()); + handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue()); } } @@ -286,16 +283,16 @@ void Synthesiser::noteOff (const int midiChannel, if (voice->getCurrentlyPlayingNote() == midiNoteNumber) { - SynthesiserSound* const sound = voice->getCurrentlyPlayingSound(); - - if (sound != nullptr - && sound->appliesToNote (midiNoteNumber) - && sound->appliesToChannel (midiChannel)) + if (SynthesiserSound* const sound = voice->getCurrentlyPlayingSound()) { - voice->keyIsDown = false; + if (sound->appliesToNote (midiNoteNumber) + && sound->appliesToChannel (midiChannel)) + { + voice->keyIsDown = false; - if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown)) - stopVoice (voice, allowTailOff); + if (! (sustainPedalsDown [midiChannel] || voice->sostenutoPedalDown)) + stopVoice (voice, allowTailOff); + } } } } @@ -352,6 +349,20 @@ void Synthesiser::handleController (const int midiChannel, } } +void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote() == midiNoteNumber + && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))) + voice->aftertouchChanged (aftertouchValue); + } +} + void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) { jassert (midiChannel > 0 && midiChannel <= 16); @@ -401,33 +412,38 @@ void Synthesiser::handleSoftPedal (int midiChannel, bool /*isDown*/) } //============================================================================== -SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, - const bool stealIfNoneAvailable) const +SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, const bool stealIfNoneAvailable) const { const ScopedLock sl (lock); - for (int i = voices.size(); --i >= 0;) - if (voices.getUnchecked (i)->getCurrentlyPlayingNote() < 0 - && voices.getUnchecked (i)->canPlaySound (soundToPlay)) - return voices.getUnchecked (i); + for (int i = 0; i < voices.size(); ++i) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote() < 0 && voice->canPlaySound (soundToPlay)) + return voice; + } if (stealIfNoneAvailable) - { - // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. - SynthesiserVoice* oldest = nullptr; - - for (int i = voices.size(); --i >= 0;) - { - SynthesiserVoice* const voice = voices.getUnchecked (i); - - if (voice->canPlaySound (soundToPlay) - && (oldest == nullptr || oldest->noteOnTime > voice->noteOnTime)) - oldest = voice; - } - - jassert (oldest != nullptr); - return oldest; - } + return findVoiceToSteal (soundToPlay); return nullptr; } + +SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay) const +{ + // currently this just steals the one that's been playing the longest, but could be made a bit smarter.. + SynthesiserVoice* oldest = nullptr; + + for (int i = 0; i < voices.size(); ++i) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->canPlaySound (soundToPlay) + && (oldest == nullptr || voice->wasStartedBefore (*oldest))) + oldest = voice; + } + + jassert (oldest != nullptr); + return oldest; +} diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index b6c2b67..e765697 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_SYNTHESISER_JUCEHEADER__ -#define __JUCE_SYNTHESISER_JUCEHEADER__ - -#include "../buffers/juce_AudioSampleBuffer.h" -#include "../midi/juce_MidiBuffer.h" +#ifndef JUCE_SYNTHESISER_H_INCLUDED +#define JUCE_SYNTHESISER_H_INCLUDED //============================================================================== @@ -68,14 +64,13 @@ public: */ virtual bool appliesToChannel (const int midiChannel) = 0; - /** - */ - typedef ReferenceCountedObjectPtr Ptr; + /** The class is reference-counted, so this is a handy pointer class for it. */ + typedef ReferenceCountedObjectPtr Ptr; private: //============================================================================== - JUCE_LEAK_DETECTOR (SynthesiserSound); + JUCE_LEAK_DETECTOR (SynthesiserSound) }; @@ -100,16 +95,14 @@ public: //============================================================================== /** 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; } + int getCurrentlyPlayingNote() const noexcept { return currentlyPlayingNote; } /** Returns the sound that this voice is currently playing. - - Returns 0 if it's not playing. + Returns nullptr if it's not playing. */ - SynthesiserSound::Ptr getCurrentlyPlayingSound() const { return currentlyPlayingSound; } + SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept { return currentlyPlayingSound; } /** Must return true if this voice object is capable of playing the given sound. @@ -120,16 +113,15 @@ public: 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; + virtual bool canPlaySound (SynthesiserSound*) = 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, + virtual void startNote (int midiNoteNumber, + float velocity, SynthesiserSound* sound, - const int currentPitchWheelPosition) = 0; + int currentPitchWheelPosition) = 0; /** Called to stop a note. @@ -144,20 +136,22 @@ public: finishes playing (during the rendering callback), it must make sure that it calls clearCurrentNote(). */ - virtual void stopNote (const bool allowTailOff) = 0; + virtual void stopNote (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; + virtual void pitchWheelMoved (int newPitchWheelValue) = 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; + virtual void controllerMoved (int controllerNumber, int newControllerValue) = 0; + + /** Called to let the voice know that the aftertouch has changed. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void aftertouchChanged (int newAftertouchValue); //============================================================================== /** Renders the next block of data for this voice. @@ -196,6 +190,17 @@ public: */ void setCurrentPlaybackSampleRate (double newRate); + /** Returns true if the key that triggered this voice is still held down. + Note that the voice may still be playing after the key was released (e.g because the + sostenuto pedal is down). + */ + bool isKeyDown() const noexcept { return keyIsDown; } + + /** Returns true if the sostenuto pedal is currently active for this voice. */ + bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } + + /** Returns true if this voice started playing its current note before the other voice did. */ + bool wasStartedBefore (const SynthesiserVoice& other) const noexcept; protected: //============================================================================== @@ -228,10 +233,9 @@ private: int currentlyPlayingNote; uint32 noteOnTime; SynthesiserSound::Ptr currentlyPlayingSound; - bool keyIsDown; // the voice may still be playing when the key is not down (i.e. sustain pedal) - bool sostenutoPedalDown; + bool keyIsDown, sostenutoPedalDown; - JUCE_LEAK_DETECTOR (SynthesiserVoice); + JUCE_LEAK_DETECTOR (SynthesiserVoice) }; @@ -277,7 +281,7 @@ public: void clearVoices(); /** Returns the number of voices that have been added. */ - int getNumVoices() const { return voices.size(); } + int getNumVoices() const noexcept { return voices.size(); } /** Returns one of the voices that have been added. */ SynthesiserVoice* getVoice (int index) const; @@ -290,7 +294,7 @@ public: it later on when no longer needed. The caller should not retain a pointer to the voice. */ - void addVoice (SynthesiserVoice* newVoice); + SynthesiserVoice* addVoice (SynthesiserVoice* newVoice); /** Deletes one of the voices. */ void removeVoice (int index); @@ -300,17 +304,17 @@ public: void clearSounds(); /** Returns the number of sounds that have been added to the synth. */ - int getNumSounds() const { return sounds.size(); } + int getNumSounds() const noexcept { return sounds.size(); } /** Returns one of the sounds. */ - SynthesiserSound* getSound (int index) const { return sounds [index]; } + SynthesiserSound* getSound (int index) const noexcept { 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. + The object passed in is reference counted, so will be deleted when the + synthesiser and all voices are no longer using it. */ - void addSound (const SynthesiserSound::Ptr& newSound); + SynthesiserSound* addSound (const SynthesiserSound::Ptr& newSound); /** Removes and deletes one of the sounds. */ void removeSound (int index); @@ -327,7 +331,7 @@ public: /** Returns true if note-stealing is enabled. @see setNoteStealingEnabled */ - bool isNoteStealingEnabled() const { return shouldStealNotes; } + bool isNoteStealingEnabled() const noexcept { return shouldStealNotes; } //============================================================================== /** Triggers a note-on event. @@ -380,7 +384,7 @@ public: virtual void allNotesOff (int midiChannel, bool allowTailOff); - /** Sends a pitch-wheel message. + /** Sends a pitch-wheel message to any active voices. This will send a pitch-wheel message to any voices that are playing sounds on the given midi channel. @@ -394,7 +398,7 @@ public: virtual void handlePitchWheel (int midiChannel, int wheelValue); - /** Sends a midi controller message. + /** Sends a midi controller message to any active voices. This will send a midi controller message to any voices that are playing sounds on the given midi channel. @@ -410,13 +414,32 @@ public: int controllerNumber, int controllerValue); + /** Sends an aftertouch message. + + This will send an aftertouch message to any voices that are playing sounds on + the given midi channel and note number. + + 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, from 1 to 16 inclusive + @param midiNoteNumber the midi note number, 0 to 127 + @param aftertouchValue the aftertouch value, between 0 and 127, + as returned by MidiMessage::getAftertouchValue() + */ + virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); + + /** Handles a sustain pedal event. */ virtual void handleSustainPedal (int midiChannel, bool isDown); + + /** Handles a sostenuto pedal event. */ virtual void handleSostenutoPedal (int midiChannel, bool isDown); + + /** Can be overridden to handle soft pedal events. */ virtual void handleSoftPedal (int midiChannel, bool isDown); //============================================================================== - /** Tells the synthesiser what the sample rate is for the audio it's being used to - render. + /** 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. @@ -445,8 +468,8 @@ protected: /** This is used to control access to the rendering callback and the note trigger methods. */ CriticalSection lock; - OwnedArray voices; - ReferenceCountedArray sounds; + OwnedArray voices; + ReferenceCountedArray sounds; /** The last pitch-wheel values for each midi channel. */ int lastPitchWheelValues [16]; @@ -454,13 +477,19 @@ protected: /** 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. + Returns nullptr 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; + /** Chooses a voice that is most suitable for being re-used. + The default method returns the one that has been playing for the longest, but + you may want to override this and do something more cunning instead. + */ + virtual SynthesiserVoice* findVoiceToSteal (SynthesiserSound* soundToPlay) const; + /** Starts a specified voice playing a particular sound. You'll probably never need to call this, it's used internally by noteOn(), but @@ -472,6 +501,9 @@ protected: int midiNoteNumber, float velocity); + /** Can be overridden to do custom handling of incoming midi events. */ + virtual void handleMidiEvent (const MidiMessage&); + private: //============================================================================== double sampleRate; @@ -479,16 +511,15 @@ private: bool shouldStealNotes; BigInteger sustainPedalsDown; - void handleMidiEvent (const MidiMessage& m); - void stopVoice (SynthesiserVoice* voice, bool allowTailOff); + void stopVoice (SynthesiserVoice*, bool allowTailOff); #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // Note the new parameters for this method. virtual int findFreeVoice (const bool) const { return 0; } #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Synthesiser) }; -#endif // __JUCE_SYNTHESISER_JUCEHEADER__ +#endif // JUCE_SYNTHESISER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h index a887147..88b1ef2 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDBurner.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ -#define __JUCE_AUDIOCDBURNER_JUCEHEADER__ +#ifndef JUCE_AUDIOCDBURNER_H_INCLUDED +#define JUCE_AUDIOCDBURNER_H_INCLUDED #if JUCE_USE_CDBURNER || DOXYGEN @@ -159,12 +158,12 @@ private: AudioCDBurner (const int deviceIndex); class Pimpl; - friend class ScopedPointer; + friend struct ContainerDeletePolicy; ScopedPointer pimpl; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDBurner); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDBurner) }; #endif -#endif // __JUCE_AUDIOCDBURNER_JUCEHEADER__ +#endif // JUCE_AUDIOCDBURNER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp index 58c87fa..ca9c5b0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h index 92f6fc8..0e868db 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_cd/juce_AudioCDReader.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ -#define __JUCE_AUDIOCDREADER_JUCEHEADER__ +#ifndef JUCE_AUDIOCDREADER_H_INCLUDED +#define JUCE_AUDIOCDREADER_H_INCLUDED #if JUCE_USE_CDREADER || DOXYGEN @@ -68,7 +67,7 @@ public: /** Implementation of the AudioFormatReader method. */ bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples); + int64 startSampleInFile, int numSamples) override; /** Checks whether the CD has been removed from the drive. */ bool isCDStillPresent() const; @@ -168,8 +167,8 @@ private: AudioCDReader(); #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDReader) }; #endif -#endif // __JUCE_AUDIOCDREADER_JUCEHEADER__ +#endif // JUCE_AUDIOCDREADER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index c9f54ad..fad5eea 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -43,19 +42,63 @@ bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager: && useDefaultOutputChannels == other.useDefaultOutputChannels; } +//============================================================================== +class AudioDeviceManager::CallbackHandler : public AudioIODeviceCallback, + public MidiInputCallback, + public AudioIODeviceType::Listener +{ +public: + CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {} + +private: + void audioDeviceIOCallback (const float** ins, int numIns, float** outs, int numOuts, int numSamples) override + { + owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples); + } + + void audioDeviceAboutToStart (AudioIODevice* device) override + { + owner.audioDeviceAboutToStartInt (device); + } + + void audioDeviceStopped() override + { + owner.audioDeviceStoppedInt(); + } + + void audioDeviceError (const String& message) override + { + owner.audioDeviceErrorInt (message); + } + + void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override + { + owner.handleIncomingMidiMessageInt (source, message); + } + + void audioDeviceListChanged() override + { + owner.audioDeviceListChanged(); + } + + AudioDeviceManager& owner; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler) +}; + + //============================================================================== AudioDeviceManager::AudioDeviceManager() : numInputChansNeeded (0), numOutputChansNeeded (2), listNeedsScanning (true), useInputNames (false), - inputLevelMeasurementEnabledCount (0), inputLevel (0), - tempBuffer (2, 2), + testSoundPosition (0), cpuUsageMs (0), timeToCpuScale (0) { - callbackHandler.owner = this; + callbackHandler = new CallbackHandler (*this); } AudioDeviceManager::~AudioDeviceManager() @@ -70,20 +113,20 @@ void AudioDeviceManager::createDeviceTypesIfNeeded() { if (availableDeviceTypes.size() == 0) { - createAudioDeviceTypes (availableDeviceTypes); + OwnedArray types; + createAudioDeviceTypes (types); - while (lastDeviceTypeConfigs.size() < availableDeviceTypes.size()) - lastDeviceTypeConfigs.add (new AudioDeviceSetup()); + for (int i = 0; i < types.size(); ++i) + addAudioDeviceType (types.getUnchecked(i)); - if (availableDeviceTypes.size() > 0) - currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); + types.clear (false); - for (int i = 0; i < availableDeviceTypes.size(); ++i) - availableDeviceTypes.getUnchecked(i)->addListener (&callbackHandler); + if (AudioIODeviceType* first = availableDeviceTypes.getFirst()) + currentDeviceType = first->getTypeName(); } } -const OwnedArray & AudioDeviceManager::getAvailableDeviceTypes() +const OwnedArray& AudioDeviceManager::getAvailableDeviceTypes() { scanDevicesIfNeeded(); return availableDeviceTypes; @@ -91,17 +134,25 @@ const OwnedArray & AudioDeviceManager::getAvailableDeviceType void AudioDeviceManager::audioDeviceListChanged() { + if (currentAudioDevice != nullptr) + { + currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); + currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); + currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); + currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); + } + sendChangeMessage(); } //============================================================================== -static void addIfNotNull (OwnedArray & list, AudioIODeviceType* const device) +static void addIfNotNull (OwnedArray& list, AudioIODeviceType* const device) { if (device != nullptr) list.add (device); } -void AudioDeviceManager::createAudioDeviceTypes (OwnedArray & list) +void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& list) { addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); @@ -114,10 +165,22 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray & addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android()); } +void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType) +{ + if (newDeviceType != nullptr) + { + jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size()); + availableDeviceTypes.add (newDeviceType); + lastDeviceTypeConfigs.add (new AudioDeviceSetup()); + + newDeviceType->addListener (callbackHandler); + } +} + //============================================================================== String AudioDeviceManager::initialise (const int numInputChannelsNeeded, const int numOutputChannelsNeeded, - const XmlElement* const e, + const XmlElement* const xml, const bool selectDefaultDeviceOnFailure, const String& preferredDefaultDeviceName, const AudioDeviceSetup* preferredSetupOptions) @@ -127,115 +190,132 @@ String AudioDeviceManager::initialise (const int numInputChannelsNeeded, numInputChansNeeded = numInputChannelsNeeded; numOutputChansNeeded = numOutputChannelsNeeded; - if (e != nullptr && e->hasTagName ("DEVICESETUP")) + if (xml != nullptr && xml->hasTagName ("DEVICESETUP")) + return initialiseFromXML (*xml, selectDefaultDeviceOnFailure, + preferredDefaultDeviceName, preferredSetupOptions); + + return initialiseDefault (preferredDefaultDeviceName, preferredSetupOptions); +} + +String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName, + const AudioDeviceSetup* preferredSetupOptions) +{ + AudioDeviceSetup setup; + + if (preferredSetupOptions != nullptr) { - lastExplicitSettings = new XmlElement (*e); - - String error; - AudioDeviceSetup setup; - - if (preferredSetupOptions != nullptr) - setup = *preferredSetupOptions; - - if (e->getStringAttribute ("audioDeviceName").isNotEmpty()) - { - setup.inputDeviceName = setup.outputDeviceName - = e->getStringAttribute ("audioDeviceName"); - } - else - { - setup.inputDeviceName = e->getStringAttribute ("audioInputDeviceName"); - setup.outputDeviceName = e->getStringAttribute ("audioOutputDeviceName"); - } - - currentDeviceType = e->getStringAttribute ("deviceType"); - - if (findType (currentDeviceType) == nullptr) - { - AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName); - - if (type != nullptr) - currentDeviceType = type->getTypeName(); - else if (availableDeviceTypes.size() > 0) - currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); - } - - setup.bufferSize = e->getIntAttribute ("audioDeviceBufferSize"); - setup.sampleRate = e->getDoubleAttribute ("audioDeviceRate"); - - setup.inputChannels .parseString (e->getStringAttribute ("audioDeviceInChans", "11"), 2); - setup.outputChannels.parseString (e->getStringAttribute ("audioDeviceOutChans", "11"), 2); - - setup.useDefaultInputChannels = ! e->hasAttribute ("audioDeviceInChans"); - setup.useDefaultOutputChannels = ! e->hasAttribute ("audioDeviceOutChans"); - - error = setAudioDeviceSetup (setup, true); - - midiInsFromXml.clear(); - forEachXmlChildElementWithTagName (*e, c, "MIDIINPUT") - midiInsFromXml.add (c->getStringAttribute ("name")); - - const StringArray allMidiIns (MidiInput::getDevices()); - - for (int i = allMidiIns.size(); --i >= 0;) - setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); - - if (error.isNotEmpty() && selectDefaultDeviceOnFailure) - error = initialise (numInputChannelsNeeded, numOutputChannelsNeeded, 0, - false, preferredDefaultDeviceName); - - setDefaultMidiOutput (e->getStringAttribute ("defaultMidiOutput")); - - return error; + setup = *preferredSetupOptions; } - else + else if (preferredDefaultDeviceName.isNotEmpty()) { - AudioDeviceSetup setup; + for (int j = availableDeviceTypes.size(); --j >= 0;) + { + AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); - if (preferredSetupOptions != nullptr) - { - setup = *preferredSetupOptions; - } - else if (preferredDefaultDeviceName.isNotEmpty()) - { - for (int j = availableDeviceTypes.size(); --j >= 0;) + const StringArray outs (type->getDeviceNames (false)); + + for (int i = 0; i < outs.size(); ++i) { - AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); - - StringArray outs (type->getDeviceNames (false)); - - int i; - for (i = 0; i < outs.size(); ++i) + if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) { - if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.outputDeviceName = outs[i]; - break; - } + setup.outputDeviceName = outs[i]; + break; } + } - StringArray ins (type->getDeviceNames (true)); + const StringArray ins (type->getDeviceNames (true)); - for (i = 0; i < ins.size(); ++i) + for (int i = 0; i < ins.size(); ++i) + { + if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) { - if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) - { - setup.inputDeviceName = ins[i]; - break; - } + setup.inputDeviceName = ins[i]; + break; } } } - - insertDefaultDeviceNames (setup); - return setAudioDeviceSetup (setup, false); } + + insertDefaultDeviceNames (setup); + return setAudioDeviceSetup (setup, false); +} + +String AudioDeviceManager::initialiseFromXML (const XmlElement& xml, + const bool selectDefaultDeviceOnFailure, + const String& preferredDefaultDeviceName, + const AudioDeviceSetup* preferredSetupOptions) +{ + lastExplicitSettings = new XmlElement (xml); + + String error; + AudioDeviceSetup setup; + + if (preferredSetupOptions != nullptr) + setup = *preferredSetupOptions; + + if (xml.getStringAttribute ("audioDeviceName").isNotEmpty()) + { + setup.inputDeviceName = setup.outputDeviceName + = xml.getStringAttribute ("audioDeviceName"); + } + else + { + setup.inputDeviceName = xml.getStringAttribute ("audioInputDeviceName"); + setup.outputDeviceName = xml.getStringAttribute ("audioOutputDeviceName"); + } + + currentDeviceType = xml.getStringAttribute ("deviceType"); + + if (findType (currentDeviceType) == nullptr) + { + if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName)) + currentDeviceType = type->getTypeName(); + else if (availableDeviceTypes.size() > 0) + currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); + } + + setup.bufferSize = xml.getIntAttribute ("audioDeviceBufferSize"); + setup.sampleRate = xml.getDoubleAttribute ("audioDeviceRate"); + + setup.inputChannels .parseString (xml.getStringAttribute ("audioDeviceInChans", "11"), 2); + setup.outputChannels.parseString (xml.getStringAttribute ("audioDeviceOutChans", "11"), 2); + + setup.useDefaultInputChannels = ! xml.hasAttribute ("audioDeviceInChans"); + setup.useDefaultOutputChannels = ! xml.hasAttribute ("audioDeviceOutChans"); + + error = setAudioDeviceSetup (setup, true); + + midiInsFromXml.clear(); + + forEachXmlChildElementWithTagName (xml, c, "MIDIINPUT") + midiInsFromXml.add (c->getStringAttribute ("name")); + + const StringArray allMidiIns (MidiInput::getDevices()); + + for (int i = allMidiIns.size(); --i >= 0;) + setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); + + if (error.isNotEmpty() && selectDefaultDeviceOnFailure) + error = initialise (numInputChansNeeded, numOutputChansNeeded, + nullptr, false, preferredDefaultDeviceName); + + setDefaultMidiOutput (xml.getStringAttribute ("defaultMidiOutput")); + + return error; +} + +String AudioDeviceManager::initialiseWithDefaultDevices (int numInputChannelsNeeded, + int numOutputChannelsNeeded) +{ + lastExplicitSettings = nullptr; + + return initialise (numInputChannelsNeeded, numOutputChannelsNeeded, + nullptr, false, String(), nullptr); } void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const { - AudioIODeviceType* type = getCurrentDeviceTypeObject(); - if (type != nullptr) + if (AudioIODeviceType* type = getCurrentDeviceTypeObject()) { if (setup.outputDeviceName.isEmpty()) setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)]; @@ -301,8 +381,8 @@ void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup) void AudioDeviceManager::deleteCurrentDevice() { currentAudioDevice = nullptr; - currentSetup.inputDeviceName = String::empty; - currentSetup.outputDeviceName = String::empty; + currentSetup.inputDeviceName.clear(); + currentSetup.outputDeviceName.clear(); } void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, @@ -313,6 +393,13 @@ void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type && currentDeviceType != type) { + if (currentAudioDevice != nullptr) + { + closeAudioDevice(); + Thread::sleep (1500); // allow a moment for OS devices to sort themselves out, to help + // avoid things like DirectSound/ASIO clashes + } + currentDeviceType = type; AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i)); @@ -329,8 +416,8 @@ void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const { for (int i = 0; i < availableDeviceTypes.size(); ++i) - if (availableDeviceTypes[i]->getTypeName() == currentDeviceType) - return availableDeviceTypes[i]; + if (availableDeviceTypes.getUnchecked(i)->getTypeName() == currentDeviceType) + return availableDeviceTypes.getUnchecked(i); return availableDeviceTypes[0]; } @@ -341,15 +428,15 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup jassert (&newSetup != ¤tSetup); // this will have no effect if (newSetup == currentSetup && currentAudioDevice != nullptr) - return String::empty; + return String(); if (! (newSetup == currentSetup)) sendChangeMessage(); stopDevice(); - const String newInputDeviceName (numInputChansNeeded == 0 ? String::empty : newSetup.inputDeviceName); - const String newOutputDeviceName (numOutputChansNeeded == 0 ? String::empty : newSetup.outputDeviceName); + const String newInputDeviceName (numInputChansNeeded == 0 ? String() : newSetup.inputDeviceName); + const String newOutputDeviceName (numOutputChansNeeded == 0 ? String() : newSetup.outputDeviceName); String error; AudioIODeviceType* type = getCurrentDeviceTypeObject(); @@ -361,7 +448,7 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup if (treatAsChosenDevice) updateXml(); - return String::empty; + return String(); } if (currentSetup.inputDeviceName != newInputDeviceName @@ -386,7 +473,9 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup currentAudioDevice = type->createDevice (newOutputDeviceName, newInputDeviceName); if (currentAudioDevice == nullptr) - error = "Can't open the audio device!\n\nThis may be because another application is currently using the same device - if so, you should close any other applications and try again!"; + error = "Can't open the audio device!\n\n" + "This may be because another application is currently using the same device - " + "if so, you should close any other applications and try again!"; else error = currentAudioDevice->getLastError(); @@ -408,18 +497,12 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup outputChannels.setRange (0, numOutputChansNeeded, true); } - if (newInputDeviceName.isEmpty()) - inputChannels.clear(); - - if (newOutputDeviceName.isEmpty()) - outputChannels.clear(); + if (newInputDeviceName.isEmpty()) inputChannels.clear(); + if (newOutputDeviceName.isEmpty()) outputChannels.clear(); } - if (! newSetup.useDefaultInputChannels) - inputChannels = newSetup.inputChannels; - - if (! newSetup.useDefaultOutputChannels) - outputChannels = newSetup.outputChannels; + if (! newSetup.useDefaultInputChannels) inputChannels = newSetup.inputChannels; + if (! newSetup.useDefaultOutputChannels) outputChannels = newSetup.outputChannels; currentSetup = newSetup; @@ -435,11 +518,11 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup { currentDeviceType = currentAudioDevice->getTypeName(); - currentAudioDevice->start (&callbackHandler); + currentAudioDevice->start (callbackHandler); - currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); - currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); - currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); + currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); + currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); + currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); for (int i = 0; i < availableDeviceTypes.size(); ++i) @@ -461,16 +544,16 @@ double AudioDeviceManager::chooseBestSampleRate (double rate) const { jassert (currentAudioDevice != nullptr); - if (rate > 0) - for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) - if (currentAudioDevice->getSampleRate (i) == rate) - return rate; + const Array rates (currentAudioDevice->getAvailableSampleRates()); + + if (rate > 0 && rates.contains (rate)) + return rate; double lowestAbove44 = 0.0; - for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) + for (int i = rates.size(); --i >= 0;) { - const double sr = currentAudioDevice->getSampleRate (i); + const double sr = rates[i]; if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44)) lowestAbove44 = sr; @@ -479,17 +562,15 @@ double AudioDeviceManager::chooseBestSampleRate (double rate) const if (lowestAbove44 > 0.0) return lowestAbove44; - return currentAudioDevice->getSampleRate (0); + return rates[0]; } int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const { jassert (currentAudioDevice != nullptr); - if (bufferSize > 0) - for (int i = currentAudioDevice->getNumBufferSizesAvailable(); --i >= 0;) - if (currentAudioDevice->getBufferSizeSamples(i) == bufferSize) - return bufferSize; + if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize)) + return bufferSize; return currentAudioDevice->getDefaultBufferSize(); } @@ -550,10 +631,8 @@ void AudioDeviceManager::updateXml() } for (int i = 0; i < enabledMidiInputs.size(); ++i) - { - XmlElement* const m = lastExplicitSettings->createNewChildElement ("MIDIINPUT"); - m->setAttribute ("name", enabledMidiInputs[i]->getName()); - } + lastExplicitSettings->createNewChildElement ("MIDIINPUT") + ->setAttribute ("name", enabledMidiInputs[i]->getName()); if (midiInsFromXml.size() > 0) { @@ -562,13 +641,9 @@ void AudioDeviceManager::updateXml() const StringArray availableMidiDevices (MidiInput::getDevices()); for (int i = 0; i < midiInsFromXml.size(); ++i) - { if (! availableMidiDevices.contains (midiInsFromXml[i], true)) - { - XmlElement* const m = lastExplicitSettings->createNewChildElement ("MIDIINPUT"); - m->setAttribute ("name", midiInsFromXml[i]); - } - } + lastExplicitSettings->createNewChildElement ("MIDIINPUT") + ->setAttribute ("name", midiInsFromXml[i]); } if (defaultMidiOutputName.isNotEmpty()) @@ -601,7 +676,7 @@ void AudioDeviceManager::removeAudioCallback (AudioIODeviceCallback* callbackToR const ScopedLock sl (audioCallbackLock); needsDeinitialising = needsDeinitialising && callbacks.contains (callbackToRemove); - callbacks.removeValue (callbackToRemove); + callbacks.removeFirstMatchingValue (callbackToRemove); } if (needsDeinitialising) @@ -617,7 +692,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat { const ScopedLock sl (audioCallbackLock); - if (inputLevelMeasurementEnabledCount > 0 && numInputChannels > 0) + if (inputLevelMeasurementEnabledCount.get() > 0 && numInputChannels > 0) { for (int j = 0; j < numSamples; ++j) { @@ -652,7 +727,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples); - float** const tempChans = tempBuffer.getArrayOfChannels(); + float** const tempChans = tempBuffer.getArrayOfWritePointers(); for (int i = callbacks.size(); --i > 0;) { @@ -661,12 +736,10 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat for (int chan = 0; chan < numOutputChannels; ++chan) { - const float* const src = tempChans [chan]; - float* const dst = outputChannelData [chan]; - - if (src != nullptr && dst != nullptr) - for (int j = 0; j < numSamples; ++j) - dst[j] += src[j]; + if (const float* const src = tempChans [chan]) + if (float* const dst = outputChannelData [chan]) + for (int j = 0; j < numSamples; ++j) + dst[j] += src[j]; } } @@ -683,7 +756,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat if (testSound != nullptr) { const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition); - const float* const src = testSound->getSampleData (0, testSoundPosition); + const float* const src = testSound->getReadPointer (0, testSoundPosition); for (int i = 0; i < numOutputChannels; ++i) for (int j = 0; j < numSamps; ++j) @@ -728,14 +801,20 @@ void AudioDeviceManager::audioDeviceStoppedInt() callbacks.getUnchecked(i)->audioDeviceStopped(); } +void AudioDeviceManager::audioDeviceErrorInt (const String& message) +{ + const ScopedLock sl (audioCallbackLock); + for (int i = callbacks.size(); --i >= 0;) + callbacks.getUnchecked(i)->audioDeviceError (message); +} + double AudioDeviceManager::getCpuUsage() const { return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); } //============================================================================== -void AudioDeviceManager::setMidiInputEnabled (const String& name, - const bool enabled) +void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled) { if (enabled != isMidiInputEnabled (name)) { @@ -745,9 +824,7 @@ void AudioDeviceManager::setMidiInputEnabled (const String& name, if (index >= 0) { - MidiInput* const midiIn = MidiInput::openDevice (index, &callbackHandler); - - if (midiIn != nullptr) + if (MidiInput* const midiIn = MidiInput::openDevice (index, callbackHandler)) { enabledMidiInputs.add (midiIn); midiIn->start(); @@ -775,16 +852,18 @@ bool AudioDeviceManager::isMidiInputEnabled (const String& name) const return false; } -void AudioDeviceManager::addMidiInputCallback (const String& name, - MidiInputCallback* callbackToAdd) +void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd) { removeMidiInputCallback (name, callbackToAdd); if (name.isEmpty() || isMidiInputEnabled (name)) { const ScopedLock sl (midiCallbackLock); - midiCallbacks.add (callbackToAdd); - midiCallbackDevices.add (name); + + MidiCallbackInfo mc; + mc.deviceName = name; + mc.callback = callbackToAdd; + midiCallbacks.add (mc); } } @@ -792,30 +871,28 @@ void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputC { for (int i = midiCallbacks.size(); --i >= 0;) { - if (midiCallbackDevices[i] == name && midiCallbacks.getUnchecked(i) == callbackToRemove) + const MidiCallbackInfo& mc = midiCallbacks.getReference(i); + + if (mc.callback == callbackToRemove && mc.deviceName == name) { const ScopedLock sl (midiCallbackLock); midiCallbacks.remove (i); - midiCallbackDevices.remove (i); } } } -void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, - const MidiMessage& message) +void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message) { if (! message.isActiveSense()) { - const bool isDefaultSource = (source == nullptr || source == enabledMidiInputs.getFirst()); - const ScopedLock sl (midiCallbackLock); - for (int i = midiCallbackDevices.size(); --i >= 0;) + for (int i = 0; i < midiCallbacks.size(); ++i) { - const String name (midiCallbackDevices[i]); + const MidiCallbackInfo& mc = midiCallbacks.getReference(i); - if ((isDefaultSource && name.isEmpty()) || (name.isNotEmpty() && name == source->getName())) - midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message); + if (mc.deviceName.isEmpty() || mc.deviceName == source->getName()) + mc.callback->handleIncomingMidiMessage (source, message); } } } @@ -825,12 +902,11 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) { if (defaultMidiOutputName != deviceName) { - Array oldCallbacks; + Array oldCallbacks; { const ScopedLock sl (audioCallbackLock); - oldCallbacks = callbacks; - callbacks.clear(); + oldCallbacks.swapWith (callbacks); } if (currentAudioDevice != nullptr) @@ -849,7 +925,7 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) { const ScopedLock sl (audioCallbackLock); - callbacks = oldCallbacks; + oldCallbacks.swapWith (callbacks); } updateXml(); @@ -857,41 +933,11 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) } } -//============================================================================== -void AudioDeviceManager::CallbackHandler::audioDeviceIOCallback (const float** inputChannelData, - int numInputChannels, - float** outputChannelData, - int numOutputChannels, - int numSamples) -{ - owner->audioDeviceIOCallbackInt (inputChannelData, numInputChannels, outputChannelData, numOutputChannels, numSamples); -} - -void AudioDeviceManager::CallbackHandler::audioDeviceAboutToStart (AudioIODevice* device) -{ - owner->audioDeviceAboutToStartInt (device); -} - -void AudioDeviceManager::CallbackHandler::audioDeviceStopped() -{ - owner->audioDeviceStoppedInt(); -} - -void AudioDeviceManager::CallbackHandler::handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) -{ - owner->handleIncomingMidiMessageInt (source, message); -} - -void AudioDeviceManager::CallbackHandler::audioDeviceListChanged() -{ - owner->audioDeviceListChanged(); -} - //============================================================================== void AudioDeviceManager::playTestSound() { { // cunningly nested to swap, unlock and delete in that order. - ScopedPointer oldSound; + ScopedPointer oldSound; { const ScopedLock sl (audioCallbackLock); @@ -906,16 +952,15 @@ void AudioDeviceManager::playTestSound() const double sampleRate = currentAudioDevice->getCurrentSampleRate(); const int soundLength = (int) sampleRate; - AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); - float* samples = newSound->getSampleData (0); - - const double frequency = MidiMessage::getMidiNoteInHertz (80); + const double frequency = 440.0; const float amplitude = 0.5f; const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); + AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); + for (int i = 0; i < soundLength; ++i) - samples[i] = amplitude * (float) std::sin (i * phasePerSample); + newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample)); newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); @@ -927,8 +972,6 @@ void AudioDeviceManager::playTestSound() void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement) { - const ScopedLock sl (audioCallbackLock); - if (enableMeasurement) ++inputLevelMeasurementEnabledCount; else @@ -939,6 +982,6 @@ void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasureme double AudioDeviceManager::getCurrentInputLevel() const { - jassert (inputLevelMeasurementEnabledCount > 0); // you need to call enableInputLevelMeasurement() before using this! + jassert (inputLevelMeasurementEnabledCount.get() > 0); // you need to call enableInputLevelMeasurement() before using this! return inputLevel; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h index a447fe8..f1e483f 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -1,34 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ -#define __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ - -#include "juce_AudioIODeviceType.h" -#include "../midi_io/juce_MidiInput.h" -#include "../midi_io/juce_MidiOutput.h" +#ifndef JUCE_AUDIODEVICEMANAGER_H_INCLUDED +#define JUCE_AUDIODEVICEMANAGER_H_INCLUDED //============================================================================== @@ -121,7 +116,8 @@ public: /** The current sample rate. This rate is used for both the input and output devices. - A value of 0 indicates the default rate. + A value of 0 indicates that you don't care what rate is used, and the + device will choose a sensible rate for you. */ double sampleRate; @@ -165,11 +161,14 @@ public: 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 numInputChannelsNeeded the maximum number of input channels your app would like to + use (the actual number of channels opened may be less than + the number requested) + @param numOutputChannelsNeeded the maximum number of output channels your app would like to + use (the actual number of channels opened may be less than + the number requested) @param savedState either a previously-saved state that was produced - by createStateXml(), or 0 if you want the manager + by createStateXml(), or nullptr 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 @@ -191,8 +190,12 @@ public: int numOutputChannelsNeeded, const XmlElement* savedState, bool selectDefaultDeviceOnFailure, - const String& preferredDefaultDeviceName = String::empty, - const AudioDeviceSetup* preferredSetupOptions = 0); + const String& preferredDefaultDeviceName = String(), + const AudioDeviceSetup* preferredSetupOptions = nullptr); + + /** Resets everything to a default device setup, clearing any stored settings. */ + String initialiseWithDefaultDevices (int numInputChannelsNeeded, + int numOutputChannelsNeeded); /** Returns some XML representing the current state of the manager. @@ -206,10 +209,9 @@ public: //============================================================================== /** Returns the current device properties that are in use. - @see setAudioDeviceSetup */ - void getAudioDeviceSetup (AudioDeviceSetup& setup); + void getAudioDeviceSetup (AudioDeviceSetup& result); /** Changes the current device or its settings. @@ -258,9 +260,7 @@ public: 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. */ @@ -302,8 +302,8 @@ public: //============================================================================== /** Returns the average proportion of available CPU being spent inside the audio callbacks. - - Returns a value between 0 and 1.0 + @returns A value between 0 and 1.0 to indicate the approximate proportion of CPU + time spent in the callbacks. */ double getCpuUsage() const; @@ -330,16 +330,16 @@ public: 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). + The device name can be empty to indicate that it wants to receive all incoming + events from all the enabled MIDI inputs. Or it can be the name of one of the + MIDI input devices if it just wants the events from that device. (see + MidiInput::getDevices() for the list of device names). Only devices which are enabled (see the setMidiInputEnabled() method) will have their events forwarded on to listeners. @@ -347,8 +347,7 @@ public: void addMidiInputCallback (const String& midiInputDeviceName, MidiInputCallback* callback); - /** Removes a listener that was previously registered with addMidiInputCallback(). - */ + /** Removes a listener that was previously registered with addMidiInputCallback(). */ void removeMidiInputCallback (const String& midiInputDeviceName, MidiInputCallback* callback); @@ -368,23 +367,18 @@ public: void setDefaultMidiOutput (const String& deviceName); /** Returns the name of the default midi output. - @see setDefaultMidiOutput, getDefaultMidiOutput */ - String getDefaultMidiOutputName() const { return defaultMidiOutputName; } + const String& getDefaultMidiOutputName() const noexcept { 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. - + If no device has been selected, or the device can't be opened, this will return nullptr. @see getDefaultMidiOutputName */ MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; } - /** Returns a list of the types of device supported. - */ - const OwnedArray & getAvailableDeviceTypes(); + /** Returns a list of the types of device supported. */ + const OwnedArray& getAvailableDeviceTypes(); //============================================================================== /** Creates a list of available types. @@ -395,7 +389,12 @@ public: You can override this if your app needs to do something specific, like avoid using DirectSound devices, etc. */ - virtual void createAudioDeviceTypes (OwnedArray & types); + virtual void createAudioDeviceTypes (OwnedArray& types); + + /** Adds a new device type to the list of types. + The manager will take ownership of the object that is passed-in. + */ + void addAudioDeviceType (AudioIODeviceType* newDeviceType); //============================================================================== /** Plays a beep through the current audio device. @@ -421,9 +420,7 @@ public: 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; @@ -442,56 +439,51 @@ public: private: //============================================================================== - OwnedArray availableDeviceTypes; - OwnedArray lastDeviceTypeConfigs; + OwnedArray availableDeviceTypes; + OwnedArray lastDeviceTypeConfigs; AudioDeviceSetup currentSetup; - ScopedPointer currentAudioDevice; - Array callbacks; + ScopedPointer currentAudioDevice; + Array callbacks; int numInputChansNeeded, numOutputChansNeeded; String currentDeviceType; BigInteger inputChannels, outputChannels; - ScopedPointer lastExplicitSettings; + ScopedPointer lastExplicitSettings; mutable bool listNeedsScanning; bool useInputNames; - int inputLevelMeasurementEnabledCount; + Atomic inputLevelMeasurementEnabledCount; double inputLevel; - ScopedPointer testSound; + ScopedPointer testSound; int testSoundPosition; AudioSampleBuffer tempBuffer; + struct MidiCallbackInfo + { + String deviceName; + MidiInputCallback* callback; + }; + StringArray midiInsFromXml; - OwnedArray enabledMidiInputs; - Array midiCallbacks; - StringArray midiCallbackDevices; + OwnedArray enabledMidiInputs; + Array midiCallbacks; + String defaultMidiOutputName; - ScopedPointer defaultMidiOutput; + ScopedPointer defaultMidiOutput; CriticalSection audioCallbackLock, midiCallbackLock; double cpuUsageMs, timeToCpuScale; //============================================================================== - class CallbackHandler : public AudioIODeviceCallback, - public MidiInputCallback, - public AudioIODeviceType::Listener - { - public: - void audioDeviceIOCallback (const float**, int, float**, int, int); - void audioDeviceAboutToStart (AudioIODevice*); - void audioDeviceStopped(); - void handleIncomingMidiMessage (MidiInput*, const MidiMessage&); - void audioDeviceListChanged(); - - AudioDeviceManager* owner; - }; - - CallbackHandler callbackHandler; + class CallbackHandler; friend class CallbackHandler; + friend struct ContainerDeletePolicy; + ScopedPointer callbackHandler; void audioDeviceIOCallbackInt (const float** inputChannelData, int totalNumInputChannels, float** outputChannelData, int totalNumOutputChannels, int numSamples); void audioDeviceAboutToStartInt (AudioIODevice*); void audioDeviceStoppedInt(); + void audioDeviceErrorInt (const String&); void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); void audioDeviceListChanged(); @@ -507,11 +499,14 @@ private: double chooseBestSampleRate (double preferred) const; int chooseBestBufferSize (int preferred) const; void insertDefaultDeviceNames (AudioDeviceSetup&) const; + String initialiseDefault (const String& preferredDefaultDeviceName, const AudioDeviceSetup*); + String initialiseFromXML (const XmlElement&, bool selectDefaultDeviceOnFailure, + const String& preferredDefaultDeviceName, const AudioDeviceSetup*); AudioIODeviceType* findType (const String& inputName, const String& outputName); AudioIODeviceType* findType (const String& typeName); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager) }; -#endif // __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ +#endif // JUCE_AUDIODEVICEMANAGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp index 93dbf80..95221ba 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.cpp @@ -1,42 +1,37 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioIODevice::AudioIODevice (const String& deviceName, const String& typeName_) - : name (deviceName), - typeName (typeName_) +AudioIODevice::AudioIODevice (const String& deviceName, const String& deviceTypeName) + : name (deviceName), typeName (deviceTypeName) { } -AudioIODevice::~AudioIODevice() -{ -} +AudioIODevice::~AudioIODevice() {} -bool AudioIODevice::hasControlPanel() const -{ - return false; -} +void AudioIODeviceCallback::audioDeviceError (const String&) {} +bool AudioIODevice::setAudioPreprocessingEnabled (bool) { return false; } +bool AudioIODevice::hasControlPanel() const { return false; } bool AudioIODevice::showControlPanel() { @@ -44,6 +39,3 @@ bool AudioIODevice::showControlPanel() // their hasControlPanel() method. return false; } - -//============================================================================== -void AudioIODeviceCallback::audioDeviceError (const String&) {} diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h index 7e52852..8ce41d0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOIODEVICE_JUCEHEADER__ -#define __JUCE_AUDIOIODEVICE_JUCEHEADER__ +#ifndef JUCE_AUDIOIODEVICE_H_INCLUDED +#define JUCE_AUDIOIODEVICE_H_INCLUDED class AudioIODevice; @@ -158,47 +157,19 @@ public: virtual 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 + /** Returns the set of sample-rates this device supports. + @see getCurrentSampleRate */ - virtual int getNumSampleRates() = 0; + virtual Array getAvailableSampleRates() = 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 + /** Returns the set of buffer sizes that are available. + @see getCurrentBufferSizeSamples, getDefaultBufferSize */ - 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; + virtual Array getAvailableBufferSizes() = 0; /** Returns the default buffer-size to use. - @returns a number of samples - @see getNumBufferSizesAvailable, getBufferSizeSamples + @see getAvailableBufferSizes */ virtual int getDefaultBufferSize() = 0; @@ -210,9 +181,9 @@ public: @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() + available, see getAvailableSampleRates() @param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer - sizes, see getNumBufferSizesAvailable() and getBufferSizeSamples() + sizes, see getAvailableBufferSizes() @returns an error description if there's a problem, or an empty string if it succeeds in opening the device @see close @@ -318,6 +289,11 @@ public: */ virtual bool showControlPanel(); + /** On devices which support it, this allows automatic gain control or other + mic processing to be disabled. + If the device doesn't support this operation, it'll return false. + */ + virtual bool setAudioPreprocessingEnabled (bool shouldBeEnabled); //============================================================================== protected: @@ -330,4 +306,4 @@ protected: }; -#endif // __JUCE_AUDIOIODEVICE_JUCEHEADER__ +#endif // JUCE_AUDIOIODEVICE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp index a32f080..80026e7 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h index 8929fa9..d6cd99a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ -#define __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ - -#include "juce_AudioIODevice.h" -class AudioDeviceManager; +#ifndef JUCE_AUDIOIODEVICETYPE_H_INCLUDED +#define JUCE_AUDIOIODEVICETYPE_H_INCLUDED //============================================================================== @@ -38,7 +34,7 @@ class AudioDeviceManager; method. Each of the objects returned can then be used to list the available devices of that type. E.g. @code - OwnedArray types; + OwnedArray types; myAudioDeviceManager.createAudioDeviceTypes (types); for (int i = 0; i < types.size(); ++i) @@ -122,7 +118,7 @@ public: /** A class for receiving events when audio devices are inserted or removed. - You can register a AudioIODeviceType::Listener with an~AudioIODeviceType object + You can register an AudioIODeviceType::Listener with an~AudioIODeviceType object using the AudioIODeviceType::addListener() method, and it will be called when devices of that type are added or removed. @@ -179,8 +175,8 @@ private: String typeName; ListenerList listeners; - JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType) }; -#endif // __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ +#endif // JUCE_AUDIOIODEVICETYPE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h new file mode 100644 index 0000000..689e1f2 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_SystemAudioVolume.h @@ -0,0 +1,61 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED +#define JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED + + +//============================================================================== +/** + Contains functions to control the system's master volume. +*/ +class JUCE_API SystemAudioVolume +{ +public: + //============================================================================== + /** Returns the operating system's current volume level in the range 0 to 1.0 */ + static float JUCE_CALLTYPE getGain(); + + /** Attempts to set the operating system's current volume level. + @param newGain the level, between 0 and 1.0 + @returns true if the operation succeeds + */ + static bool JUCE_CALLTYPE setGain (float newGain); + + /** Returns true if the system's audio output is currently muted. */ + static bool JUCE_CALLTYPE isMuted(); + + /** Attempts to mute the operating system's audio output. + @param shouldBeMuted true if you want it to be muted + @returns true if the operation succeeds + */ + static bool JUCE_CALLTYPE setMuted (bool shouldBeMuted); + +private: + SystemAudioVolume(); // Don't instantiate this class, just call its static fns. + JUCE_DECLARE_NON_COPYABLE (SystemAudioVolume) +}; + + +#endif // JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp index d379789..a9733db 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_AUDIO_DEVICES_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_AUDIO_DEVICES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -46,6 +45,7 @@ #import #import #import + #import #undef Point #undef Component @@ -57,15 +57,7 @@ //============================================================================== #elif JUCE_WINDOWS #if JUCE_WASAPI - #pragma warning (push) - #pragma warning (disable: 4201) #include - #include - #include - #include - #include - #include - #pragma warning (pop) #endif #if JUCE_ASIO @@ -133,6 +125,7 @@ #if JUCE_USE_ANDROID_OPENSLES #include #include + #include #endif #endif @@ -140,7 +133,6 @@ namespace juce { -// START_AUTOINCLUDE audio_io/*.cpp, midi_io/*.cpp, audio_cd/*.cpp, sources/*.cpp #include "audio_io/juce_AudioDeviceManager.cpp" #include "audio_io/juce_AudioIODevice.cpp" #include "audio_io/juce_AudioIODeviceType.cpp" @@ -149,21 +141,11 @@ namespace juce #include "audio_cd/juce_AudioCDReader.cpp" #include "sources/juce_AudioSourcePlayer.cpp" #include "sources/juce_AudioTransportSource.cpp" -// END_AUTOINCLUDE - -} - -//============================================================================== -using namespace juce; - -namespace juce -{ - #include "native/juce_MidiDataConcatenator.h" +#include "native/juce_MidiDataConcatenator.h" //============================================================================== #if JUCE_MAC #include "../juce_core/native/juce_osx_ObjCHelpers.h" - #include "../juce_core/native/juce_mac_ObjCSuffix.h" #include "native/juce_mac_CoreAudio.cpp" #include "native/juce_mac_CoreMidi.cpp" @@ -235,4 +217,11 @@ namespace juce #endif +#if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED + // None of these methods are available. (On Windows you might need to enable WASAPI for this) + float JUCE_CALLTYPE SystemAudioVolume::getGain() { jassertfalse; return 0.0f; } + bool JUCE_CALLTYPE SystemAudioVolume::setGain (float) { jassertfalse; return false; } + bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { jassertfalse; return false; } + bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; } +#endif } diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h index 75559c8..b8528d5 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIO_DEVICES_JUCEHEADER__ -#define __JUCE_AUDIO_DEVICES_JUCEHEADER__ +#ifndef JUCE_AUDIO_DEVICES_H_INCLUDED +#define JUCE_AUDIO_DEVICES_H_INCLUDED #include "../juce_events/juce_events.h" #include "../juce_audio_basics/juce_audio_basics.h" @@ -47,7 +46,7 @@ Enables WASAPI audio devices (Windows Vista and above). */ #ifndef JUCE_WASAPI - #define JUCE_WASAPI 0 + #define JUCE_WASAPI 1 #endif /** Config: JUCE_DIRECTSOUND @@ -101,39 +100,18 @@ namespace juce { -// START_AUTOINCLUDE audio_io, midi_io, sources, audio_cd -#ifndef __JUCE_AUDIODEVICEMANAGER_JUCEHEADER__ - #include "audio_io/juce_AudioDeviceManager.h" -#endif -#ifndef __JUCE_AUDIOIODEVICE_JUCEHEADER__ - #include "audio_io/juce_AudioIODevice.h" -#endif -#ifndef __JUCE_AUDIOIODEVICETYPE_JUCEHEADER__ - #include "audio_io/juce_AudioIODeviceType.h" -#endif -#ifndef __JUCE_MIDIINPUT_JUCEHEADER__ - #include "midi_io/juce_MidiInput.h" -#endif -#ifndef __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ - #include "midi_io/juce_MidiMessageCollector.h" -#endif -#ifndef __JUCE_MIDIOUTPUT_JUCEHEADER__ - #include "midi_io/juce_MidiOutput.h" -#endif -#ifndef __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ - #include "sources/juce_AudioSourcePlayer.h" -#endif -#ifndef __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ - #include "sources/juce_AudioTransportSource.h" -#endif -#ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ - #include "audio_cd/juce_AudioCDBurner.h" -#endif -#ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ - #include "audio_cd/juce_AudioCDReader.h" -#endif -// END_AUTOINCLUDE +#include "audio_io/juce_AudioIODevice.h" +#include "audio_io/juce_AudioIODeviceType.h" +#include "audio_io/juce_SystemAudioVolume.h" +#include "midi_io/juce_MidiInput.h" +#include "midi_io/juce_MidiMessageCollector.h" +#include "midi_io/juce_MidiOutput.h" +#include "sources/juce_AudioSourcePlayer.h" +#include "sources/juce_AudioTransportSource.h" +#include "audio_cd/juce_AudioCDBurner.h" +#include "audio_cd/juce_AudioCDReader.h" +#include "audio_io/juce_AudioDeviceManager.h" } -#endif // __JUCE_AUDIO_DEVICES_JUCEHEADER__ +#endif // JUCE_AUDIO_DEVICES_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm index ea13a2f..a135119 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_audio_devices.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_module_info b/JuceLibraryCode/modules/juce_audio_devices/juce_module_info index 0fb6846..c1f01b6 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_devices", "name": "JUCE audio and midi I/O device classes", - "version": "2.0.21", + "version": "3.0.6", "description": "Classes to play and record from audio and midi i/o devices.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", @@ -22,5 +22,7 @@ "native/*" ], "OSXFrameworks": "CoreAudio CoreMIDI DiscRecording", - "iOSFrameworks": "AudioToolbox CoreMIDI" + "iOSFrameworks": "AudioToolbox CoreMIDI", + "LinuxLibs": "asound", + "mingwLibs": "winmm" } diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h index 2a3c44b..6308020 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiInput.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIINPUT_JUCEHEADER__ -#define __JUCE_MIDIINPUT_JUCEHEADER__ +#ifndef JUCE_MIDIINPUT_H_INCLUDED +#define JUCE_MIDIINPUT_H_INCLUDED class MidiInput; @@ -72,8 +71,8 @@ public: */ virtual void handlePartialSysexMessage (MidiInput* source, const uint8* messageData, - const int numBytesSoFar, - const double timestamp) + int numBytesSoFar, + double timestamp) { // (this bit is just to avoid compiler warnings about unused variables) (void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp; @@ -129,7 +128,7 @@ public: 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. + Returns nullptr 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. @@ -176,8 +175,8 @@ protected: private: //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) }; -#endif // __JUCE_MIDIINPUT_JUCEHEADER__ +#endif // JUCE_MIDIINPUT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp index 4bcc897..28e81c0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -71,6 +70,7 @@ void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, { // you need to call reset() to set the correct sample rate before using this object jassert (sampleRate != 44100.0001); + jassert (numSamples > 0); const double timeNow = Time::getMillisecondCounterHiRes(); const double msElapsed = timeNow - lastCallbackTime; diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h index e073101..5b16737 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiMessageCollector.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ -#define __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ - -#include "juce_MidiInput.h" +#ifndef JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED +#define JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED //============================================================================== @@ -79,17 +76,19 @@ public: This method is fully thread-safe when overlapping calls are made with addMessageToQueue(). + + Precondition: numSamples must be greater than 0. */ void removeNextBlockOfMessages (MidiBuffer& destBuffer, int numSamples); //============================================================================== /** @internal */ - void handleNoteOn (MidiKeyboardState* source, int midiChannel, int midiNoteNumber, float velocity); + void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override; /** @internal */ - void handleNoteOff (MidiKeyboardState* source, int midiChannel, int midiNoteNumber); + void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) override; /** @internal */ - void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message); + void handleIncomingMidiMessage (MidiInput*, const MidiMessage&) override; private: //============================================================================== @@ -98,8 +97,8 @@ private: MidiBuffer incomingMessages; double sampleRate; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiMessageCollector); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiMessageCollector) }; -#endif // __JUCE_MIDIMESSAGECOLLECTOR_JUCEHEADER__ +#endif // JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp index 1bebd78..0af7d1b 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h index d05883c..c668f8a 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h +++ b/JuceLibraryCode/modules/juce_audio_devices/midi_io/juce_MidiOutput.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIOUTPUT_JUCEHEADER__ -#define __JUCE_MIDIOUTPUT_JUCEHEADER__ +#ifndef JUCE_MIDIOUTPUT_H_INCLUDED +#define JUCE_MIDIOUTPUT_H_INCLUDED //============================================================================== @@ -74,7 +73,7 @@ public: 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. + Returns nullptr if a device can't be created. @param deviceName the name to use for the new device */ @@ -138,11 +137,11 @@ protected: PendingMessage* firstMessage; MidiOutput(); - void run(); + void run() override; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput) }; -#endif // __JUCE_MIDIOUTPUT_JUCEHEADER__ +#endif // JUCE_MIDIOUTPUT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h index 0ff8060..4dc70b9 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ -#define __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ +#ifndef JUCE_MIDIDATACONCATENATOR_H_INCLUDED +#define JUCE_MIDIDATACONCATENATOR_H_INCLUDED //============================================================================== /** @@ -37,45 +36,78 @@ public: //============================================================================== MidiDataConcatenator (const int initialBufferSize) : pendingData ((size_t) initialBufferSize), - pendingBytes (0), pendingDataTime (0) + pendingDataTime (0), pendingBytes (0), runningStatus (0) { } void reset() { pendingBytes = 0; + runningStatus = 0; pendingDataTime = 0; } - void pushMidiData (const void* data, int numBytes, double time, - MidiInput* input, MidiInputCallback& callback) + template + void pushMidiData (const void* inputData, int numBytes, double time, + UserDataType* input, CallbackType& callback) { - const uint8* d = static_cast (data); + const uint8* d = static_cast (inputData); while (numBytes > 0) { if (pendingBytes > 0 || d[0] == 0xf0) { processSysex (d, numBytes, time, input, callback); + runningStatus = 0; } else { - int used = 0; - const MidiMessage m (d, numBytes, used, 0, time); + int len = 0; + uint8 data[3]; - if (used <= 0) - break; // malformed message.. + while (numBytes > 0) + { + // If there's a realtime message embedded in the middle of + // the normal message, handle it now.. + if (*d >= 0xf8 && *d <= 0xfe) + { + const MidiMessage m (*d++, time); + callback.handleIncomingMidiMessage (input, m); + --numBytes; + } + else + { + if (len == 0 && *d < 0x80 && runningStatus >= 0x80) + data[len++] = runningStatus; - callback.handleIncomingMidiMessage (input, m); - numBytes -= used; - d += used; + data[len++] = *d++; + --numBytes; + + if (len >= MidiMessage::getMessageLengthFromFirstByte (data[0])) + break; + } + } + + if (len > 0) + { + int used = 0; + const MidiMessage m (data, len, used, 0, time); + + if (used <= 0) + break; // malformed message.. + + jassert (used == len); + callback.handleIncomingMidiMessage (input, m); + runningStatus = data[0]; + } } } } private: + template void processSysex (const uint8*& d, int& numBytes, double time, - MidiInput* input, MidiInputCallback& callback) + UserDataType* input, CallbackType& callback) { if (*d == 0xf0) { @@ -91,6 +123,14 @@ private: { if (pendingBytes > 0 && *d >= 0x80) { + if (*d == 0xf7) + { + *dest++ = *d++; + ++pendingBytes; + --numBytes; + break; + } + if (*d >= 0xfa || *d == 0xf8) { callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); @@ -99,11 +139,15 @@ private: } else { - if (*d == 0xf7) + pendingBytes = 0; + int used = 0; + const MidiMessage m (d, numBytes, used, 0, time); + + if (used > 0) { - *dest++ = *d++; - pendingBytes++; - --numBytes; + callback.handleIncomingMidiMessage (input, m); + numBytes -= used; + d += used; } break; @@ -112,7 +156,7 @@ private: else { *dest++ = *d++; - pendingBytes++; + ++pendingBytes; --numBytes; } } @@ -133,10 +177,11 @@ private: } MemoryBlock pendingData; - int pendingBytes; double pendingDataTime; + int pendingBytes; + uint8 runningStatus; - JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator); + JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator) }; -#endif // __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ +#endif // JUCE_MIDIDATACONCATENATOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp index c4c6ab5..80711da 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -78,8 +77,8 @@ public: numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2), numClientOutputChannels (0), numDeviceOutputChannels (0), actualBufferSize (0), isRunning (false), - outputChannelBuffer (1, 1), - inputChannelBuffer (1, 1) + inputChannelBuffer (1, 1), + outputChannelBuffer (1, 1) { JNIEnv* env = getEnv(); sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM); @@ -106,7 +105,7 @@ public: close(); } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); @@ -114,7 +113,7 @@ public: return s; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray s; @@ -131,36 +130,43 @@ public: return s; } - int getNumSampleRates() { return 1;} - double getSampleRate (int index) { return sampleRate; } - - int getDefaultBufferSize() { return 2048; } - int getNumBufferSizesAvailable() { return 50; } - - int getBufferSizeSamples (int index) + Array getAvailableSampleRates() override { + Array r; + r.add ((double) sampleRate); + return r; + } + + Array getAvailableBufferSizes() override + { + Array b; int n = 16; - for (int i = 0; i < index; ++i) + + for (int i = 0; i < 50; ++i) + { + b.add (n); n += n < 64 ? 16 : (n < 512 ? 32 : (n < 1024 ? 64 : (n < 2048 ? 128 : 256))); + } - return n; + return b; } + int getDefaultBufferSize() override { return 2048; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double requestedSampleRate, - int bufferSize) + int bufferSize) override { close(); if (sampleRate != (int) requestedSampleRate) return "Sample rate not allowed"; - lastError = String::empty; + lastError.clear(); int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; numDeviceInputChannels = 0; @@ -228,7 +234,7 @@ public: return lastError; } - void close() + void close() override { if (isRunning) { @@ -238,18 +244,18 @@ public: } } - int getOutputLatencyInSamples() { return (minBufferSizeOut * 3) / 4; } - int getInputLatencyInSamples() { return (minBufferSizeIn * 3) / 4; } - bool isOpen() { return isRunning; } - int getCurrentBufferSizeSamples() { return actualBufferSize; } - int getCurrentBitDepth() { return 16; } - double getCurrentSampleRate() { return sampleRate; } - BigInteger getActiveOutputChannels() const { return activeOutputChans; } - BigInteger getActiveInputChannels() const { return activeInputChans; } - String getLastError() { return lastError; } - bool isPlaying() { return isRunning && callback != 0; } + int getOutputLatencyInSamples() override { return (minBufferSizeOut * 3) / 4; } + int getInputLatencyInSamples() override { return (minBufferSizeIn * 3) / 4; } + bool isOpen() override { return isRunning; } + int getCurrentBufferSizeSamples() override { return actualBufferSize; } + int getCurrentBitDepth() override { return 16; } + double getCurrentSampleRate() override { return sampleRate; } + BigInteger getActiveOutputChannels() const override { return activeOutputChans; } + BigInteger getActiveInputChannels() const override { return activeInputChans; } + String getLastError() override { return lastError; } + bool isPlaying() override { return isRunning && callback != 0; } - void start (AudioIODeviceCallback* newCallback) + void start (AudioIODeviceCallback* newCallback) override { if (isRunning && callback != newCallback) { @@ -261,7 +267,7 @@ public: } } - void stop() + void stop() override { if (isRunning) { @@ -278,7 +284,7 @@ public: } } - void run() + void run() override { JNIEnv* env = getEnv(); jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels)); @@ -298,7 +304,7 @@ public: for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) { - AudioData::Pointer d (inputChannelBuffer.getSampleData (chan)); + AudioData::Pointer d (inputChannelBuffer.getWritePointer (chan)); if (chan < numDeviceInputChannels) { @@ -322,8 +328,8 @@ public: if (callback != nullptr) { - callback->audioDeviceIOCallback ((const float**) inputChannelBuffer.getArrayOfChannels(), numClientInputChannels, - outputChannelBuffer.getArrayOfChannels(), numClientOutputChannels, + callback->audioDeviceIOCallback (inputChannelBuffer.getArrayOfReadPointers(), numClientInputChannels, + outputChannelBuffer.getArrayOfWritePointers(), numClientOutputChannels, actualBufferSize); } else @@ -343,7 +349,7 @@ public: { AudioData::Pointer d (dest + chan, numDeviceOutputChannels); - const float* const sourceChanData = outputChannelBuffer.getSampleData (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); + const float* const sourceChanData = outputChannelBuffer.getReadPointer (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); AudioData::Pointer s (sourceChanData); d.convertSamples (s, actualBufferSize); } @@ -392,7 +398,7 @@ private: } } - JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice); + JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice) }; //============================================================================== @@ -426,7 +432,7 @@ public: } private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType) }; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp index b8a79bb..88eeae6 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Midi.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -42,6 +41,7 @@ MidiOutput* MidiOutput::openDevice (int index) MidiOutput::~MidiOutput() { + stopBackgroundThread(); } void MidiOutput::sendMessageNow (const MidiMessage&) diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index 5525bb1..e02951d 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -31,9 +30,6 @@ bool isOpenSLAvailable() return library.open ("libOpenSLES.so"); } -const unsigned short openSLRates[] = { 8000, 16000, 32000, 44100, 48000 }; -const unsigned short openSLBufferSizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size - //============================================================================== class OpenSLAudioIODevice : public AudioIODevice, public Thread @@ -67,7 +63,7 @@ public: bool openedOk() const { return engine.outputMixObject != nullptr; } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); @@ -75,38 +71,33 @@ public: return s; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray s; s.add ("Audio Input"); return s; } - int getNumSampleRates() { return numElementsInArray (openSLRates); } - - double getSampleRate (int index) + Array getAvailableSampleRates() override { - jassert (index >= 0 && index < getNumSampleRates()); - return (int) openSLRates [index]; + static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 }; + return Array (rates, numElementsInArray (rates)); } - int getDefaultBufferSize() { return 1024; } - int getNumBufferSizesAvailable() { return numElementsInArray (openSLBufferSizes); } - - int getBufferSizeSamples (int index) + Array getAvailableBufferSizes() override { - jassert (index >= 0 && index < getNumBufferSizesAvailable()); - return (int) openSLBufferSizes [index]; + static const int sizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size + return Array (sizes, numElementsInArray (sizes)); } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double requestedSampleRate, - int bufferSize) + int bufferSize) override { close(); - lastError = String::empty; + lastError.clear(); sampleRate = (int) requestedSampleRate; int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; @@ -121,11 +112,12 @@ public: actualBufferSize = preferredBufferSize; - inputBuffer.setSize (jmax (1, numInputChannels), actualBufferSize); + inputBuffer.setSize (jmax (1, numInputChannels), actualBufferSize); outputBuffer.setSize (jmax (1, numOutputChannels), actualBufferSize); + outputBuffer.clear(); - recorder = engine.createRecorder (numInputChannels, sampleRate); - player = engine.createPlayer (numOutputChannels, sampleRate); + recorder = engine.createRecorder (numInputChannels, sampleRate); + player = engine.createPlayer (numOutputChannels, sampleRate); startThread (8); @@ -133,7 +125,7 @@ public: return lastError; } - void close() + void close() override { stop(); stopThread (6000); @@ -142,18 +134,19 @@ public: player = nullptr; } - int getOutputLatencyInSamples() { return outputLatency; } - int getInputLatencyInSamples() { return inputLatency; } - bool isOpen() { return deviceOpen; } - int getCurrentBufferSizeSamples() { return actualBufferSize; } - int getCurrentBitDepth() { return 16; } - double getCurrentSampleRate() { return sampleRate; } - BigInteger getActiveOutputChannels() const { return activeOutputChans; } - BigInteger getActiveInputChannels() const { return activeInputChans; } - String getLastError() { return lastError; } - bool isPlaying() { return callback != nullptr; } + int getDefaultBufferSize() override { return 1024; } + int getOutputLatencyInSamples() override { return outputLatency; } + int getInputLatencyInSamples() override { return inputLatency; } + bool isOpen() override { return deviceOpen; } + int getCurrentBufferSizeSamples() override { return actualBufferSize; } + int getCurrentBitDepth() override { return 16; } + double getCurrentSampleRate() override { return sampleRate; } + BigInteger getActiveOutputChannels() const override { return activeOutputChans; } + BigInteger getActiveInputChannels() const override { return activeInputChans; } + String getLastError() override { return lastError; } + bool isPlaying() override { return callback != nullptr; } - void start (AudioIODeviceCallback* newCallback) + void start (AudioIODeviceCallback* newCallback) override { stop(); @@ -166,47 +159,15 @@ public: } } - void stop() + void stop() override { - AudioIODeviceCallback* const oldCallback = setCallback (nullptr); - - if (oldCallback != nullptr) + if (AudioIODeviceCallback* const oldCallback = setCallback (nullptr)) oldCallback->audioDeviceStopped(); } - void run() + bool setAudioPreprocessingEnabled (bool enable) override { - if (recorder != nullptr) recorder->start(); - if (player != nullptr) player->start(); - - while (! threadShouldExit()) - { - if (player != nullptr && ! threadShouldExit()) - player->writeBuffer (outputBuffer, *this); - - if (recorder != nullptr) - recorder->readNextBlock (inputBuffer, *this); - - invokeCallback(); - } - } - - void invokeCallback() - { - const ScopedLock sl (callbackLock); - - if (callback != nullptr) - { - callback->audioDeviceIOCallback (numInputChannels > 0 ? (const float**) inputBuffer.getArrayOfChannels() : nullptr, - numInputChannels, - numOutputChannels > 0 ? outputBuffer.getArrayOfChannels() : nullptr, - numOutputChannels, - actualBufferSize); - } - else - { - outputBuffer.clear(); - } + return recorder != nullptr && recorder->setAudioPreprocessingEnabled (enable); } private: @@ -231,6 +192,31 @@ private: return oldCallback; } + void run() override + { + if (recorder != nullptr) recorder->start(); + if (player != nullptr) player->start(); + + while (! threadShouldExit()) + { + if (player != nullptr) player->writeBuffer (outputBuffer, *this); + if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this); + + const ScopedLock sl (callbackLock); + + if (callback != nullptr) + { + callback->audioDeviceIOCallback (numInputChannels > 0 ? inputBuffer.getArrayOfReadPointers() : nullptr, numInputChannels, + numOutputChannels > 0 ? outputBuffer.getArrayOfWritePointers() : nullptr, numOutputChannels, + actualBufferSize); + } + else + { + outputBuffer.clear(); + } + } + } + //================================================================================================== struct Engine { @@ -240,9 +226,8 @@ private: if (library.open ("libOpenSLES.so")) { typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*); - CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine"); - if (createEngine != nullptr) + if (CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine")) { check (createEngine (&engineObject, 0, nullptr, 0, nullptr, nullptr)); @@ -250,6 +235,7 @@ private: SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY"); SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD"); + SL_IID_ANDROIDCONFIGURATION = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDCONFIGURATION"); check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE)); check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface)); @@ -291,11 +277,12 @@ private: SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE; SLInterfaceID* SL_IID_PLAY; SLInterfaceID* SL_IID_RECORD; + SLInterfaceID* SL_IID_ANDROIDCONFIGURATION; private: DynamicLibrary library; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine) }; //================================================================================================== @@ -422,7 +409,7 @@ private: typedef AudioData::Pointer SrcSampleType; DstSampleType dstData (destBuffer + i, bufferList.numChannels); - SrcSampleType srcData (buffer.getSampleData (i, offset)); + SrcSampleType srcData (buffer.getReadPointer (i, offset)); dstData.convertSamples (srcData, bufferList.numSamples); } @@ -447,14 +434,15 @@ private: static_cast (context)->bufferList.bufferReturned(); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player) }; //================================================================================================== struct Recorder { Recorder (int numChannels, int sampleRate, Engine& engine) - : recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr), + : recorderObject (nullptr), recorderRecord (nullptr), + recorderBufferQueue (nullptr), configObject (nullptr), bufferList (numChannels) { jassert (numChannels == 1); // STEREO doesn't always work!! @@ -486,6 +474,7 @@ private: { check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord)); check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue)); + check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDCONFIGURATION, &configObject)); check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this)); check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED)); @@ -540,7 +529,7 @@ private: typedef AudioData::Pointer DstSampleType; typedef AudioData::Pointer SrcSampleType; - DstSampleType dstData (buffer.getSampleData (i, offset)); + DstSampleType dstData (buffer.getWritePointer (i, offset)); SrcSampleType srcData (srcBuffer + i, bufferList.numChannels); dstData.convertSamples (srcData, bufferList.numSamples); } @@ -552,10 +541,20 @@ private: } } + bool setAudioPreprocessingEnabled (bool enable) + { + SLuint32 mode = enable ? SL_ANDROID_RECORDING_PRESET_GENERIC + : SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + + return configObject != nullptr + && check ((*configObject)->SetConfiguration (configObject, SL_ANDROID_KEY_RECORDING_PRESET, &mode, sizeof (mode))); + } + private: SLObjectItf recorderObject; SLRecordItf recorderRecord; SLAndroidSimpleBufferQueueItf recorderBufferQueue; + SLAndroidConfigurationItf configObject; BufferList bufferList; @@ -571,7 +570,7 @@ private: static_cast (context)->bufferList.bufferReturned(); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Recorder); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Recorder) }; @@ -588,7 +587,7 @@ private: return result == SL_RESULT_SUCCESS; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice) }; @@ -622,7 +621,7 @@ public: } private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioDeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioDeviceType) }; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp index b86a481..b7d1611 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -1,32 +1,31 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -class IPhoneAudioIODevice : public AudioIODevice +class iOSAudioIODevice : public AudioIODevice { public: - IPhoneAudioIODevice (const String& deviceName) + iOSAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, "Audio"), actualBufferSize (0), isRunning (false), @@ -43,13 +42,13 @@ public: updateDeviceInfo(); } - ~IPhoneAudioIODevice() + ~iOSAudioIODevice() { - getSessionHolder().activeDevices.removeValue (this); + getSessionHolder().activeDevices.removeFirstMatchingValue (this); close(); } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); @@ -57,7 +56,7 @@ public: return s; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray s; if (audioInputIsAvailable) @@ -68,51 +67,69 @@ public: return s; } - int getNumSampleRates() { return 1; } - double getSampleRate (int index) { return sampleRate; } + Array getAvailableSampleRates() override + { + // can't find a good way to actually ask the device for which of these it supports.. + static const double rates[] = { 8000.0, 16000.0, 22050.0, 32000.0, 44100.0, 48000.0 }; + return Array (rates, numElementsInArray (rates)); + } - int getNumBufferSizesAvailable() { return 6; } - int getBufferSizeSamples (int index) { return 1 << (jlimit (0, 5, index) + 6); } - int getDefaultBufferSize() { return 1024; } + Array getAvailableBufferSizes() override + { + Array r; - String open (const BigInteger& inputChannels, - const BigInteger& outputChannels, - double sampleRate, - int bufferSize) + for (int i = 6; i < 12; ++i) + r.add (1 << i); + + return r; + } + + int getDefaultBufferSize() override { return 1024; } + + String open (const BigInteger& inputChannelsWanted, + const BigInteger& outputChannelsWanted, + double targetSampleRate, int bufferSize) override { close(); - lastError = String::empty; + lastError.clear(); preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; // xxx set up channel mapping - activeOutputChans = outputChannels; + activeOutputChans = outputChannelsWanted; activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); numOutputChannels = activeOutputChans.countNumberOfSetBits(); monoOutputChannelNumber = activeOutputChans.findNextSetBit (0); - activeInputChans = inputChannels; + activeInputChans = inputChannelsWanted; activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); numInputChannels = activeInputChans.countNumberOfSetBits(); monoInputChannelNumber = activeInputChans.findNextSetBit (0); AudioSessionSetActive (true); - UInt32 audioCategory = audioInputIsAvailable ? kAudioSessionCategory_PlayAndRecord - : kAudioSessionCategory_MediaPlayback; + if (numInputChannels > 0 && audioInputIsAvailable) + { + setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_PlayAndRecord); + setSessionUInt32Property (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, 1); + } + else + { + setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback); + } - AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory); AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); fixAudioRouteIfSetToReceiver(); + + setSessionFloat64Property (kAudioSessionProperty_PreferredHardwareSampleRate, targetSampleRate); updateDeviceInfo(); - Float32 bufferDuration = preferredBufferSize / sampleRate; - AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof (bufferDuration), &bufferDuration); - actualBufferSize = preferredBufferSize; + setSessionFloat32Property (kAudioSessionProperty_PreferredHardwareIOBufferDuration, preferredBufferSize / sampleRate); + updateCurrentBufferSize(); - prepareFloatBuffers(); + prepareFloatBuffers (actualBufferSize); isRunning = true; routingChanged (nullptr); // creates and starts the AU @@ -121,11 +138,14 @@ public: return lastError; } - void close() + void close() override { if (isRunning) { isRunning = false; + + setSessionUInt32Property (kAudioSessionProperty_AudioCategory, kAudioSessionCategory_MediaPlayback); + AudioSessionRemovePropertyListenerWithUserData (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); AudioSessionSetActive (false); @@ -137,31 +157,38 @@ public: } } - bool isOpen() { return isRunning; } + bool isOpen() override { return isRunning; } - int getCurrentBufferSizeSamples() { return actualBufferSize; } - double getCurrentSampleRate() { return sampleRate; } - int getCurrentBitDepth() { return 16; } + int getCurrentBufferSizeSamples() override { return actualBufferSize; } + double getCurrentSampleRate() override { return sampleRate; } + int getCurrentBitDepth() override { return 16; } - BigInteger getActiveOutputChannels() const { return activeOutputChans; } - BigInteger getActiveInputChannels() const { return activeInputChans; } + BigInteger getActiveOutputChannels() const override { return activeOutputChans; } + BigInteger getActiveInputChannels() const override { return activeInputChans; } - int getOutputLatencyInSamples() { return 0; } //xxx - int getInputLatencyInSamples() { return 0; } //xxx + int getOutputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareOutputLatency); } + int getInputLatencyInSamples() override { return getLatency (kAudioSessionProperty_CurrentHardwareInputLatency); } - void start (AudioIODeviceCallback* callback_) + int getLatency (AudioSessionPropertyID propID) { - if (isRunning && callback != callback_) + Float32 latency = 0; + getSessionProperty (propID, latency); + return roundToInt (latency * getCurrentSampleRate()); + } + + void start (AudioIODeviceCallback* newCallback) override + { + if (isRunning && callback != newCallback) { - if (callback_ != nullptr) - callback_->audioDeviceAboutToStart (this); + if (newCallback != nullptr) + newCallback->audioDeviceAboutToStart (this); const ScopedLock sl (callbackLock); - callback = callback_; + callback = newCallback; } } - void stop() + void stop() override { if (isRunning) { @@ -178,8 +205,14 @@ public: } } - bool isPlaying() { return isRunning && callback != nullptr; } - String getLastError() { return lastError; } + bool isPlaying() override { return isRunning && callback != nullptr; } + String getLastError() override { return lastError; } + + bool setAudioPreprocessingEnabled (bool enable) override + { + return setSessionUInt32Property (kAudioSessionProperty_Mode, enable ? kAudioSessionMode_Default + : kAudioSessionMode_Measurement); + } private: //================================================================================================== @@ -201,17 +234,20 @@ private: float* outputChannels[3]; bool monoInputChannelNumber, monoOutputChannelNumber; - void prepareFloatBuffers() + void prepareFloatBuffers (int bufferSize) { - floatData.setSize (numInputChannels + numOutputChannels, actualBufferSize); - zeromem (inputChannels, sizeof (inputChannels)); - zeromem (outputChannels, sizeof (outputChannels)); + if (numInputChannels + numOutputChannels > 0) + { + floatData.setSize (numInputChannels + numOutputChannels, bufferSize); + zeromem (inputChannels, sizeof (inputChannels)); + zeromem (outputChannels, sizeof (outputChannels)); - for (int i = 0; i < numInputChannels; ++i) - inputChannels[i] = floatData.getSampleData (i); + for (int i = 0; i < numInputChannels; ++i) + inputChannels[i] = floatData.getWritePointer (i); - for (int i = 0; i < numOutputChannels; ++i) - outputChannels[i] = floatData.getSampleData (i + numInputChannels); + for (int i = 0; i < numOutputChannels; ++i) + outputChannels[i] = floatData.getWritePointer (i + numInputChannels); + } } //================================================================================================== @@ -227,6 +263,9 @@ private: if (callback != nullptr) { + if ((int) numFrames > floatData.getNumSamples()) + prepareFloatBuffers ((int) numFrames); + if (audioInputIsAvailable && numInputChannels > 0) { short* shortData = (short*) data->mBuffers[0].mData; @@ -295,11 +334,15 @@ private: void updateDeviceInfo() { - UInt32 size = sizeof (sampleRate); - AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate); + getSessionProperty (kAudioSessionProperty_CurrentHardwareSampleRate, sampleRate); + getSessionProperty (kAudioSessionProperty_AudioInputAvailable, audioInputIsAvailable); + } - size = sizeof (audioInputIsAvailable); - AudioSessionGetProperty (kAudioSessionProperty_AudioInputAvailable, &size, &audioInputIsAvailable); + void updateCurrentBufferSize() + { + Float32 bufferDuration = sampleRate > 0 ? (Float32) (preferredBufferSize / sampleRate) : 0.0f; + getSessionProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, bufferDuration); + actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); } void routingChanged (const void* propertyValue) @@ -317,7 +360,12 @@ private: CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) - fixAudioRouteIfSetToReceiver(); + { + const ScopedLock sl (callbackLock); + + if (callback != nullptr) + callback->audioDeviceError ("Old device unavailable"); + } } updateDeviceInfo(); @@ -330,11 +378,7 @@ private: UInt32 formatSize = sizeof (format); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize); - Float32 bufferDuration = preferredBufferSize / sampleRate; - UInt32 bufferDurationSize = sizeof (bufferDuration); - AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, &bufferDurationSize, &bufferDurationSize); - actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); - + updateCurrentBufferSize(); AudioOutputUnitStart (audioUnit); } } @@ -349,13 +393,13 @@ private: static void interruptionListenerCallback (void* client, UInt32 interruptionType) { - const Array & activeDevices = static_cast (client)->activeDevices; + const Array & activeDevices = static_cast (client)->activeDevices; for (int i = activeDevices.size(); --i >= 0;) activeDevices.getUnchecked(i)->interruptionListener (interruptionType); } - Array activeDevices; + Array activeDevices; }; static AudioSessionHolder& getSessionHolder() @@ -366,26 +410,28 @@ private: void interruptionListener (const UInt32 interruptionType) { - /*if (interruptionType == kAudioSessionBeginInterruption) + if (interruptionType == kAudioSessionBeginInterruption) { isRunning = false; AudioOutputUnitStop (audioUnit); + AudioSessionSetActive (false); - if (juce_iPhoneShowModalAlert ("Audio Interrupted", - "This could have been interrupted by another application or by unplugging a headset", - @"Resume", - @"Cancel")) - { - isRunning = true; - routingChanged (nullptr); - } - }*/ + const ScopedLock sl (callbackLock); + + if (callback != nullptr) + callback->audioDeviceError ("iOS audio session interruption"); + } if (interruptionType == kAudioSessionEndInterruption) { isRunning = true; AudioSessionSetActive (true); AudioOutputUnitStart (audioUnit); + + const ScopedLock sl (callbackLock); + + if (callback != nullptr) + callback->audioDeviceError ("iOS audio session resumed"); } } @@ -393,12 +439,12 @@ private: static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) { - return static_cast (client)->process (flags, time, numFrames, data); + return static_cast (client)->process (flags, time, numFrames, data); } static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue) { - static_cast (client)->routingChanged (propertyValue); + static_cast (client)->routingChanged (propertyValue); } //================================================================================================== @@ -408,9 +454,9 @@ private: format.mFormatID = kAudioFormatLinearPCM; format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kAudioFormatFlagsNativeEndian; format.mBitsPerChannel = 8 * sizeof (short); - format.mChannelsPerFrame = numChannels; + format.mChannelsPerFrame = (UInt32) numChannels; format.mFramesPerPacket = 1; - format.mBytesPerFrame = format.mBytesPerPacket = numChannels * sizeof (short); + format.mBytesPerFrame = format.mBytesPerPacket = (UInt32) numChannels * sizeof (short); } bool createAudioUnit() @@ -470,72 +516,61 @@ private: static void fixAudioRouteIfSetToReceiver() { CFStringRef audioRoute = 0; - UInt32 propertySize = sizeof (audioRoute); - if (AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &audioRoute) == noErr) + if (getSessionProperty (kAudioSessionProperty_AudioRoute, audioRoute) == noErr) { NSString* route = (NSString*) audioRoute; //DBG ("audio route: " + nsStringToJuce (route)); if ([route hasPrefix: @"Receiver"]) - { - UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; - AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride); - } + setSessionUInt32Property (kAudioSessionProperty_OverrideAudioRoute, kAudioSessionOverrideAudioRoute_Speaker); CFRelease (audioRoute); } } - JUCE_DECLARE_NON_COPYABLE (IPhoneAudioIODevice); + template + static OSStatus getSessionProperty (AudioSessionPropertyID propID, Type& result) noexcept + { + UInt32 valueSize = sizeof (result); + return AudioSessionGetProperty (propID, &valueSize, &result); + } + + static bool setSessionUInt32Property (AudioSessionPropertyID propID, UInt32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } + static bool setSessionFloat32Property (AudioSessionPropertyID propID, Float32 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } + static bool setSessionFloat64Property (AudioSessionPropertyID propID, Float64 v) noexcept { AudioSessionSetProperty (propID, sizeof (v), &v) == kAudioSessionNoError; } + + JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) }; //============================================================================== -class IPhoneAudioIODeviceType : public AudioIODeviceType +class iOSAudioIODeviceType : public AudioIODeviceType { public: - //============================================================================== - IPhoneAudioIODeviceType() - : AudioIODeviceType ("iPhone Audio") - { - } + iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") {} void scanForDevices() {} + StringArray getDeviceNames (bool /*wantInputNames*/) const { return StringArray ("iOS Audio"); } + int getDefaultDeviceIndex (bool /*forInput*/) const { return 0; } + int getIndexOfDevice (AudioIODevice* d, bool /*asInput*/) const { return d != nullptr ? 0 : -1; } + bool hasSeparateInputsAndOutputs() const { return false; } - StringArray getDeviceNames (bool wantInputNames) const - { - return StringArray ("iPhone Audio"); - } - - int getDefaultDeviceIndex (bool forInput) const - { - return 0; - } - - int getIndexOfDevice (AudioIODevice* device, bool asInput) const - { - return device != nullptr ? 0 : -1; - } - - bool hasSeparateInputsAndOutputs() const { return false; } - - AudioIODevice* createDevice (const String& outputDeviceName, - const String& inputDeviceName) + AudioIODevice* createDevice (const String& outputDeviceName, const String& inputDeviceName) { if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) - return new IPhoneAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName - : inputDeviceName); + return new iOSAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName + : inputDeviceName); return nullptr; } private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (IPhoneAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSAudioIODeviceType) }; //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { - return new IPhoneAudioIODeviceType(); + return new iOSAudioIODeviceType(); } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 51ad00e..9bc7ecf 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -1,134 +1,193 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ namespace { - void getDeviceSampleRates (snd_pcm_t* handle, Array & rates) + +#ifndef JUCE_ALSA_LOGGING + #define JUCE_ALSA_LOGGING 0 +#endif + +#if JUCE_ALSA_LOGGING + #define JUCE_ALSA_LOG(dbgtext) { juce::String tempDbgBuf ("ALSA: "); tempDbgBuf << dbgtext; Logger::writeToLog (tempDbgBuf); DBG (tempDbgBuf) } + #define JUCE_CHECKED_RESULT(x) (logErrorMessage (x, __LINE__)) + + static int logErrorMessage (int err, int lineNum) + { + if (err < 0) + JUCE_ALSA_LOG ("Error: line " << lineNum << ": code " << err << " (" << snd_strerror (err) << ")"); + + return err; + } +#else + #define JUCE_ALSA_LOG(x) + #define JUCE_CHECKED_RESULT(x) (x) +#endif + +#define JUCE_ALSA_FAILED(x) failed (x) + +static void getDeviceSampleRates (snd_pcm_t* handle, Array& rates) +{ + const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; + + snd_pcm_hw_params_t* hwParams; + snd_pcm_hw_params_alloca (&hwParams); + + for (int i = 0; ratesToTry[i] != 0; ++i) { - const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; - - snd_pcm_hw_params_t* hwParams; - snd_pcm_hw_params_alloca (&hwParams); - - for (int i = 0; ratesToTry[i] != 0; ++i) + if (snd_pcm_hw_params_any (handle, hwParams) >= 0 + && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0) { - if (snd_pcm_hw_params_any (handle, hwParams) >= 0 - && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0) - { - rates.addIfNotAlreadyThere (ratesToTry[i]); - } - } - } - - void getDeviceNumChannels (snd_pcm_t* handle, unsigned int* minChans, unsigned int* maxChans) - { - snd_pcm_hw_params_t *params; - snd_pcm_hw_params_alloca (¶ms); - - if (snd_pcm_hw_params_any (handle, params) >= 0) - { - snd_pcm_hw_params_get_channels_min (params, minChans); - snd_pcm_hw_params_get_channels_max (params, maxChans); - } - } - - void getDeviceProperties (const String& deviceID, - unsigned int& minChansOut, - unsigned int& maxChansOut, - unsigned int& minChansIn, - unsigned int& maxChansIn, - Array & rates) - { - if (deviceID.isEmpty()) - return; - - snd_ctl_t* handle; - - if (snd_ctl_open (&handle, deviceID.upToLastOccurrenceOf (",", false, false).toUTF8(), SND_CTL_NONBLOCK) >= 0) - { - snd_pcm_info_t* info; - snd_pcm_info_alloca (&info); - - snd_pcm_info_set_stream (info, SND_PCM_STREAM_PLAYBACK); - snd_pcm_info_set_device (info, deviceID.fromLastOccurrenceOf (",", false, false).getIntValue()); - snd_pcm_info_set_subdevice (info, 0); - - if (snd_ctl_pcm_info (handle, info) >= 0) - { - snd_pcm_t* pcmHandle; - if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC | SND_PCM_NONBLOCK) >= 0) - { - getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut); - getDeviceSampleRates (pcmHandle, rates); - - snd_pcm_close (pcmHandle); - } - } - - snd_pcm_info_set_stream (info, SND_PCM_STREAM_CAPTURE); - - if (snd_ctl_pcm_info (handle, info) >= 0) - { - snd_pcm_t* pcmHandle; - if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_CAPTURE, SND_PCM_ASYNC | SND_PCM_NONBLOCK) >= 0) - { - getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn); - - if (rates.size() == 0) - getDeviceSampleRates (pcmHandle, rates); - - snd_pcm_close (pcmHandle); - } - } - - snd_ctl_close (handle); + rates.addIfNotAlreadyThere ((double) ratesToTry[i]); } } } +static void getDeviceNumChannels (snd_pcm_t* handle, unsigned int* minChans, unsigned int* maxChans) +{ + snd_pcm_hw_params_t *params; + snd_pcm_hw_params_alloca (¶ms); + + if (snd_pcm_hw_params_any (handle, params) >= 0) + { + snd_pcm_hw_params_get_channels_min (params, minChans); + snd_pcm_hw_params_get_channels_max (params, maxChans); + + JUCE_ALSA_LOG ("getDeviceNumChannels: " << (int) *minChans << " " << (int) *maxChans); + + // some virtual devices (dmix for example) report 10000 channels , we have to clamp these values + *maxChans = jmin (*maxChans, 32u); + *minChans = jmin (*minChans, *maxChans); + } + else + { + JUCE_ALSA_LOG ("getDeviceNumChannels failed"); + } +} + +static void getDeviceProperties (const String& deviceID, + unsigned int& minChansOut, + unsigned int& maxChansOut, + unsigned int& minChansIn, + unsigned int& maxChansIn, + Array& rates, + bool testOutput, + bool testInput) +{ + minChansOut = maxChansOut = minChansIn = maxChansIn = 0; + + if (deviceID.isEmpty()) + return; + + JUCE_ALSA_LOG ("getDeviceProperties(" << deviceID.toUTF8().getAddress() << ")"); + + snd_pcm_info_t* info; + snd_pcm_info_alloca (&info); + + if (testOutput) + { + snd_pcm_t* pcmHandle; + + if (JUCE_CHECKED_RESULT (snd_pcm_open (&pcmHandle, deviceID.toUTF8().getAddress(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) >= 0) + { + getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut); + getDeviceSampleRates (pcmHandle, rates); + + snd_pcm_close (pcmHandle); + } + } + + if (testInput) + { + snd_pcm_t* pcmHandle; + + if (JUCE_CHECKED_RESULT (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) >= 0)) + { + getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn); + + if (rates.size() == 0) + getDeviceSampleRates (pcmHandle, rates); + + snd_pcm_close (pcmHandle); + } + } +} + +static void ensureMinimumNumBitsSet (BigInteger& chans, int minNumChans) +{ + int i = 0; + + while (chans.countNumberOfSetBits() < minNumChans) + chans.setBit (i++); +} + +static void silentErrorHandler (const char*, int, const char*, int, const char*,...) {} + //============================================================================== class ALSADevice { public: - ALSADevice (const String& deviceID, bool forInput) + ALSADevice (const String& devID, bool forInput) : handle (0), bitDepth (16), numChannelsRunning (0), latency (0), + deviceID (devID), isInput (forInput), isInterleaved (true) { - failed (snd_pcm_open (&handle, deviceID.toUTF8(), - forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, - SND_PCM_ASYNC)); + JUCE_ALSA_LOG ("snd_pcm_open (" << deviceID.toUTF8().getAddress() << ", forInput=" << forInput << ")"); + + int err = snd_pcm_open (&handle, deviceID.toUTF8(), + forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, + SND_PCM_ASYNC); + if (err < 0) + { + if (-err == EBUSY) + error << "The device \"" << deviceID << "\" is busy (another application is using it)."; + else if (-err == ENOENT) + error << "The device \"" << deviceID << "\" is not available."; + else + error << "Could not open " << (forInput ? "input" : "output") << " device \"" << deviceID + << "\": " << snd_strerror(err) << " (" << err << ")"; + + JUCE_ALSA_LOG ("snd_pcm_open failed; " << error); + } } ~ALSADevice() + { + closeNow(); + } + + void closeNow() { if (handle != 0) + { snd_pcm_close (handle); + handle = 0; + } } bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize) @@ -136,23 +195,31 @@ public: if (handle == 0) return false; + JUCE_ALSA_LOG ("ALSADevice::setParameters(" << deviceID << ", " + << (int) sampleRate << ", " << numChannels << ", " << bufferSize << ")"); + snd_pcm_hw_params_t* hwParams; snd_pcm_hw_params_alloca (&hwParams); - if (failed (snd_pcm_hw_params_any (handle, hwParams))) + if (snd_pcm_hw_params_any (handle, hwParams) < 0) + { + // this is the error message that aplay returns when an error happens here, + // it is a bit more explicit that "Invalid parameter" + error = "Broken configuration for this PCM: no configurations available"; return false; + } - if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0) - isInterleaved = false; - else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0) + if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0) // works better for plughw.. isInterleaved = true; + else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0) + isInterleaved = false; else { jassertfalse; return false; } - enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17 }; + enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17, onlyUseLower24Bits = 1 << 18 }; const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32 | isFloatBit | isLittleEndianBit, SND_PCM_FORMAT_FLOAT_BE, 32 | isFloatBit, @@ -160,6 +227,7 @@ public: SND_PCM_FORMAT_S32_BE, 32, SND_PCM_FORMAT_S24_3LE, 24 | isLittleEndianBit, SND_PCM_FORMAT_S24_3BE, 24, + SND_PCM_FORMAT_S24_LE, 32 | isLittleEndianBit | onlyUseLower24Bits, SND_PCM_FORMAT_S16_LE, 16 | isLittleEndianBit, SND_PCM_FORMAT_S16_BE, 16 }; bitDepth = 0; @@ -168,10 +236,14 @@ public: { if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0) { - bitDepth = formatsToTry [i + 1] & 255; - const bool isFloat = (formatsToTry [i + 1] & isFloatBit) != 0; - const bool isLittleEndian = (formatsToTry [i + 1] & isLittleEndianBit) != 0; - converter = createConverter (isInput, bitDepth, isFloat, isLittleEndian, numChannels); + const int type = formatsToTry [i + 1]; + bitDepth = type & 255; + + converter = createConverter (isInput, bitDepth, + (type & isFloatBit) != 0, + (type & isLittleEndianBit) != 0, + (type & onlyUseLower24Bits) != 0, + numChannels); break; } } @@ -179,7 +251,7 @@ public: if (bitDepth == 0) { error = "device doesn't support a compatible PCM format"; - DBG ("ALSA error: " + error + "\n"); + JUCE_ALSA_LOG ("Error: " + error); return false; } @@ -187,45 +259,48 @@ public: unsigned int periods = 4; snd_pcm_uframes_t samplesPerPeriod = bufferSize; - if (failed (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) - || failed (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels)) - || failed (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) - || failed (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) - || failed (snd_pcm_hw_params (handle, hwParams))) + if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0)) + || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels)) + || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir)) + || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir)) + || JUCE_ALSA_FAILED (snd_pcm_hw_params (handle, hwParams))) { return false; } snd_pcm_uframes_t frames = 0; - if (failed (snd_pcm_hw_params_get_period_size (hwParams, &frames, &dir)) - || failed (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) + if (JUCE_ALSA_FAILED (snd_pcm_hw_params_get_period_size (hwParams, &frames, &dir)) + || JUCE_ALSA_FAILED (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir))) latency = 0; else latency = frames * (periods - 1); // (this is the method JACK uses to guess the latency..) + JUCE_ALSA_LOG ("frames: " << (int) frames << ", periods: " << (int) periods + << ", samplesPerPeriod: " << (int) samplesPerPeriod); + snd_pcm_sw_params_t* swParams; snd_pcm_sw_params_alloca (&swParams); snd_pcm_uframes_t boundary; - if (failed (snd_pcm_sw_params_current (handle, swParams)) - || failed (snd_pcm_sw_params_get_boundary (swParams, &boundary)) - || failed (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0)) - || failed (snd_pcm_sw_params_set_silence_size (handle, swParams, boundary)) - || failed (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod)) - || failed (snd_pcm_sw_params_set_stop_threshold (handle, swParams, boundary)) - || failed (snd_pcm_sw_params (handle, swParams))) + if (JUCE_ALSA_FAILED (snd_pcm_sw_params_current (handle, swParams)) + || JUCE_ALSA_FAILED (snd_pcm_sw_params_get_boundary (swParams, &boundary)) + || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0)) + || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_size (handle, swParams, boundary)) + || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod)) + || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_stop_threshold (handle, swParams, boundary)) + || JUCE_ALSA_FAILED (snd_pcm_sw_params (handle, swParams))) { return false; } - #if 0 + #if JUCE_ALSA_LOGGING // 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 + #endif numChannelsRunning = numChannels; @@ -236,7 +311,7 @@ public: bool writeToOutputDevice (AudioSampleBuffer& outputChannelBuffer, const int numSamples) { jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels()); - float** const data = outputChannelBuffer.getArrayOfChannels(); + float* const* const data = outputChannelBuffer.getArrayOfWritePointers(); snd_pcm_sframes_t numDone = 0; if (isInterleaved) @@ -256,16 +331,11 @@ public: numDone = snd_pcm_writen (handle, (void**) data, numSamples); } - if (failed (numDone)) - { - if (numDone == -EPIPE) - { - if (failed (snd_pcm_prepare (handle))) - return false; - } - else if (numDone != -ESTRPIPE) - return false; - } + if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, numDone, 1 /* silent */))) + return false; + + if (numDone < numSamples) + JUCE_ALSA_LOG ("Did not write all samples: numDone: " << numDone << ", numSamples: " << numSamples); return true; } @@ -273,7 +343,7 @@ public: bool readFromInputDevice (AudioSampleBuffer& inputChannelBuffer, const int numSamples) { jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels()); - float** const data = inputChannelBuffer.getArrayOfChannels(); + float* const* const data = inputChannelBuffer.getArrayOfWritePointers(); if (isInterleaved) { @@ -282,16 +352,11 @@ public: snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), numSamples); - if (failed (num)) - { - if (num == -EPIPE) - { - if (failed (snd_pcm_prepare (handle))) - return false; - } - else if (num != -ESTRPIPE) - return false; - } + if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */))) + return false; + + if (num < numSamples) + JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples); for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (data[i], 0, scratch.getData(), i, numSamples); @@ -300,9 +365,12 @@ public: { snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, numSamples); - if (failed (num) && num != -EPIPE && num != -ESTRPIPE) + if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */))) return false; + if (num < numSamples) + JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples); + for (int i = 0; i < numChannelsRunning; ++i) converter->convertSamples (data[i], data[i], numSamples); } @@ -315,8 +383,9 @@ public: String error; int bitDepth, numChannelsRunning, latency; - //============================================================================== private: + //============================================================================== + String deviceID; const bool isInput; bool isInterleaved; MemoryBlock scratch; @@ -334,33 +403,36 @@ private: if (isLittleEndian) return new AudioData::ConverterInstance , DestType> (numInterleavedChannels, 1); - else - return new AudioData::ConverterInstance , DestType> (numInterleavedChannels, 1); - } - else - { - typedef AudioData::Pointer SourceType; - if (isLittleEndian) - return new AudioData::ConverterInstance > (1, numInterleavedChannels); - else - return new AudioData::ConverterInstance > (1, numInterleavedChannels); + return new AudioData::ConverterInstance , DestType> (numInterleavedChannels, 1); } + + typedef AudioData::Pointer SourceType; + + if (isLittleEndian) + return new AudioData::ConverterInstance > (1, numInterleavedChannels); + + return new AudioData::ConverterInstance > (1, numInterleavedChannels); } }; - static AudioData::Converter* createConverter (const bool forInput, const int bitDepth, const bool isFloat, const bool isLittleEndian, const int numInterleavedChannels) + static AudioData::Converter* createConverter (bool forInput, int bitDepth, + bool isFloat, bool isLittleEndian, bool useOnlyLower24Bits, + int numInterleavedChannels) { - switch (bitDepth) - { - case 16: return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); - case 24: return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); - case 32: return isFloat ? ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels) - : ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); - default: jassertfalse; break; // unsupported format! - } + JUCE_ALSA_LOG ("format: bitDepth=" << bitDepth << ", isFloat=" << isFloat + << ", isLittleEndian=" << isLittleEndian << ", numChannels=" << numInterleavedChannels); - return nullptr; + if (isFloat) return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); + if (bitDepth == 16) return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); + if (bitDepth == 24) return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); + + jassert (bitDepth == 32); + + if (useOnlyLower24Bits) + return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); + + return ConverterHelper ::createConverter (forInput, isLittleEndian, numInterleavedChannels); } //============================================================================== @@ -370,28 +442,28 @@ private: return false; error = snd_strerror (errorNum); - DBG ("ALSA error: " + error + "\n"); + JUCE_ALSA_LOG ("ALSA error: " << error); return true; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSADevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSADevice) }; //============================================================================== class ALSAThread : public Thread { public: - ALSAThread (const String& inputId_, - const String& outputId_) + ALSAThread (const String& inputDeviceID, const String& outputDeviceID) : Thread ("Juce ALSA"), sampleRate (0), bufferSize (0), outputLatency (0), inputLatency (0), callback (0), - inputId (inputId_), - outputId (outputId_), + inputId (inputDeviceID), + outputId (outputDeviceID), numCallbacks (0), + audioIoInProgress (false), inputChannelBuffer (1, 1), outputChannelBuffer (1, 1) { @@ -405,14 +477,14 @@ public: void open (BigInteger inputChannels, BigInteger outputChannels, - const double sampleRate_, - const int bufferSize_) + const double newSampleRate, + const int newBufferSize) { close(); - error = String::empty; - sampleRate = sampleRate_; - bufferSize = bufferSize_; + error.clear(); + sampleRate = newSampleRate; + bufferSize = newBufferSize; inputChannelBuffer.setSize (jmax ((int) minChansIn, inputChannels.getHighestBit()) + 1, bufferSize); inputChannelBuffer.clear(); @@ -425,12 +497,14 @@ public: { if (inputChannels[i]) { - inputChannelDataForCallback.add (inputChannelBuffer.getSampleData (i)); + inputChannelDataForCallback.add (inputChannelBuffer.getReadPointer (i)); currentInputChans.setBit (i); } } } + ensureMinimumNumBitsSet (outputChannels, minChansOut); + outputChannelBuffer.setSize (jmax ((int) minChansOut, outputChannels.getHighestBit()) + 1, bufferSize); outputChannelBuffer.clear(); outputChannelDataForCallback.clear(); @@ -442,7 +516,7 @@ public: { if (outputChannels[i]) { - outputChannelDataForCallback.add (outputChannelBuffer.getSampleData (i)); + outputChannelDataForCallback.add (outputChannelBuffer.getWritePointer (i)); currentOutputChans.setBit (i); } } @@ -459,10 +533,9 @@ public: return; } - currentOutputChans.setRange (0, minChansOut, true); - if (! outputDevice->setParameters ((unsigned int) sampleRate, - jlimit ((int) minChansOut, (int) maxChansOut, currentOutputChans.getHighestBit() + 1), + jlimit ((int) minChansOut, (int) maxChansOut, + currentOutputChans.getHighestBit() + 1), bufferSize)) { error = outputDevice->error; @@ -484,7 +557,7 @@ public: return; } - currentInputChans.setRange (0, minChansIn, true); + ensureMinimumNumBitsSet (currentInputChans, minChansIn); if (! inputDevice->setParameters ((unsigned int) sampleRate, jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1), @@ -505,14 +578,12 @@ public: } if (outputDevice != nullptr && inputDevice != nullptr) - { snd_pcm_link (outputDevice->handle, inputDevice->handle); - } - if (inputDevice != nullptr && failed (snd_pcm_prepare (inputDevice->handle))) + if (inputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (inputDevice->handle))) return; - if (outputDevice != nullptr && failed (snd_pcm_prepare (outputDevice->handle))) + if (outputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (outputDevice->handle))) return; startThread (9); @@ -533,6 +604,25 @@ public: void close() { + if (isThreadRunning()) + { + // problem: when pulseaudio is suspended (with pasuspend) , the ALSAThread::run is just stuck in + // snd_pcm_writei -- no error, no nothing it just stays stuck. So the only way I found to exit "nicely" + // (that is without the "killing thread by force" of stopThread) , is to just call snd_pcm_close from + // here which will cause the thread to resume, and exit + signalThreadShouldExit(); + + const int callbacksToStop = numCallbacks; + + if ((! waitForThreadToExit (400)) && audioIoInProgress && numCallbacks == callbacksToStop) + { + JUCE_ALSA_LOG ("Thread is stuck in i/o.. Is pulseaudio suspended?"); + + if (outputDevice != nullptr) outputDevice->closeNow(); + if (inputDevice != nullptr) inputDevice->closeNow(); + } + } + stopThread (6000); inputDevice = nullptr; @@ -550,17 +640,21 @@ public: callback = newCallback; } - void run() + void run() override { while (! threadShouldExit()) { - if (inputDevice != nullptr) + if (inputDevice != nullptr && inputDevice->handle) { + audioIoInProgress = true; + if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize)) { - DBG ("ALSA: read failure"); + JUCE_ALSA_LOG ("Read failure"); break; } + + audioIoInProgress = false; } if (threadShouldExit()) @@ -572,7 +666,7 @@ public: if (callback != nullptr) { - callback->audioDeviceIOCallback ((const float**) inputChannelDataForCallback.getRawDataPointer(), + callback->audioDeviceIOCallback (inputChannelDataForCallback.getRawDataPointer(), inputChannelDataForCallback.size(), outputChannelDataForCallback.getRawDataPointer(), outputChannelDataForCallback.size(), @@ -585,22 +679,30 @@ public: } } - if (outputDevice != nullptr) + if (outputDevice != nullptr && outputDevice->handle) { - failed (snd_pcm_wait (outputDevice->handle, 2000)); + JUCE_ALSA_FAILED (snd_pcm_wait (outputDevice->handle, 2000)); if (threadShouldExit()) break; - failed (snd_pcm_avail_update (outputDevice->handle)); + snd_pcm_sframes_t avail = snd_pcm_avail_update (outputDevice->handle); + + if (avail < 0) + JUCE_ALSA_FAILED (snd_pcm_recover (outputDevice->handle, avail, 0)); + + audioIoInProgress = true; if (! outputDevice->writeToOutputDevice (outputChannelBuffer, bufferSize)) { - DBG ("ALSA: write failure"); + JUCE_ALSA_LOG ("write failure"); break; } + + audioIoInProgress = false; } } + audioIoInProgress = false; } int getBitDepth() const noexcept @@ -620,7 +722,7 @@ public: int bufferSize, outputLatency, inputLatency; BigInteger currentInputChans, currentOutputChans; - Array sampleRates; + Array sampleRates; StringArray channelNamesOut, channelNamesIn; AudioIODeviceCallback* callback; @@ -629,11 +731,13 @@ private: const String inputId, outputId; ScopedPointer outputDevice, inputDevice; int numCallbacks; + bool audioIoInProgress; CriticalSection callbackLock; AudioSampleBuffer inputChannelBuffer, outputChannelBuffer; - Array inputChannelDataForCallback, outputChannelDataForCallback; + Array inputChannelDataForCallback; + Array outputChannelDataForCallback; unsigned int minChansOut, maxChansOut; unsigned int minChansIn, maxChansIn; @@ -644,7 +748,7 @@ private: return false; error = snd_strerror (errorNum); - DBG ("ALSA error: " + error + "\n"); + JUCE_ALSA_LOG ("ALSA error: " << error); return true; } @@ -659,18 +763,17 @@ private: maxChansIn = 0; unsigned int dummy = 0; - getDeviceProperties (inputId, dummy, dummy, minChansIn, maxChansIn, sampleRates); - getDeviceProperties (outputId, minChansOut, maxChansOut, dummy, dummy, sampleRates); + getDeviceProperties (inputId, dummy, dummy, minChansIn, maxChansIn, sampleRates, false, true); + getDeviceProperties (outputId, minChansOut, maxChansOut, dummy, dummy, sampleRates, true, false); - unsigned int i; - for (i = 0; i < maxChansOut; ++i) + for (unsigned int i = 0; i < maxChansOut; ++i) channelNamesOut.add ("channel " + String ((int) i + 1)); - for (i = 0; i < maxChansIn; ++i) + for (unsigned int i = 0; i < maxChansIn; ++i) channelNamesIn.add ("channel " + String ((int) i + 1)); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAThread); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAThread) }; @@ -679,14 +782,15 @@ class ALSAAudioIODevice : public AudioIODevice { public: ALSAAudioIODevice (const String& deviceName, - const String& inputId_, - const String& outputId_) - : AudioIODevice (deviceName, "ALSA"), - inputId (inputId_), - outputId (outputId_), + const String& typeName, + const String& inputDeviceID, + const String& outputDeviceID) + : AudioIODevice (deviceName, typeName), + inputId (inputDeviceID), + outputId (outputDeviceID), isOpen_ (false), isStarted (false), - internal (inputId_, outputId_) + internal (inputDeviceID, outputDeviceID) { } @@ -695,31 +799,34 @@ public: close(); } - StringArray getOutputChannelNames() { return internal.channelNamesOut; } - StringArray getInputChannelNames() { return internal.channelNamesIn; } + StringArray getOutputChannelNames() override { return internal.channelNamesOut; } + StringArray getInputChannelNames() override { return internal.channelNamesIn; } - int getNumSampleRates() { return internal.sampleRates.size(); } - double getSampleRate (int index) { return internal.sampleRates [index]; } + Array getAvailableSampleRates() override { return internal.sampleRates; } - int getDefaultBufferSize() { return 512; } - int getNumBufferSizesAvailable() { return 50; } - - int getBufferSizeSamples (int index) + Array getAvailableBufferSizes() override { + Array r; int n = 16; - for (int i = 0; i < index; ++i) + + for (int i = 0; i < 50; ++i) + { + r.add (n); n += n < 64 ? 16 : (n < 512 ? 32 : (n < 1024 ? 64 : (n < 2048 ? 128 : 256))); + } - return n; + return r; } + int getDefaultBufferSize() override { return 512; } + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, - int bufferSizeSamples) + int bufferSizeSamples) override { close(); @@ -728,11 +835,13 @@ public: if (sampleRate <= 0) { - for (int i = 0; i < getNumSampleRates(); ++i) + for (int i = 0; i < internal.sampleRates.size(); ++i) { - if (getSampleRate (i) >= 44100) + double rate = internal.sampleRates[i]; + + if (rate >= 44100) { - sampleRate = getSampleRate (i); + sampleRate = rate; break; } } @@ -745,28 +854,28 @@ public: return internal.error; } - void close() + void close() override { stop(); internal.close(); isOpen_ = false; } - bool isOpen() { return isOpen_; } - bool isPlaying() { return isStarted && internal.error.isEmpty(); } - String getLastError() { return internal.error; } + bool isOpen() override { return isOpen_; } + bool isPlaying() override { return isStarted && internal.error.isEmpty(); } + String getLastError() override { return internal.error; } - int getCurrentBufferSizeSamples() { return internal.bufferSize; } - double getCurrentSampleRate() { return internal.sampleRate; } - int getCurrentBitDepth() { return internal.getBitDepth(); } + int getCurrentBufferSizeSamples() override { return internal.bufferSize; } + double getCurrentSampleRate() override { return internal.sampleRate; } + int getCurrentBitDepth() override { return internal.getBitDepth(); } - BigInteger getActiveOutputChannels() const { return internal.currentOutputChans; } - BigInteger getActiveInputChannels() const { return internal.currentInputChans; } + BigInteger getActiveOutputChannels() const override { return internal.currentOutputChans; } + BigInteger getActiveInputChannels() const override { return internal.currentInputChans; } - int getOutputLatencyInSamples() { return internal.outputLatency; } - int getInputLatencyInSamples() { return internal.inputLatency; } + int getOutputLatencyInSamples() override { return internal.outputLatency; } + int getInputLatencyInSamples() override { return internal.inputLatency; } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback) override { if (! isOpen_) callback = nullptr; @@ -779,11 +888,11 @@ public: isStarted = (callback != nullptr); } - void stop() + void stop() override { AudioIODeviceCallback* const oldCallback = internal.callback; - start (0); + start (nullptr); if (oldCallback != nullptr) oldCallback->audioDeviceStopped(); @@ -801,15 +910,23 @@ private: class ALSAAudioIODeviceType : public AudioIODeviceType { public: - //============================================================================== - ALSAAudioIODeviceType() - : AudioIODeviceType ("ALSA"), - hasScanned (false) + ALSAAudioIODeviceType (bool onlySoundcards, const String &typeName) + : AudioIODeviceType (typeName), + hasScanned (false), + listOnlySoundcards (onlySoundcards) { + #if ! JUCE_ALSA_LOGGING + snd_lib_error_set_handler (&silentErrorHandler); + #endif } ~ALSAAudioIODeviceType() { + #if ! JUCE_ALSA_LOGGING + snd_lib_error_set_handler (nullptr); + #endif + + snd_config_update_free_global(); // prevent valgrind from screaming about alsa leaks } //============================================================================== @@ -824,82 +941,176 @@ public: outputNames.clear(); outputIds.clear(); -/* void** hints = 0; - if (snd_device_name_hint (-1, "pcm", &hints) >= 0) + JUCE_ALSA_LOG ("scanForDevices()"); + + if (listOnlySoundcards) + enumerateAlsaSoundcards(); + else + enumerateAlsaPCMDevices(); + + inputNames.appendNumbersToDuplicates (false, true); + outputNames.appendNumbersToDuplicates (false, true); + } + + StringArray getDeviceNames (bool wantInputNames) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + return wantInputNames ? inputNames : outputNames; + } + + int getDefaultDeviceIndex (bool forInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int idx = (forInput ? inputIds : outputIds).indexOf ("default"); + return idx >= 0 ? idx : 0; + } + + bool hasSeparateInputsAndOutputs() const { return true; } + + int getIndexOfDevice (AudioIODevice* device, bool asInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + if (ALSAAudioIODevice* d = dynamic_cast (device)) + return asInput ? inputIds.indexOf (d->inputId) + : outputIds.indexOf (d->outputId); + + return -1; + } + + AudioIODevice* createDevice (const String& outputDeviceName, + const String& inputDeviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int inputIndex = inputNames.indexOf (inputDeviceName); + const int outputIndex = outputNames.indexOf (outputDeviceName); + + String deviceName (outputIndex >= 0 ? outputDeviceName + : inputDeviceName); + + if (inputIndex >= 0 || outputIndex >= 0) + return new ALSAAudioIODevice (deviceName, getTypeName(), + inputIds [inputIndex], + outputIds [outputIndex]); + + return nullptr; + } + +private: + //============================================================================== + StringArray inputNames, outputNames, inputIds, outputIds; + bool hasScanned, listOnlySoundcards; + + bool testDevice (const String &id, const String &outputName, const String &inputName) + { + unsigned int minChansOut = 0, maxChansOut = 0; + unsigned int minChansIn = 0, maxChansIn = 0; + Array rates; + + bool isInput = inputName.isNotEmpty(), isOutput = outputName.isNotEmpty(); + getDeviceProperties (id, minChansOut, maxChansOut, minChansIn, maxChansIn, rates, isOutput, isInput); + + isInput = maxChansIn > 0; + isOutput = maxChansOut > 0; + + if ((isInput || isOutput) && rates.size() > 0) { - for (void** hint = hints; *hint != 0; ++hint) + JUCE_ALSA_LOG ("testDevice: '" << id.toUTF8().getAddress() << "' -> isInput: " + << isInput << ", isOutput: " << isOutput); + + if (isInput) { - const String name (getHint (*hint, "NAME")); - - if (name.isNotEmpty()) - { - const String ioid (getHint (*hint, "IOID")); - - String desc (getHint (*hint, "DESC")); - if (desc.isEmpty()) - desc = name; - - desc = desc.replaceCharacters ("\n\r", " "); - - DBG ("name: " << name << "\ndesc: " << desc << "\nIO: " << ioid); - - if (ioid.isEmpty() || ioid == "Input") - { - inputNames.add (desc); - inputIds.add (name); - } - - if (ioid.isEmpty() || ioid == "Output") - { - outputNames.add (desc); - outputIds.add (name); - } - } + inputNames.add (inputName); + inputIds.add (id); } - snd_device_name_free_hint (hints); + if (isOutput) + { + outputNames.add (outputName); + outputIds.add (id); + } + + return isInput || isOutput; } -*/ + + return false; + } + + void enumerateAlsaSoundcards() + { snd_ctl_t* handle = nullptr; snd_ctl_card_info_t* info = nullptr; snd_ctl_card_info_alloca (&info); int cardNum = -1; - while (outputIds.size() + inputIds.size() <= 32) + while (outputIds.size() + inputIds.size() <= 64) { snd_card_next (&cardNum); if (cardNum < 0) break; - if (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toUTF8(), SND_CTL_NONBLOCK) >= 0) + if (JUCE_CHECKED_RESULT (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toUTF8(), SND_CTL_NONBLOCK)) >= 0) { - if (snd_ctl_card_info (handle, info) >= 0) + if (JUCE_CHECKED_RESULT (snd_ctl_card_info (handle, info)) >= 0) { String cardId (snd_ctl_card_info_get_id (info)); if (cardId.removeCharacters ("0123456789").isEmpty()) cardId = String (cardNum); + String cardName = snd_ctl_card_info_get_name (info); + + if (cardName.isEmpty()) + cardName = cardId; + int device = -1; + snd_pcm_info_t* pcmInfo; + snd_pcm_info_alloca (&pcmInfo); + for (;;) { if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0) break; - String id, name; - id << "hw:" << cardId << ',' << device; + snd_pcm_info_set_device (pcmInfo, device); - bool isInput, isOutput; - - if (testDevice (id, isInput, isOutput)) + for (int subDevice = 0, nbSubDevice = 1; subDevice < nbSubDevice; ++subDevice) { - name << snd_ctl_card_info_get_name (info); + snd_pcm_info_set_subdevice (pcmInfo, subDevice); + snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_CAPTURE); + const bool isInput = (snd_ctl_pcm_info (handle, pcmInfo) >= 0); - if (name.isEmpty()) - name = id; + snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_PLAYBACK); + const bool isOutput = (snd_ctl_pcm_info (handle, pcmInfo) >= 0); + + if (! (isInput || isOutput)) + continue; + + if (nbSubDevice == 1) + nbSubDevice = snd_pcm_info_get_subdevices_count (pcmInfo); + + String id, name; + + if (nbSubDevice == 1) + { + id << "hw:" << cardId << "," << device; + name << cardName << ", " << snd_pcm_info_get_name (pcmInfo); + } + else + { + id << "hw:" << cardId << "," << device << "," << subDevice; + name << cardName << ", " << snd_pcm_info_get_name (pcmInfo) + << " {" << snd_pcm_info_get_subdevice_name (pcmInfo) << "}"; + } + + JUCE_ALSA_LOG ("Soundcard ID: " << id << ", name: '" << name + << ", isInput:" << isInput << ", isOutput:" << isOutput << "\n"); if (isInput) { @@ -916,97 +1127,117 @@ public: } } - snd_ctl_close (handle); + JUCE_CHECKED_RESULT (snd_ctl_close (handle)); } } - - inputNames.appendNumbersToDuplicates (false, true); - outputNames.appendNumbersToDuplicates (false, true); } - StringArray getDeviceNames (bool wantInputNames) const + /* Enumerates all ALSA output devices (as output by the command aplay -L) + Does not try to open the devices (with "testDevice" for example), + so that it also finds devices that are busy and not yet available. + */ + void enumerateAlsaPCMDevices() { - jassert (hasScanned); // need to call scanForDevices() before doing this + void** hints = nullptr; - return wantInputNames ? inputNames : outputNames; + if (JUCE_CHECKED_RESULT (snd_device_name_hint (-1, "pcm", &hints)) == 0) + { + for (char** h = (char**) hints; *h; ++h) + { + const String id (hintToString (*h, "NAME")); + const String description (hintToString (*h, "DESC")); + const String ioid (hintToString (*h, "IOID")); + + JUCE_ALSA_LOG ("ID: " << id << "; desc: " << description << "; ioid: " << ioid); + + String ss = id.fromFirstOccurrenceOf ("=", false, false) + .upToFirstOccurrenceOf (",", false, false); + + if (id.isEmpty() + || id.startsWith ("default:") || id.startsWith ("sysdefault:") + || id.startsWith ("plughw:") || id == "null") + continue; + + String name (description.replace ("\n", "; ")); + + if (name.isEmpty()) + name = id; + + bool isOutput = (ioid != "Input"); + bool isInput = (ioid != "Output"); + + // alsa is stupid here, it advertises dmix and dsnoop as input/output devices, but + // opening dmix as input, or dsnoop as output will trigger errors.. + isInput = isInput && ! id.startsWith ("dmix"); + isOutput = isOutput && ! id.startsWith ("dsnoop"); + + if (isInput) + { + inputNames.add (name); + inputIds.add (id); + } + + if (isOutput) + { + outputNames.add (name); + outputIds.add (id); + } + } + + snd_device_name_free_hint (hints); + } + + // sometimes the "default" device is not listed, but it is nice to see it explicitely in the list + if (! outputIds.contains ("default")) + testDevice ("default", "Default ALSA Output", "Default ALSA Input"); + + // same for the pulseaudio plugin + if (! outputIds.contains ("pulse")) + testDevice ("pulse", "Pulseaudio output", "Pulseaudio input"); + + // make sure the default device is listed first, and followed by the pulse device (if present) + int idx = outputIds.indexOf ("pulse"); + outputIds.move (idx, 0); + outputNames.move (idx, 0); + + idx = inputIds.indexOf ("pulse"); + inputIds.move (idx, 0); + inputNames.move (idx, 0); + + idx = outputIds.indexOf ("default"); + outputIds.move (idx, 0); + outputNames.move (idx, 0); + + idx = inputIds.indexOf ("default"); + inputIds.move (idx, 0); + inputNames.move (idx, 0); } - int getDefaultDeviceIndex (bool forInput) const + static String hintToString (const void* hints, const char* type) { - jassert (hasScanned); // need to call scanForDevices() before doing this - return 0; - } - - bool hasSeparateInputsAndOutputs() const { return true; } - - int getIndexOfDevice (AudioIODevice* device, bool asInput) const - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - ALSAAudioIODevice* d = dynamic_cast (device); - if (d == nullptr) - return -1; - - return asInput ? inputIds.indexOf (d->inputId) - : outputIds.indexOf (d->outputId); - } - - AudioIODevice* createDevice (const String& outputDeviceName, - const String& inputDeviceName) - { - jassert (hasScanned); // need to call scanForDevices() before doing this - - const int inputIndex = inputNames.indexOf (inputDeviceName); - const int outputIndex = outputNames.indexOf (outputDeviceName); - - String deviceName (outputIndex >= 0 ? outputDeviceName - : inputDeviceName); - - if (inputIndex >= 0 || outputIndex >= 0) - return new ALSAAudioIODevice (deviceName, - inputIds [inputIndex], - outputIds [outputIndex]); - - return nullptr; - } - - //============================================================================== -private: - StringArray inputNames, outputNames, inputIds, outputIds; - bool hasScanned; - - static bool testDevice (const String& id, bool& isInput, bool& isOutput) - { - unsigned int minChansOut = 0, maxChansOut = 0; - unsigned int minChansIn = 0, maxChansIn = 0; - Array rates; - - getDeviceProperties (id, minChansOut, maxChansOut, minChansIn, maxChansIn, rates); - - DBG ("ALSA device: " + id - + " outs=" + String ((int) minChansOut) + "-" + String ((int) maxChansOut) - + " ins=" + String ((int) minChansIn) + "-" + String ((int) maxChansIn) - + " rates=" + String (rates.size())); - - isInput = maxChansIn > 0; - isOutput = maxChansOut > 0; - - return (isInput || isOutput) && rates.size() > 0; - } - - /*static String getHint (void* hint, const char* type) - { - char* const n = snd_device_name_get_hint (hint, type); - const String s ((const char*) n); - free (n); + char* const hint = snd_device_name_get_hint (hints, type); + const String s (String::fromUTF8 (hint)); + ::free (hint); return s; - }*/ + } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAAudioIODeviceType) }; +} + //============================================================================== +AudioIODeviceType* createAudioIODeviceType_ALSA_Soundcards() +{ + return new ALSAAudioIODeviceType (true, "ALSA HW"); +} + +AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices() +{ + return new ALSAAudioIODeviceType (false, "ALSA"); +} + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { - return new ALSAAudioIODeviceType(); + return createAudioIODeviceType_ALSA_PCMDevices(); } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_AudioCDReader.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_AudioCDReader.cpp index 8ff14ca..a645333 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_AudioCDReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_AudioCDReader.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp index c48be0b..38ad64d 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -35,20 +34,19 @@ static void* juce_loadJackFunction (const char* const name) } #define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \ - typedef return_type (*fn_name##_ptr_t)argument_types; \ - return_type fn_name argument_types { \ - static fn_name##_ptr_t fn = nullptr; \ - if (fn == nullptr) { fn = (fn_name##_ptr_t)juce_loadJackFunction(#fn_name); } \ - if (fn) return (*fn)arguments; \ - else return nullptr; \ + return_type fn_name argument_types \ + { \ + typedef return_type (*fn_type) argument_types; \ + static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ + return (fn != nullptr) ? ((*fn) arguments) : (return_type) 0; \ } #define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \ - typedef void (*fn_name##_ptr_t)argument_types; \ - void fn_name argument_types { \ - static fn_name##_ptr_t fn = nullptr; \ - if (fn == nullptr) { fn = (fn_name##_ptr_t)juce_loadJackFunction(#fn_name); } \ - if (fn) (*fn)arguments; \ + void fn_name argument_types \ + { \ + typedef void (*fn_type) argument_types; \ + static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ + if (fn != nullptr) (*fn) arguments; \ } //============================================================================== @@ -67,13 +65,13 @@ JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)); JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)); JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)); -JUCE_DECL_JACK_FUNCTION (int, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)); +JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)); JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)); JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port)); JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name)); #if JUCE_DEBUG - #define JACK_LOGGING_ENABLED 1 + #define JACK_LOGGING_ENABLED 1 #endif #if JACK_LOGGING_ENABLED @@ -84,38 +82,77 @@ namespace std::cerr << s << std::endl; } - void dumpJackErrorMessage (const jack_status_t status) + const char* getJackErrorMessage (const jack_status_t status) { - if (status & JackServerFailed || status & JackServerError) jack_Log ("Unable to connect to JACK server"); - if (status & JackVersionError) jack_Log ("Client's protocol version does not match"); - if (status & JackInvalidOption) jack_Log ("The operation contained an invalid or unsupported option"); - if (status & JackNameNotUnique) jack_Log ("The desired client name was not unique"); - if (status & JackNoSuchClient) jack_Log ("Requested client does not exist"); - if (status & JackInitFailure) jack_Log ("Unable to initialize client"); + if (status & JackServerFailed + || status & JackServerError) return "Unable to connect to JACK server"; + if (status & JackVersionError) return "Client's protocol version does not match"; + if (status & JackInvalidOption) return "The operation contained an invalid or unsupported option"; + if (status & JackNameNotUnique) return "The desired client name was not unique"; + if (status & JackNoSuchClient) return "Requested client does not exist"; + if (status & JackInitFailure) return "Unable to initialize client"; + return nullptr; } } + #define JUCE_JACK_LOG_STATUS(x) { if (const char* m = getJackErrorMessage (x)) jack_Log (m); } + #define JUCE_JACK_LOG(x) jack_Log(x) #else - #define dumpJackErrorMessage(a) {} - #define jack_Log(...) {} + #define JUCE_JACK_LOG_STATUS(x) {} + #define JUCE_JACK_LOG(x) {} #endif //============================================================================== #ifndef JUCE_JACK_CLIENT_NAME - #define JUCE_JACK_CLIENT_NAME "JuceJack" + #define JUCE_JACK_CLIENT_NAME "JUCEJack" #endif +struct JackPortIterator +{ + JackPortIterator (jack_client_t* const client, const bool forInput) + : ports (nullptr), index (-1) + { + if (client != nullptr) + ports = juce::jack_get_ports (client, nullptr, nullptr, + forInput ? JackPortIsOutput : JackPortIsInput); + // (NB: This looks like it's the wrong way round, but it is correct!) + } + + ~JackPortIterator() + { + ::free (ports); + } + + bool next() + { + if (ports == nullptr || ports [index + 1] == nullptr) + return false; + + name = CharPointer_UTF8 (ports[++index]); + clientName = name.upToFirstOccurrenceOf (":", false, false); + return true; + } + + const char** ports; + int index; + String name; + String clientName; +}; + +class JackAudioIODeviceType; +static Array activeDeviceTypes; + //============================================================================== class JackAudioIODevice : public AudioIODevice { public: JackAudioIODevice (const String& deviceName, - const String& inputId_, - const String& outputId_) + const String& inId, + const String& outId) : AudioIODevice (deviceName, "JACK"), - inputId (inputId_), - outputId (outputId_), - isOpen_ (false), + inputId (inId), + outputId (outId), + deviceIsOpen (false), callback (nullptr), totalNumberOfInputChannels (0), totalNumberOfOutputChannels (0) @@ -125,9 +162,9 @@ public: jack_status_t status; client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status); - if (client == 0) + if (client == nullptr) { - dumpJackErrorMessage (status); + JUCE_JACK_LOG_STATUS (status); } else { @@ -135,7 +172,7 @@ public: // open input ports const StringArray inputChannels (getInputChannelNames()); - for (int i = 0; i < inputChannels.size(); i++) + for (int i = 0; i < inputChannels.size(); ++i) { String inputName; inputName << "in_" << ++totalNumberOfInputChannels; @@ -146,7 +183,7 @@ public: // open output ports const StringArray outputChannels (getOutputChannelNames()); - for (int i = 0; i < outputChannels.size (); i++) + for (int i = 0; i < outputChannels.size (); ++i) { String outputName; outputName << "out_" << ++totalNumberOfOutputChannels; @@ -163,129 +200,117 @@ public: ~JackAudioIODevice() { close(); - if (client != 0) + if (client != nullptr) { juce::jack_client_close (client); - client = 0; + client = nullptr; } } StringArray getChannelNames (bool forInput) const { StringArray names; - const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ - forInput ? JackPortIsInput : JackPortIsOutput); - if (ports != 0) - { - int j = 0; - while (ports[j] != 0) - { - const String portName (ports [j++]); - - if (portName.upToFirstOccurrenceOf (":", false, false) == getName()) - names.add (portName.fromFirstOccurrenceOf (":", false, false)); - } - - free (ports); - } + for (JackPortIterator i (client, forInput); i.next();) + if (i.clientName == getName()) + names.add (i.name.fromFirstOccurrenceOf (":", false, false)); return names; } - StringArray getOutputChannelNames() { return getChannelNames (false); } - StringArray getInputChannelNames() { return getChannelNames (true); } - int getNumSampleRates() { return client != 0 ? 1 : 0; } - double getSampleRate (int index) { return client != 0 ? juce::jack_get_sample_rate (client) : 0; } - int getNumBufferSizesAvailable() { return client != 0 ? 1 : 0; } - int getBufferSizeSamples (int index) { return getDefaultBufferSize(); } - int getDefaultBufferSize() { return client != 0 ? juce::jack_get_buffer_size (client) : 0; } + StringArray getOutputChannelNames() override { return getChannelNames (false); } + StringArray getInputChannelNames() override { return getChannelNames (true); } + + Array getAvailableSampleRates() override + { + Array rates; + + if (client != nullptr) + rates.add (juce::jack_get_sample_rate (client)); + + return rates; + } + + Array getAvailableBufferSizes() override + { + Array sizes; + + if (client != nullptr) + sizes.add (juce::jack_get_buffer_size (client)); + + return sizes; + } + + int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); } + int getCurrentBufferSizeSamples() override { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; } + double getCurrentSampleRate() override { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; } + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, int bufferSizeSamples) + double /* sampleRate */, int /* bufferSizeSamples */) override { - if (client == 0) + if (client == nullptr) { lastError = "No JACK client running"; return lastError; } - lastError = String::empty; + lastError.clear(); close(); juce::jack_set_process_callback (client, processCallback, this); + juce::jack_set_port_connect_callback (client, portConnectCallback, this); juce::jack_on_shutdown (client, shutdownCallback, this); juce::jack_activate (client); - isOpen_ = true; + deviceIsOpen = true; if (! inputChannels.isZero()) { - const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsOutput); - - if (ports != 0) + for (JackPortIterator i (client, true); i.next();) { - const int numInputChannels = inputChannels.getHighestBit() + 1; - - for (int i = 0; i < numInputChannels; ++i) + if (inputChannels [i.index] && i.clientName == getName()) { - const String portName (ports[i]); - - if (inputChannels[i] && portName.upToFirstOccurrenceOf (":", false, false) == getName()) - { - int error = juce::jack_connect (client, ports[i], juce::jack_port_name ((jack_port_t*) inputPorts[i])); - if (error != 0) - jack_Log ("Cannot connect input port " + String (i) + " (" + String (ports[i]) + "), error " + String (error)); - } + int error = juce::jack_connect (client, i.ports[i.index], juce::jack_port_name ((jack_port_t*) inputPorts[i.index])); + if (error != 0) + JUCE_JACK_LOG ("Cannot connect input port " + String (i.index) + " (" + i.name + "), error " + String (error)); } - - free (ports); } } if (! outputChannels.isZero()) { - const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsInput); - - if (ports != 0) + for (JackPortIterator i (client, false); i.next();) { - const int numOutputChannels = outputChannels.getHighestBit() + 1; - - for (int i = 0; i < numOutputChannels; ++i) + if (outputChannels [i.index] && i.clientName == getName()) { - const String portName (ports[i]); - - if (outputChannels[i] && portName.upToFirstOccurrenceOf (":", false, false) == getName()) - { - int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i]), ports[i]); - if (error != 0) - jack_Log ("Cannot connect output port " + String (i) + " (" + String (ports[i]) + "), error " + String (error)); - } + int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i.index]), i.ports[i.index]); + if (error != 0) + JUCE_JACK_LOG ("Cannot connect output port " + String (i.index) + " (" + i.name + "), error " + String (error)); } - - free (ports); } } return lastError; } - void close() + void close() override { stop(); - if (client != 0) + if (client != nullptr) { juce::jack_deactivate (client); - juce::jack_set_process_callback (client, processCallback, 0); - juce::jack_on_shutdown (client, shutdownCallback, 0); + juce::jack_set_process_callback (client, processCallback, nullptr); + juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr); + juce::jack_on_shutdown (client, shutdownCallback, nullptr); } - isOpen_ = false; + deviceIsOpen = false; } - void start (AudioIODeviceCallback* newCallback) + void start (AudioIODeviceCallback* newCallback) override { - if (isOpen_ && newCallback != callback) + if (deviceIsOpen && newCallback != callback) { if (newCallback != nullptr) newCallback->audioDeviceAboutToStart (this); @@ -302,41 +327,20 @@ public: } } - void stop() + void stop() override { - start (0); + start (nullptr); } - bool isOpen() { return isOpen_; } - bool isPlaying() { return callback != nullptr; } - int getCurrentBufferSizeSamples() { return getBufferSizeSamples (0); } - double getCurrentSampleRate() { return getSampleRate (0); } - int getCurrentBitDepth() { return 32; } - String getLastError() { return lastError; } + bool isOpen() override { return deviceIsOpen; } + bool isPlaying() override { return callback != nullptr; } + int getCurrentBitDepth() override { return 32; } + String getLastError() override { return lastError; } - BigInteger getActiveOutputChannels() const - { - BigInteger outputBits; + BigInteger getActiveOutputChannels() const override { return activeOutputChannels; } + BigInteger getActiveInputChannels() const override { return activeInputChannels; } - for (int i = 0; i < outputPorts.size(); i++) - if (juce::jack_port_connected ((jack_port_t*) outputPorts [i])) - outputBits.setBit (i); - - return outputBits; - } - - BigInteger getActiveInputChannels() const - { - BigInteger inputBits; - - for (int i = 0; i < inputPorts.size(); i++) - if (juce::jack_port_connected ((jack_port_t*) inputPorts [i])) - inputBits.setBit (i); - - return inputBits; - } - - int getOutputLatencyInSamples() + int getOutputLatencyInSamples() override { int latency = 0; @@ -346,7 +350,7 @@ public: return latency; } - int getInputLatencyInSamples() + int getInputLatencyInSamples() override { int latency = 0; @@ -361,72 +365,106 @@ public: private: void process (const int numSamples) { - int i, numActiveInChans = 0, numActiveOutChans = 0; + int numActiveInChans = 0, numActiveOutChans = 0; - for (i = 0; i < totalNumberOfInputChannels; ++i) + for (int i = 0; i < totalNumberOfInputChannels; ++i) { - jack_default_audio_sample_t* in - = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples); - - if (in != nullptr) - inChans [numActiveInChans++] = (float*) in; + if (activeInputChannels[i]) + if (jack_default_audio_sample_t* in + = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples)) + inChans [numActiveInChans++] = (float*) in; } - for (i = 0; i < totalNumberOfOutputChannels; ++i) + for (int i = 0; i < totalNumberOfOutputChannels; ++i) { - jack_default_audio_sample_t* out - = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples); - - if (out != nullptr) - outChans [numActiveOutChans++] = (float*) out; + if (activeOutputChannels[i]) + if (jack_default_audio_sample_t* out + = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples)) + outChans [numActiveOutChans++] = (float*) out; } const ScopedLock sl (callbackLock); if (callback != nullptr) { - callback->audioDeviceIOCallback (const_cast (inChans.getData()), numActiveInChans, - outChans, numActiveOutChans, numSamples); + if ((numActiveInChans + numActiveOutChans) > 0) + callback->audioDeviceIOCallback (const_cast (inChans.getData()), numActiveInChans, + outChans, numActiveOutChans, numSamples); } else { - for (i = 0; i < numActiveOutChans; ++i) + for (int i = 0; i < numActiveOutChans; ++i) zeromem (outChans[i], sizeof (float) * numSamples); } } static int processCallback (jack_nframes_t nframes, void* callbackArgument) { - if (callbackArgument != 0) + if (callbackArgument != nullptr) ((JackAudioIODevice*) callbackArgument)->process (nframes); return 0; } - static void threadInitCallback (void* callbackArgument) + void updateActivePorts() { - jack_Log ("JackAudioIODevice::initialise"); + BigInteger newOutputChannels, newInputChannels; + + for (int i = 0; i < outputPorts.size(); ++i) + if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i))) + newOutputChannels.setBit (i); + + for (int i = 0; i < inputPorts.size(); ++i) + if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i))) + newInputChannels.setBit (i); + + if (newOutputChannels != activeOutputChannels + || newInputChannels != activeInputChannels) + { + AudioIODeviceCallback* const oldCallback = callback; + + stop(); + + activeOutputChannels = newOutputChannels; + activeInputChannels = newInputChannels; + + if (oldCallback != nullptr) + start (oldCallback); + + sendDeviceChangedCallback(); + } + } + + static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg) + { + if (JackAudioIODevice* device = static_cast (arg)) + device->updateActivePorts(); + } + + static void threadInitCallback (void* /* callbackArgument */) + { + JUCE_JACK_LOG ("JackAudioIODevice::initialise"); } static void shutdownCallback (void* callbackArgument) { - jack_Log ("JackAudioIODevice::shutdown"); + JUCE_JACK_LOG ("JackAudioIODevice::shutdown"); - JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument; - - if (device != nullptr) + if (JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument) { - device->client = 0; + device->client = nullptr; device->close(); } } static void errorCallback (const char* msg) { - jack_Log ("JackAudioIODevice::errorCallback " + String (msg)); + JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg)); } - bool isOpen_; + static void sendDeviceChangedCallback(); + + bool deviceIsOpen; jack_client_t* client; String lastError; AudioIODeviceCallback* callback; @@ -436,6 +474,7 @@ private: int totalNumberOfInputChannels; int totalNumberOfOutputChannels; Array inputPorts, outputPorts; + BigInteger activeInputChannels, activeOutputChannels; }; @@ -443,14 +482,18 @@ private: class JackAudioIODeviceType : public AudioIODeviceType { public: - //============================================================================== JackAudioIODeviceType() : AudioIODeviceType ("JACK"), hasScanned (false) { + activeDeviceTypes.add (this); + } + + ~JackAudioIODeviceType() + { + activeDeviceTypes.removeFirstMatchingValue (this); } - //============================================================================== void scanForDevices() { hasScanned = true; @@ -459,74 +502,41 @@ public: outputNames.clear(); outputIds.clear(); - if (juce_libjackHandle == nullptr) - { - juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); + if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY); + if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); + if (juce_libjackHandle == nullptr) return; - if (juce_libjackHandle == nullptr) - return; - } + jack_status_t status; // open a dummy client - jack_status_t status; - jack_client_t* client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status); - - if (client == 0) - { - dumpJackErrorMessage (status); - } - else + if (jack_client_t* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status)) { // scan for output devices - const char** ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsOutput); - - if (ports != 0) + for (JackPortIterator i (client, false); i.next();) { - int j = 0; - while (ports[j] != 0) + if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.clientName)) { - String clientName (ports[j]); - clientName = clientName.upToFirstOccurrenceOf (":", false, false); - - if (clientName != String (JUCE_JACK_CLIENT_NAME) - && ! inputNames.contains (clientName)) - { - inputNames.add (clientName); - inputIds.add (ports [j]); - } - - ++j; + inputNames.add (i.clientName); + inputIds.add (i.ports [i.index]); } - - free (ports); } // scan for input devices - ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsInput); - - if (ports != 0) + for (JackPortIterator i (client, true); i.next();) { - int j = 0; - while (ports[j] != 0) + if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.clientName)) { - String clientName (ports[j]); - clientName = clientName.upToFirstOccurrenceOf (":", false, false); - - if (clientName != String (JUCE_JACK_CLIENT_NAME) - && ! outputNames.contains (clientName)) - { - outputNames.add (clientName); - outputIds.add (ports [j]); - } - - ++j; + outputNames.add (i.clientName); + outputIds.add (i.ports [i.index]); } - - free (ports); } juce::jack_client_close (client); } + else + { + JUCE_JACK_LOG_STATUS (status); + } } StringArray getDeviceNames (bool wantInputNames) const @@ -535,7 +545,7 @@ public: return wantInputNames ? inputNames : outputNames; } - int getDefaultDeviceIndex (bool forInput) const + int getDefaultDeviceIndex (bool /* forInput */) const { jassert (hasScanned); // need to call scanForDevices() before doing this return 0; @@ -547,12 +557,11 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - JackAudioIODevice* d = dynamic_cast (device); - if (d == 0) - return -1; + if (JackAudioIODevice* d = dynamic_cast (device)) + return asInput ? inputIds.indexOf (d->inputId) + : outputIds.indexOf (d->outputId); - return asInput ? inputIds.indexOf (d->inputId) - : outputIds.indexOf (d->outputId); + return -1; } AudioIODevice* createDevice (const String& outputDeviceName, @@ -572,14 +581,22 @@ public: return nullptr; } - //============================================================================== + void portConnectionChange() { callDeviceChangeListeners(); } + private: StringArray inputNames, outputNames, inputIds, outputIds; bool hasScanned; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType) }; +void JackAudioIODevice::sendDeviceChangedCallback() +{ + for (int i = activeDeviceTypes.size(); --i >= 0;) + if (JackAudioIODeviceType* d = activeDeviceTypes[i]) + d->portConnectionChange(); +} + //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp index 8a7c4c5..a6799f3 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -37,163 +36,387 @@ //============================================================================== namespace { - snd_seq_t* iterateMidiClient (snd_seq_t* seqHandle, - snd_seq_client_info_t* clientInfo, - const bool forInput, - StringArray& deviceNamesFound, - const int deviceIndexToOpen) + +class AlsaPortAndCallback; + +//============================================================================== +class AlsaClient : public ReferenceCountedObject +{ +public: + typedef ReferenceCountedObjectPtr Ptr; + + AlsaClient (bool forInput) + : input (forInput), handle (nullptr) { - snd_seq_t* returnedHandle = nullptr; + snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT + : SND_SEQ_OPEN_OUTPUT, 0); + } - snd_seq_port_info_t* portInfo; - if (snd_seq_port_info_malloc (&portInfo) == 0) + ~AlsaClient() + { + if (handle != nullptr) { - int numPorts = snd_seq_client_info_get_num_ports (clientInfo); - const int client = snd_seq_client_info_get_client (clientInfo); + snd_seq_close (handle); + handle = nullptr; + } - snd_seq_port_info_set_client (portInfo, client); - snd_seq_port_info_set_port (portInfo, -1); + jassert (activeCallbacks.size() == 0); - while (--numPorts >= 0) + if (inputThread) + { + inputThread->stopThread (3000); + inputThread = nullptr; + } + } + + bool isInput() const noexcept { return input; } + + void setName (const String& name) + { + snd_seq_set_client_name (handle, name.toUTF8()); + } + + void registerCallback (AlsaPortAndCallback* cb) + { + if (cb != nullptr) + { { - if (snd_seq_query_next_port (seqHandle, portInfo) == 0 - && (snd_seq_port_info_get_capability (portInfo) - & (forInput ? SND_SEQ_PORT_CAP_READ - : SND_SEQ_PORT_CAP_WRITE)) != 0) + const ScopedLock sl (callbackLock); + activeCallbacks.add (cb); + + if (inputThread == nullptr) + inputThread = new MidiInputThread (*this); + } + + inputThread->startThread(); + } + } + + void unregisterCallback (AlsaPortAndCallback* cb) + { + const ScopedLock sl (callbackLock); + + jassert (activeCallbacks.contains (cb)); + activeCallbacks.removeAllInstancesOf (cb); + + if (activeCallbacks.size() == 0 && inputThread->isThreadRunning()) + inputThread->signalThreadShouldExit(); + } + + void handleIncomingMidiMessage (const MidiMessage& message, int port); + + snd_seq_t* get() const noexcept { return handle; } + +private: + bool input; + snd_seq_t* handle; + + Array activeCallbacks; + CriticalSection callbackLock; + + //============================================================================== + class MidiInputThread : public Thread + { + public: + MidiInputThread (AlsaClient& c) + : Thread ("Juce MIDI Input"), client (c) + { + jassert (client.input && client.get() != nullptr); + } + + void run() override + { + const int maxEventSize = 16 * 1024; + snd_midi_event_t* midiParser; + snd_seq_t* seqHandle = client.get(); + + if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) + { + const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); + HeapBlock pfd (numPfds); + snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); + + HeapBlock buffer (maxEventSize); + + while (! threadShouldExit()) { - deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); - - if (deviceNamesFound.size() == deviceIndexToOpen + 1) + if (poll (pfd, numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call { - const int sourcePort = snd_seq_port_info_get_port (portInfo); - const int sourceClient = snd_seq_client_info_get_client (clientInfo); + if (threadShouldExit()) + break; - if (sourcePort != -1) + snd_seq_nonblock (seqHandle, 1); + + do { - if (forInput) + snd_seq_event_t* inputEvent = nullptr; + + if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) { - snd_seq_set_client_name (seqHandle, JUCE_ALSA_MIDI_INPUT_NAME); + // xxx what about SYSEXes that are too big for the buffer? + const int numBytes = snd_midi_event_decode (midiParser, buffer, + maxEventSize, inputEvent); - const int portId = snd_seq_create_simple_port (seqHandle, "Juce Midi In Port", - SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, - SND_SEQ_PORT_TYPE_MIDI_GENERIC); + snd_midi_event_reset_decode (midiParser); - snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort); + if (numBytes > 0) + { + const MidiMessage message ((const uint8*) buffer, numBytes, + Time::getMillisecondCounter() * 0.001); + + client.handleIncomingMidiMessage (message, inputEvent->dest.port); + } + + snd_seq_free_event (inputEvent); } - else - { - snd_seq_set_client_name (seqHandle, JUCE_ALSA_MIDI_OUTPUT_NAME); - - const int portId = snd_seq_create_simple_port (seqHandle, "Juce Midi Out Port", - SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_MIDI_GENERIC); - - snd_seq_connect_to (seqHandle, portId, sourceClient, sourcePort); - } - - returnedHandle = seqHandle; } + while (snd_seq_event_input_pending (seqHandle, 0) > 0); + } + } + + snd_midi_event_free (midiParser); + } + }; + + private: + AlsaClient& client; + }; + + ScopedPointer inputThread; +}; + + +static AlsaClient::Ptr globalAlsaSequencerIn() +{ + static AlsaClient::Ptr global (new AlsaClient (true)); + return global; +} + +static AlsaClient::Ptr globalAlsaSequencerOut() +{ + static AlsaClient::Ptr global (new AlsaClient (false)); + return global; +} + +static AlsaClient::Ptr globalAlsaSequencer (bool input) +{ + return input ? globalAlsaSequencerIn() + : globalAlsaSequencerOut(); +} + +//============================================================================== +// represents an input or output port of the supplied AlsaClient +class AlsaPort +{ +public: + AlsaPort() noexcept : portId (-1) {} + AlsaPort (const AlsaClient::Ptr& c, int port) noexcept : client (c), portId (port) {} + + void createPort (const AlsaClient::Ptr& c, const String& name, bool forInput) + { + client = c; + + if (snd_seq_t* handle = client->get()) + portId = snd_seq_create_simple_port (handle, name.toUTF8(), + forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) + : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), + SND_SEQ_PORT_TYPE_MIDI_GENERIC); + } + + void deletePort() + { + if (isValid()) + { + snd_seq_delete_simple_port (client->get(), portId); + portId = -1; + } + } + + void connectWith (int sourceClient, int sourcePort) + { + if (client->isInput()) + snd_seq_connect_from (client->get(), portId, sourceClient, sourcePort); + else + snd_seq_connect_to (client->get(), portId, sourceClient, sourcePort); + } + + bool isValid() const noexcept + { + return client != nullptr && client->get() != nullptr && portId >= 0; + } + + AlsaClient::Ptr client; + int portId; +}; + +//============================================================================== +class AlsaPortAndCallback +{ +public: + AlsaPortAndCallback (AlsaPort p, MidiInput* in, MidiInputCallback* cb) + : port (p), midiInput (in), callback (cb), callbackEnabled (false) + { + } + + ~AlsaPortAndCallback() + { + enableCallback (false); + port.deletePort(); + } + + void enableCallback (bool enable) + { + if (callbackEnabled != enable) + { + callbackEnabled = enable; + + if (enable) + port.client->registerCallback (this); + else + port.client->unregisterCallback (this); + } + } + + void handleIncomingMidiMessage (const MidiMessage& message) const + { + callback->handleIncomingMidiMessage (midiInput, message); + } + +private: + AlsaPort port; + MidiInput* midiInput; + MidiInputCallback* callback; + bool callbackEnabled; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlsaPortAndCallback) +}; + +void AlsaClient::handleIncomingMidiMessage (const MidiMessage& message, int port) +{ + const ScopedLock sl (callbackLock); + + if (AlsaPortAndCallback* const cb = activeCallbacks[port]) + cb->handleIncomingMidiMessage (message); +} + +//============================================================================== +static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq, + snd_seq_client_info_t* clientInfo, + const bool forInput, + StringArray& deviceNamesFound, + const int deviceIndexToOpen) +{ + AlsaPort port; + + snd_seq_t* seqHandle = seq->get(); + snd_seq_port_info_t* portInfo = nullptr; + + if (snd_seq_port_info_malloc (&portInfo) == 0) + { + int numPorts = snd_seq_client_info_get_num_ports (clientInfo); + const int client = snd_seq_client_info_get_client (clientInfo); + + snd_seq_port_info_set_client (portInfo, client); + snd_seq_port_info_set_port (portInfo, -1); + + while (--numPorts >= 0) + { + if (snd_seq_query_next_port (seqHandle, portInfo) == 0 + && (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_READ + : SND_SEQ_PORT_CAP_WRITE)) != 0) + { + deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); + + if (deviceNamesFound.size() == deviceIndexToOpen + 1) + { + const int sourcePort = snd_seq_port_info_get_port (portInfo); + const int sourceClient = snd_seq_client_info_get_client (clientInfo); + + if (sourcePort != -1) + { + const String name (forInput ? JUCE_ALSA_MIDI_INPUT_NAME + : JUCE_ALSA_MIDI_OUTPUT_NAME); + seq->setName (name); + port.createPort (seq, name, forInput); + port.connectWith (sourceClient, sourcePort); } } } - - snd_seq_port_info_free (portInfo); } - return returnedHandle; + snd_seq_port_info_free (portInfo); } - snd_seq_t* iterateMidiDevices (const bool forInput, - StringArray& deviceNamesFound, - const int deviceIndexToOpen) - { - snd_seq_t* returnedHandle = nullptr; - snd_seq_t* seqHandle = nullptr; - - if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT - : SND_SEQ_OPEN_OUTPUT, 0) == 0) - { - snd_seq_system_info_t* systemInfo = nullptr; - snd_seq_client_info_t* clientInfo = nullptr; - - if (snd_seq_system_info_malloc (&systemInfo) == 0) - { - if (snd_seq_system_info (seqHandle, systemInfo) == 0 - && snd_seq_client_info_malloc (&clientInfo) == 0) - { - int numClients = snd_seq_system_info_get_cur_clients (systemInfo); - - while (--numClients >= 0 && returnedHandle == 0) - if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) - returnedHandle = iterateMidiClient (seqHandle, clientInfo, - forInput, deviceNamesFound, - deviceIndexToOpen); - - snd_seq_client_info_free (clientInfo); - } - - snd_seq_system_info_free (systemInfo); - } - - if (returnedHandle == 0) - snd_seq_close (seqHandle); - } - - deviceNamesFound.appendNumbersToDuplicates (true, true); - - return returnedHandle; - } - - snd_seq_t* createMidiDevice (const bool forInput, const String& deviceNameToOpen) - { - snd_seq_t* seqHandle = nullptr; - - if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT - : SND_SEQ_OPEN_OUTPUT, 0) == 0) - { - snd_seq_set_client_name (seqHandle, - (deviceNameToOpen + (forInput ? " Input" : " Output")).toUTF8()); - - const int portId - = snd_seq_create_simple_port (seqHandle, - forInput ? "in" - : "out", - forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) - : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), - forInput ? SND_SEQ_PORT_TYPE_APPLICATION - : SND_SEQ_PORT_TYPE_MIDI_GENERIC); - - if (portId < 0) - { - snd_seq_close (seqHandle); - seqHandle = nullptr; - } - } - - return seqHandle; - } + return port; } +static AlsaPort iterateMidiDevices (const bool forInput, + StringArray& deviceNamesFound, + const int deviceIndexToOpen) +{ + AlsaPort port; + const AlsaClient::Ptr client (globalAlsaSequencer (forInput)); + + if (snd_seq_t* const seqHandle = client->get()) + { + snd_seq_system_info_t* systemInfo = nullptr; + snd_seq_client_info_t* clientInfo = nullptr; + + if (snd_seq_system_info_malloc (&systemInfo) == 0) + { + if (snd_seq_system_info (seqHandle, systemInfo) == 0 + && snd_seq_client_info_malloc (&clientInfo) == 0) + { + int numClients = snd_seq_system_info_get_cur_clients (systemInfo); + + while (--numClients >= 0 && ! port.isValid()) + if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) + port = iterateMidiClient (client, clientInfo, forInput, + deviceNamesFound, deviceIndexToOpen); + + snd_seq_client_info_free (clientInfo); + } + + snd_seq_system_info_free (systemInfo); + } + + } + + deviceNamesFound.appendNumbersToDuplicates (true, true); + + return port; +} + +AlsaPort createMidiDevice (const bool forInput, const String& deviceNameToOpen) +{ + AlsaPort port; + AlsaClient::Ptr client (new AlsaClient (forInput)); + + if (client->get()) + { + client->setName (deviceNameToOpen + (forInput ? " Input" : " Output")); + port.createPort (client, forInput ? "in" : "out", forInput); + } + + return port; +} //============================================================================== class MidiOutputDevice { public: - MidiOutputDevice (MidiOutput* const midiOutput_, - snd_seq_t* const seqHandle_) - : - midiOutput (midiOutput_), - seqHandle (seqHandle_), + MidiOutputDevice (MidiOutput* const output, const AlsaPort& p) + : midiOutput (output), port (p), maxEventSize (16 * 1024) { - jassert (seqHandle != 0 && midiOutput != 0); + jassert (port.isValid() && midiOutput != nullptr); snd_midi_event_new (maxEventSize, &midiParser); } ~MidiOutputDevice() { snd_midi_event_free (midiParser); - snd_seq_close (seqHandle); + port.deletePort(); } void sendMessageNow (const MidiMessage& message) @@ -208,30 +431,42 @@ public: snd_seq_event_t event; snd_seq_ev_clear (&event); - snd_midi_event_encode (midiParser, - message.getRawData(), - message.getRawDataSize(), - &event); + long numBytes = (long) message.getRawDataSize(); + const uint8* data = message.getRawData(); - snd_midi_event_reset_encode (midiParser); + snd_seq_t* seqHandle = port.client->get(); - snd_seq_ev_set_source (&event, 0); - snd_seq_ev_set_subs (&event); - snd_seq_ev_set_direct (&event); + while (numBytes > 0) + { + const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event); + if (numSent <= 0) + break; + + numBytes -= numSent; + data += numSent; + + snd_seq_ev_set_source (&event, 0); + snd_seq_ev_set_subs (&event); + snd_seq_ev_set_direct (&event); + + snd_seq_event_output (seqHandle, &event); + } - snd_seq_event_output (seqHandle, &event); snd_seq_drain_output (seqHandle); + snd_midi_event_reset_encode (midiParser); } private: MidiOutput* const midiOutput; - snd_seq_t* const seqHandle; + AlsaPort port; snd_midi_event_t* midiParser; int maxEventSize; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice); }; +} // namespace + StringArray MidiOutput::getDevices() { StringArray devices; @@ -249,12 +484,12 @@ MidiOutput* MidiOutput::openDevice (int deviceIndex) MidiOutput* newDevice = nullptr; StringArray devices; - snd_seq_t* const handle = iterateMidiDevices (false, devices, deviceIndex); + AlsaPort port (iterateMidiDevices (false, devices, deviceIndex)); - if (handle != 0) + if (port.isValid()) { newDevice = new MidiOutput(); - newDevice->internal = new MidiOutputDevice (newDevice, handle); + newDevice->internal = new MidiOutputDevice (newDevice, port); } return newDevice; @@ -264,12 +499,12 @@ MidiOutput* MidiOutput::createNewDevice (const String& deviceName) { MidiOutput* newDevice = nullptr; - snd_seq_t* const handle = createMidiDevice (false, deviceName); + AlsaPort port (createMidiDevice (false, deviceName)); - if (handle != 0) + if (port.isValid()) { newDevice = new MidiOutput(); - newDevice->internal = new MidiOutputDevice (newDevice, handle); + newDevice->internal = new MidiOutputDevice (newDevice, port); } return newDevice; @@ -277,118 +512,36 @@ MidiOutput* MidiOutput::createNewDevice (const String& deviceName) MidiOutput::~MidiOutput() { - delete static_cast (internal); + stopBackgroundThread(); + + delete static_cast (internal); } void MidiOutput::sendMessageNow (const MidiMessage& message) { - static_cast (internal)->sendMessageNow (message); + static_cast (internal)->sendMessageNow (message); } - //============================================================================== -class MidiInputThread : public Thread -{ -public: - MidiInputThread (MidiInput* const midiInput_, - snd_seq_t* const seqHandle_, - MidiInputCallback* const callback_) - : Thread ("Juce MIDI Input"), - midiInput (midiInput_), - seqHandle (seqHandle_), - callback (callback_) - { - jassert (seqHandle != 0 && callback != 0 && midiInput != 0); - } - - ~MidiInputThread() - { - snd_seq_close (seqHandle); - } - - void run() - { - const int maxEventSize = 16 * 1024; - snd_midi_event_t* midiParser; - - if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) - { - HeapBlock buffer (maxEventSize); - - const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); - struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd)); - - snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); - - while (! threadShouldExit()) - { - if (poll (pfd, numPfds, 500) > 0) - { - snd_seq_event_t* inputEvent = nullptr; - - snd_seq_nonblock (seqHandle, 1); - - do - { - if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) - { - // xxx what about SYSEXes that are too big for the buffer? - const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent); - - snd_midi_event_reset_decode (midiParser); - - if (numBytes > 0) - { - const MidiMessage message ((const uint8*) buffer, - numBytes, - Time::getMillisecondCounter() * 0.001); - - - callback->handleIncomingMidiMessage (midiInput, message); - } - - snd_seq_free_event (inputEvent); - } - } - while (snd_seq_event_input_pending (seqHandle, 0) > 0); - - snd_seq_free_event (inputEvent); - } - } - - snd_midi_event_free (midiParser); - } - }; - -private: - MidiInput* const midiInput; - snd_seq_t* const seqHandle; - MidiInputCallback* const callback; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInputThread); -}; - -//============================================================================== -MidiInput::MidiInput (const String& name_) - : name (name_), - internal (0) +MidiInput::MidiInput (const String& nm) + : name (nm), internal (nullptr) { } MidiInput::~MidiInput() { stop(); - delete static_cast (internal); + delete static_cast (internal); } void MidiInput::start() { - static_cast (internal)->startThread(); + static_cast (internal)->enableCallback (true); } void MidiInput::stop() { - static_cast (internal)->stopThread (3000); + static_cast (internal)->enableCallback (false); } int MidiInput::getDefaultDeviceIndex() @@ -408,12 +561,12 @@ MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) MidiInput* newDevice = nullptr; StringArray devices; - snd_seq_t* const handle = iterateMidiDevices (true, devices, deviceIndex); + AlsaPort port (iterateMidiDevices (true, devices, deviceIndex)); - if (handle != 0) + if (port.isValid()) { newDevice = new MidiInput (devices [deviceIndex]); - newDevice->internal = new MidiInputThread (newDevice, handle, callback); + newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback); } return newDevice; @@ -423,19 +576,18 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba { MidiInput* newDevice = nullptr; - snd_seq_t* const handle = createMidiDevice (true, deviceName); + AlsaPort port (createMidiDevice (true, deviceName)); - if (handle != 0) + if (port.isValid()) { newDevice = new MidiInput (deviceName); - newDevice->internal = new MidiInputThread (newDevice, handle, callback); + newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback); } return newDevice; } - //============================================================================== #else @@ -448,7 +600,7 @@ MidiOutput* MidiOutput::createNewDevice (const String&) { return nul MidiOutput::~MidiOutput() {} void MidiOutput::sendMessageNow (const MidiMessage&) {} -MidiInput::MidiInput (const String& name_) : name (name_), internal (0) {} +MidiInput::MidiInput (const String& nm) : name (nm), internal (nullptr) {} MidiInput::~MidiInput() {} void MidiInput::start() {} void MidiInput::stop() {} diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm index b4519fb..8dd7bfa 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDBurner.mm @@ -1,345 +1,276 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ const int kilobytesPerSecond1x = 176; -} // (juce namespace) - -#define OpenDiskDevice MakeObjCClassName(OpenDiskDevice) - -@interface OpenDiskDevice : NSObject +struct AudioTrackProducerClass : public ObjCClass { -@public + AudioTrackProducerClass() : ObjCClass ("JUCEAudioTrackProducer_") + { + addIvar ("source"); + + addMethod (@selector (initWithAudioSourceHolder:), initWithAudioSourceHolder, "@@:^v"); + addMethod (@selector (cleanupTrackAfterBurn:), cleanupTrackAfterBurn, "v@:@"); + addMethod (@selector (cleanupTrackAfterVerification:), cleanupTrackAfterVerification, "c@:@"); + addMethod (@selector (estimateLengthOfTrack:), estimateLengthOfTrack, "Q@:@"); + addMethod (@selector (prepareTrack:forBurn:toMedia:), prepareTrack, "c@:@@@"); + addMethod (@selector (prepareTrackForVerification:), prepareTrackForVerification, "c@:@"); + addMethod (@selector (produceDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), + produceDataForTrack, "I@:@^cIQI^I"); + addMethod (@selector (producePreGapForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), + produceDataForTrack, "I@:@^cIQI^I"); + addMethod (@selector (verifyDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), + produceDataForTrack, "I@:@^cIQI^I"); + + registerClass(); + } + + struct AudioSourceHolder + { + AudioSourceHolder (AudioSource* s, int numFrames) + : source (s), readPosition (0), lengthInFrames (numFrames) + { + } + + ~AudioSourceHolder() + { + if (source != nullptr) + source->releaseResources(); + } + + ScopedPointer source; + int readPosition, lengthInFrames; + }; + +private: + static id initWithAudioSourceHolder (id self, SEL, AudioSourceHolder* source) + { + self = sendSuperclassMessage (self, @selector (init)); + object_setInstanceVariable (self, "source", source); + return self; + } + + static AudioSourceHolder* getSource (id self) + { + return getIvar (self, "source"); + } + + static void dealloc (id self, SEL) + { + delete getSource (self); + sendSuperclassMessage (self, @selector (dealloc)); + } + + static void cleanupTrackAfterBurn (id self, SEL, DRTrack*) {} + static BOOL cleanupTrackAfterVerification (id self, SEL, DRTrack*) { return true; } + + static uint64_t estimateLengthOfTrack (id self, SEL, DRTrack*) + { + return getSource (self)->lengthInFrames; + } + + static BOOL prepareTrack (id self, SEL, DRTrack*, DRBurn*, NSDictionary*) + { + if (AudioSourceHolder* const source = getSource (self)) + { + source->source->prepareToPlay (44100 / 75, 44100); + source->readPosition = 0; + } + + return true; + } + + static BOOL prepareTrackForVerification (id self, SEL, DRTrack*) + { + if (AudioSourceHolder* const source = getSource (self)) + source->source->prepareToPlay (44100 / 75, 44100); + + return true; + } + + static uint32_t produceDataForTrack (id self, SEL, DRTrack*, char* buffer, + uint32_t bufferLength, uint64_t /*address*/, + uint32_t /*blockSize*/, uint32_t* /*flags*/) + { + if (AudioSourceHolder* const source = getSource (self)) + { + const int numSamples = jmin ((int) bufferLength / 4, + (source->lengthInFrames * (44100 / 75)) - source->readPosition); + + if (numSamples > 0) + { + AudioSampleBuffer tempBuffer (2, numSamples); + AudioSourceChannelInfo info (tempBuffer); + + source->source->getNextAudioBlock (info); + + typedef AudioData::Pointer CDSampleFormat; + typedef AudioData::Pointer SourceSampleFormat; + + CDSampleFormat left (buffer, 2); + left.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (0)), numSamples); + CDSampleFormat right (buffer + 2, 2); + right.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (1)), numSamples); + + source->readPosition += numSamples; + } + + return numSamples * 4; + } + + return 0; + } + + static uint32_t producePreGapForTrack (id self, SEL, DRTrack*, char* buffer, + uint32_t bufferLength, uint64_t /*address*/, + uint32_t /*blockSize*/, uint32_t* /*flags*/) + { + zeromem (buffer, bufferLength); + return bufferLength; + } + + static BOOL verifyDataForTrack (id self, SEL, DRTrack*, const char*, + uint32_t /*bufferLength*/, uint64_t /*address*/, + uint32_t /*blockSize*/, uint32_t* /*flags*/) + { + return true; + } +}; + +struct OpenDiskDevice +{ + OpenDiskDevice (DRDevice* d) + : device (d), + tracks ([[NSMutableArray alloc] init]), + underrunProtection (true) + { + } + + ~OpenDiskDevice() + { + [tracks release]; + } + + void addSourceTrack (AudioSource* source, int numSamples) + { + if (source != nullptr) + { + const int numFrames = (numSamples + 587) / 588; + + static AudioTrackProducerClass cls; + + NSObject* producer = [cls.createInstance() performSelector: @selector (initWithAudioSourceHolder:) + withObject: (id) new AudioTrackProducerClass::AudioSourceHolder (source, numFrames)]; + DRTrack* track = [[DRTrack alloc] initWithProducer: producer]; + + { + NSMutableDictionary* p = [[track properties] mutableCopy]; + [p setObject: [DRMSF msfWithFrames: numFrames] forKey: DRTrackLengthKey]; + [p setObject: [NSNumber numberWithUnsignedShort: 2352] forKey: DRBlockSizeKey]; + [p setObject: [NSNumber numberWithInt: 0] forKey: DRDataFormKey]; + [p setObject: [NSNumber numberWithInt: 0] forKey: DRBlockTypeKey]; + [p setObject: [NSNumber numberWithInt: 0] forKey: DRTrackModeKey]; + [p setObject: [NSNumber numberWithInt: 0] forKey: DRSessionFormatKey]; + [track setProperties: p]; + [p release]; + } + + [tracks addObject: track]; + + [track release]; + [producer release]; + } + } + + String burn (AudioCDBurner::BurnProgressListener* listener, + bool shouldEject, bool peformFakeBurnForTesting, int burnSpeed) + { + DRBurn* burn = [DRBurn burnForDevice: device]; + + if (! [device acquireExclusiveAccess]) + return "Couldn't open or write to the CD device"; + + [device acquireMediaReservation]; + + NSMutableDictionary* d = [[burn properties] mutableCopy]; + [d autorelease]; + [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; + [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; + [d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) forKey: DRBurnCompletionActionKey]; + + if (burnSpeed > 0) + [d setObject: [NSNumber numberWithFloat: burnSpeed * kilobytesPerSecond1x] forKey: DRBurnRequestedSpeedKey]; + + if (! underrunProtection) + [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnUnderrunProtectionKey]; + + [burn setProperties: d]; + + [burn writeLayout: tracks]; + + for (;;) + { + Thread::sleep (300); + float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue]; + + if (listener != nullptr && listener->audioCDBurnProgress (progress)) + { + [burn abort]; + return "User cancelled the write operation"; + } + + if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed]) + return "Write operation failed"; + + if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone]) + break; + + NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] + objectForKey: DRErrorStatusErrorStringKey]; + if ([err length] > 0) + return nsStringToJuce (err); + } + + [device releaseMediaReservation]; + [device releaseExclusiveAccess]; + return String::empty; + } + DRDevice* device; NSMutableArray* tracks; bool underrunProtection; -} - -- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device; -- (void) dealloc; -- (void) addSourceTrack: (juce::AudioSource*) source numSamples: (int) numSamples_; -- (void) burn: (juce::AudioCDBurner::BurnProgressListener*) listener errorString: (juce::String*) error - ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting speed: (int) burnSpeed; -@end - -//============================================================================== -#define AudioTrackProducer MakeObjCClassName(AudioTrackProducer) - -@interface AudioTrackProducer : NSObject -{ - juce::AudioSource* source; - int readPosition, lengthInFrames; -} - -- (AudioTrackProducer*) init: (int) lengthInFrames; -- (AudioTrackProducer*) initWithAudioSource: (juce::AudioSource*) source numSamples: (int) lengthInSamples; -- (void) dealloc; -- (void) setupTrackProperties: (DRTrack*) track; - -- (void) cleanupTrackAfterBurn: (DRTrack*) track; -- (BOOL) cleanupTrackAfterVerification:(DRTrack*)track; -- (uint64_t) estimateLengthOfTrack:(DRTrack*)track; -- (BOOL) prepareTrack:(DRTrack*)track forBurn:(DRBurn*)burn - toMedia:(NSDictionary*)mediaInfo; -- (BOOL) prepareTrackForVerification:(DRTrack*)track; -- (uint32_t) produceDataForTrack:(DRTrack*)track intoBuffer:(char*)buffer - length:(uint32_t)bufferLength atAddress:(uint64_t)address - blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags; -- (uint32_t) producePreGapForTrack:(DRTrack*)track - intoBuffer:(char*)buffer length:(uint32_t)bufferLength - atAddress:(uint64_t)address blockSize:(uint32_t)blockSize - ioFlags:(uint32_t*)flags; -- (BOOL) verifyDataForTrack:(DRTrack*)track inBuffer:(const char*)buffer - length:(uint32_t)bufferLength atAddress:(uint64_t)address - blockSize:(uint32_t)blockSize ioFlags:(uint32_t*)flags; -- (uint32_t) producePreGapForTrack:(DRTrack*)track - intoBuffer:(char*)buffer length:(uint32_t)bufferLength - atAddress:(uint64_t)address blockSize:(uint32_t)blockSize - ioFlags:(uint32_t*)flags; -@end - -//============================================================================== -@implementation OpenDiskDevice - -- (OpenDiskDevice*) initWithDRDevice: (DRDevice*) device_ -{ - [super init]; - - device = device_; - tracks = [[NSMutableArray alloc] init]; - underrunProtection = true; - return self; -} - -- (void) dealloc -{ - [tracks release]; - [super dealloc]; -} - -- (void) addSourceTrack: (juce::AudioSource*) source_ numSamples: (int) numSamples_ -{ - AudioTrackProducer* p = [[AudioTrackProducer alloc] initWithAudioSource: source_ numSamples: numSamples_]; - DRTrack* t = [[DRTrack alloc] initWithProducer: p]; - [p setupTrackProperties: t]; - - [tracks addObject: t]; - - [t release]; - [p release]; -} - -- (void) burn: (juce::AudioCDBurner::BurnProgressListener*) listener errorString: (juce::String*) error - ejectAfterwards: (bool) shouldEject isFake: (bool) peformFakeBurnForTesting speed: (int) burnSpeed -{ - DRBurn* burn = [DRBurn burnForDevice: device]; - - if (! [device acquireExclusiveAccess]) - { - *error = "Couldn't open or write to the CD device"; - return; - } - - [device acquireMediaReservation]; - - NSMutableDictionary* d = [[burn properties] mutableCopy]; - [d autorelease]; - [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; - [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; - [d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) forKey: DRBurnCompletionActionKey]; - - if (burnSpeed > 0) - [d setObject: [NSNumber numberWithFloat: burnSpeed * juce::kilobytesPerSecond1x] forKey: DRBurnRequestedSpeedKey]; - - if (! underrunProtection) - [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnUnderrunProtectionKey]; - - [burn setProperties: d]; - - [burn writeLayout: tracks]; - - for (;;) - { - juce::Thread::sleep (300); - float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue]; - - if (listener != nullptr && listener->audioCDBurnProgress (progress)) - { - [burn abort]; - *error = "User cancelled the write operation"; - break; - } - - if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed]) - { - *error = "Write operation failed"; - break; - } - else if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone]) - { - break; - } - - NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] - objectForKey: DRErrorStatusErrorStringKey]; - - if ([err length] > 0) - { - *error = juce::CharPointer_UTF8 ([err UTF8String]); - break; - } - } - - [device releaseMediaReservation]; - [device releaseExclusiveAccess]; -} -@end - -//============================================================================== -@implementation AudioTrackProducer - -- (AudioTrackProducer*) init: (int) lengthInFrames_ -{ - lengthInFrames = lengthInFrames_; - readPosition = 0; - return self; -} - -- (void) setupTrackProperties: (DRTrack*) track -{ - NSMutableDictionary* p = [[track properties] mutableCopy]; - [p setObject:[DRMSF msfWithFrames: lengthInFrames] forKey: DRTrackLengthKey]; - [p setObject:[NSNumber numberWithUnsignedShort:2352] forKey: DRBlockSizeKey]; - [p setObject:[NSNumber numberWithInt:0] forKey: DRDataFormKey]; - [p setObject:[NSNumber numberWithInt:0] forKey: DRBlockTypeKey]; - [p setObject:[NSNumber numberWithInt:0] forKey: DRTrackModeKey]; - [p setObject:[NSNumber numberWithInt:0] forKey: DRSessionFormatKey]; - - - [track setProperties: p]; - [p release]; -} - -- (AudioTrackProducer*) initWithAudioSource: (juce::AudioSource*) source_ numSamples: (int) lengthInSamples -{ - AudioTrackProducer* s = [self init: (lengthInSamples + 587) / 588]; - - if (s != nil) - s->source = source_; - - return s; -} - -- (void) dealloc -{ - if (source != nullptr) - { - source->releaseResources(); - delete source; - } - - [super dealloc]; -} - -- (void) cleanupTrackAfterBurn: (DRTrack*) track -{ - (void) track; -} - -- (BOOL) cleanupTrackAfterVerification: (DRTrack*) track -{ - (void) track; - return true; -} - -- (uint64_t) estimateLengthOfTrack: (DRTrack*) track -{ - (void) track; - return lengthInFrames; -} - -- (BOOL) prepareTrack: (DRTrack*) track forBurn: (DRBurn*) burn - toMedia: (NSDictionary*) mediaInfo -{ - (void) track; (void) burn; (void) mediaInfo; - - if (source != nullptr) - source->prepareToPlay (44100 / 75, 44100); - - readPosition = 0; - return true; -} - -- (BOOL) prepareTrackForVerification: (DRTrack*) track -{ - (void) track; - if (source != nullptr) - source->prepareToPlay (44100 / 75, 44100); - - return true; -} - -- (uint32_t) produceDataForTrack: (DRTrack*) track intoBuffer: (char*) buffer - length: (uint32_t) bufferLength atAddress: (uint64_t) address - blockSize: (uint32_t) blockSize ioFlags: (uint32_t*) flags -{ - (void) track; (void) address; (void) blockSize; (void) flags; - - if (source != nullptr) - { - const int numSamples = juce::jmin ((int) bufferLength / 4, (lengthInFrames * (44100 / 75)) - readPosition); - - if (numSamples > 0) - { - juce::AudioSampleBuffer tempBuffer (2, numSamples); - - juce::AudioSourceChannelInfo info; - info.buffer = &tempBuffer; - info.startSample = 0; - info.numSamples = numSamples; - - source->getNextAudioBlock (info); - - typedef juce::AudioData::Pointer CDSampleFormat; - - typedef juce::AudioData::Pointer SourceSampleFormat; - CDSampleFormat left (buffer, 2); - left.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (0)), numSamples); - CDSampleFormat right (buffer + 2, 2); - right.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (1)), numSamples); - - readPosition += numSamples; - } - - return numSamples * 4; - } - - return 0; -} - -- (uint32_t) producePreGapForTrack: (DRTrack*) track - intoBuffer: (char*) buffer length: (uint32_t) bufferLength - atAddress: (uint64_t) address blockSize: (uint32_t) blockSize - ioFlags: (uint32_t*) flags -{ - (void) track; (void) address; (void) blockSize; (void) flags; - zeromem (buffer, bufferLength); - return bufferLength; -} - -- (BOOL) verifyDataForTrack: (DRTrack*) track inBuffer: (const char*) buffer - length: (uint32_t) bufferLength atAddress: (uint64_t) address - blockSize: (uint32_t) blockSize ioFlags: (uint32_t*) flags -{ - (void) track; (void) buffer; (void) bufferLength; (void) address; (void) blockSize; (void) flags; - return true; -} - -@end - - -namespace juce -{ +}; //============================================================================== class AudioCDBurner::Pimpl : public Timer { public: - Pimpl (AudioCDBurner& owner_, const int deviceIndex) - : device (0), owner (owner_) + Pimpl (AudioCDBurner& b, int deviceIndex) : owner (b) { - DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex]; - if (dev != nil) + if (DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex]) { - device = [[OpenDiskDevice alloc] initWithDRDevice: dev]; + device = new OpenDiskDevice (dev); lastState = getDiskState(); startTimer (1000); } @@ -348,10 +279,9 @@ public: ~Pimpl() { stopTimer(); - [device release]; } - void timerCallback() + void timerCallback() override { const DiskState state = getDiskState(); @@ -367,7 +297,6 @@ public: if ([device->device isValid]) { NSDictionary* status = [device->device status]; - NSString* state = [status objectForKey: DRDeviceMediaStateKey]; if ([state isEqualTo: DRDeviceMediaStateNone]) @@ -382,29 +311,23 @@ public: { if ([[[status objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue] > 0) return writableDiskPresent; - else - return readOnlyDiskPresent; + + return readOnlyDiskPresent; } } return unknown; } - bool openTray() { return [device->device isValid] && [device->device ejectMedia]; } + bool openTray() { return [device->device isValid] && [device->device ejectMedia]; } Array getAvailableWriteSpeeds() const { Array results; if ([device->device isValid]) - { - NSArray* speeds = [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey]; - for (unsigned int i = 0; i < [speeds count]; ++i) - { - const int kbPerSec = [[speeds objectAtIndex: i] intValue]; - results.add (kbPerSec / kilobytesPerSecond1x); - } - } + for (id kbPerSec in [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey]) + results.add ([kbPerSec intValue] / kilobytesPerSecond1x); return results; } @@ -426,7 +349,7 @@ public: objectForKey: DRDeviceMediaBlocksFreeKey] intValue]; } - OpenDiskDevice* device; + ScopedPointer device; private: DiskState lastState; @@ -445,40 +368,21 @@ AudioCDBurner::~AudioCDBurner() AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) { - ScopedPointer b (new AudioCDBurner (deviceIndex)); + ScopedPointer b (new AudioCDBurner (deviceIndex)); if (b->pimpl->device == nil) - b = 0; + b = nullptr; return b.release(); } -namespace -{ - NSArray* findDiskBurnerDevices() - { - NSMutableArray* results = [NSMutableArray array]; - NSArray* devs = [DRDevice devices]; - - for (int i = 0; i < [devs count]; ++i) - { - NSDictionary* dic = [[devs objectAtIndex: i] info]; - NSString* name = [dic valueForKey: DRDeviceProductNameKey]; - if (name != nil) - [results addObject: name]; - } - - return results; - } -} - StringArray AudioCDBurner::findAvailableDevices() { - NSArray* names = findDiskBurnerDevices(); StringArray s; - for (unsigned int i = 0; i < [names count]; ++i) - s.add (CharPointer_UTF8 ([[names objectAtIndex: i] UTF8String])); + for (NSDictionary* dic in [DRDevice devices]) + if (NSString* name = [dic valueForKey: DRDeviceProductNameKey]) + s.add (nsStringToJuce (name)); return s; } @@ -532,30 +436,20 @@ bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps) { if ([pimpl->device->device isValid]) { - [pimpl->device addSourceTrack: source numSamples: numSamps]; + pimpl->device->addSourceTrack (source, numSamps); return true; } return false; } -String AudioCDBurner::burn (juce::AudioCDBurner::BurnProgressListener* listener, +String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards, bool performFakeBurnForTesting, int writeSpeed) { - String error ("Couldn't open or write to the CD device"); - if ([pimpl->device->device isValid]) - { - error = String::empty; + return pimpl->device->burn (listener, ejectDiscAfterwards, performFakeBurnForTesting, writeSpeed); - [pimpl->device burn: listener - errorString: &error - ejectAfterwards: ejectDiscAfterwards - isFake: performFakeBurnForTesting - speed: writeSpeed]; - } - - return error; + return "Couldn't open or write to the CD device"; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm index 0e0a8ce..447c98b 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_AudioCDReader.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -199,9 +198,7 @@ bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int sta { reader = nullptr; - FileInputStream* const in = tracks [track].createInputStream(); - - if (in != nullptr) + if (FileInputStream* const in = tracks [track].createInputStream()) { BufferedInputStream* const bin = new BufferedInputStream (in, 65536, true); @@ -238,7 +235,9 @@ bool AudioCDReader::isCDStillPresent() const void AudioCDReader::ejectDisk() { JUCE_AUTORELEASEPOOL - [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())]; + { + [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())]; + } } bool AudioCDReader::isTrackAudio (int trackNum) const diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index c1a7e66..e51dd7c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -1,56 +1,161 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_COREAUDIO_LOGGING_ENABLED - #define JUCE_COREAUDIOLOG(a) Logger::writeToLog (a) + #define JUCE_COREAUDIOLOG(a) { String camsg ("CoreAudio: "); camsg << a; Logger::writeToLog (camsg); } #else #define JUCE_COREAUDIOLOG(a) #endif //============================================================================== -class CoreAudioInternal : public Timer +struct SystemVol +{ + SystemVol (AudioObjectPropertySelector selector) + : outputDeviceID (kAudioObjectUnknown) + { + addr.mScope = kAudioObjectPropertyScopeGlobal; + addr.mElement = kAudioObjectPropertyElementMaster; + addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + + if (AudioHardwareServiceHasProperty (kAudioObjectSystemObject, &addr)) + { + UInt32 deviceIDSize = sizeof (outputDeviceID); + OSStatus status = AudioHardwareServiceGetPropertyData (kAudioObjectSystemObject, &addr, 0, + nullptr, &deviceIDSize, &outputDeviceID); + + if (status == noErr) + { + addr.mElement = kAudioObjectPropertyElementMaster; + addr.mSelector = selector; + addr.mScope = kAudioDevicePropertyScopeOutput; + + if (! AudioHardwareServiceHasProperty (outputDeviceID, &addr)) + outputDeviceID = kAudioObjectUnknown; + } + } + } + + float getGain() + { + Float32 gain = 0; + + if (outputDeviceID != kAudioObjectUnknown) + { + UInt32 size = sizeof (gain); + AudioHardwareServiceGetPropertyData (outputDeviceID, &addr, + 0, nullptr, &size, &gain); + } + + return (float) gain; + } + + bool setGain (float gain) + { + if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) + { + Float32 newVolume = gain; + UInt32 size = sizeof (newVolume); + + return AudioHardwareServiceSetPropertyData (outputDeviceID, &addr, 0, nullptr, + size, &newVolume) == noErr; + } + + return false; + } + + bool isMuted() + { + UInt32 muted = 0; + + if (outputDeviceID != kAudioObjectUnknown) + { + UInt32 size = sizeof (muted); + AudioHardwareServiceGetPropertyData (outputDeviceID, &addr, + 0, nullptr, &size, &muted); + } + + return muted != 0; + } + + bool setMuted (bool mute) + { + if (outputDeviceID != kAudioObjectUnknown && canSetVolume()) + { + UInt32 newMute = mute ? 1 : 0; + UInt32 size = sizeof (newMute); + + return AudioHardwareServiceSetPropertyData (outputDeviceID, &addr, 0, nullptr, + size, &newMute) == noErr; + } + + return false; + } + +private: + AudioDeviceID outputDeviceID; + AudioObjectPropertyAddress addr; + + bool canSetVolume() + { + Boolean isSettable = NO; + return AudioHardwareServiceIsPropertySettable (outputDeviceID, &addr, &isSettable) == noErr + && isSettable; + } +}; + +#define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 +float JUCE_CALLTYPE SystemAudioVolume::getGain() { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).getGain(); } +bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return SystemVol (kAudioHardwareServiceDeviceProperty_VirtualMasterVolume).setGain (gain); } +bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return SystemVol (kAudioDevicePropertyMute).isMuted(); } +bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return SystemVol (kAudioDevicePropertyMute).setMuted (mute); } + +//============================================================================== +struct CoreAudioClasses +{ + +class CoreAudioIODevice; + +//============================================================================== +class CoreAudioInternal : private Timer { public: - //============================================================================== - CoreAudioInternal (AudioDeviceID id) - : inputLatency (0), + CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id) + : owner (d), + inputLatency (0), outputLatency (0), + bitDepth (32), callback (nullptr), #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 audioProcID (0), #endif - isSlaveDevice (false), deviceID (id), started (false), sampleRate (0), bufferSize (512), numInputChans (0), numOutputChans (0), - callbacksAllowed (true), - numInputChannelInfos (0), - numOutputChannelInfos (0) + callbacksAllowed (true) { jassert (deviceID != 0); @@ -81,20 +186,25 @@ public: const int tempBufSize = bufferSize + 4; audioBuffer.calloc ((size_t) ((numInputChans + numOutputChans) * tempBufSize)); - tempInputBuffers.calloc ((size_t) numInputChans + 2); + tempInputBuffers.calloc ((size_t) numInputChans + 2); tempOutputBuffers.calloc ((size_t) numOutputChans + 2); - int i, count = 0; - for (i = 0; i < numInputChans; ++i) - tempInputBuffers[i] = audioBuffer + count++ * tempBufSize; - - for (i = 0; i < numOutputChans; ++i) - tempOutputBuffers[i] = audioBuffer + count++ * tempBufSize; + int count = 0; + for (int i = 0; i < numInputChans; ++i) tempInputBuffers[i] = audioBuffer + count++ * tempBufSize; + for (int i = 0; i < numOutputChans; ++i) tempOutputBuffers[i] = audioBuffer + count++ * tempBufSize; } - // returns the number of actual available channels - void fillInChannelInfo (const bool input) + struct CallbackDetailsForChannel { + int streamNum; + int dataOffsetSamples; + int dataStrideSamples; + }; + + // returns the number of actual available channels + StringArray getChannelInfo (const bool input, Array& newChannelInfo) const + { + StringArray newNames; int chanNum = 0; UInt32 size; @@ -103,12 +213,12 @@ public: pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) { - HeapBlock bufList; + HeapBlock bufList; bufList.calloc (size, 1); - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList))) { const int numStreams = (int) bufList->mNumberBuffers; @@ -119,53 +229,144 @@ public: for (unsigned int j = 0; j < b.mNumberChannels; ++j) { String name; + NSString* nameNSString = nil; + size = sizeof (nameNSString); + pa.mSelector = kAudioObjectPropertyElementName; + pa.mElement = chanNum + 1; + + if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &nameNSString) == noErr) { - char channelName [256] = { 0 }; - UInt32 nameSize = sizeof (channelName); - UInt32 channelNum = (UInt32) chanNum + 1; - pa.mSelector = kAudioDevicePropertyChannelName; - - if (AudioObjectGetPropertyData (deviceID, &pa, sizeof (channelNum), &channelNum, &nameSize, channelName) == noErr) - name = String::fromUTF8 (channelName, (int) nameSize); + name = nsStringToJuce (nameNSString); + [nameNSString release]; } - if (input) + if ((input ? activeInputChans : activeOutputChans) [chanNum]) { - if (activeInputChans[chanNum]) - { - inputChannelInfo [numInputChannelInfos].streamNum = i; - inputChannelInfo [numInputChannelInfos].dataOffsetSamples = (int) j; - inputChannelInfo [numInputChannelInfos].dataStrideSamples = (int) b.mNumberChannels; - ++numInputChannelInfos; - } - - if (name.isEmpty()) - name << "Input " << (chanNum + 1); - - inChanNames.add (name); - } - else - { - if (activeOutputChans[chanNum]) - { - outputChannelInfo [numOutputChannelInfos].streamNum = i; - outputChannelInfo [numOutputChannelInfos].dataOffsetSamples = (int) j; - outputChannelInfo [numOutputChannelInfos].dataStrideSamples = (int) b.mNumberChannels; - ++numOutputChannelInfos; - } - - if (name.isEmpty()) - name << "Output " << (chanNum + 1); - - outChanNames.add (name); + CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels }; + newChannelInfo.add (info); } + if (name.isEmpty()) + name << (input ? "Input " : "Output ") << (chanNum + 1); + + newNames.add (name); ++chanNum; } } } } + + return newNames; + } + + Array getSampleRatesFromDevice() const + { + Array newSampleRates; + + AudioObjectPropertyAddress pa; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + UInt32 size = 0; + + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) + { + HeapBlock ranges; + ranges.calloc (size, 1); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) + { + static const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; + + for (int i = 0; i < numElementsInArray (possibleRates); ++i) + { + for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) + { + if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) + { + newSampleRates.add (possibleRates[i]); + break; + } + } + } + } + } + + if (newSampleRates.size() == 0 && sampleRate > 0) + newSampleRates.add (sampleRate); + + return newSampleRates; + } + + Array getBufferSizesFromDevice() const + { + Array newBufferSizes; + + AudioObjectPropertyAddress pa; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; + pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + UInt32 size = 0; + + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) + { + HeapBlock ranges; + ranges.calloc (size, 1); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) + { + newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); + + for (int i = 32; i < 2048; i += 32) + { + for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) + { + if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) + { + newBufferSizes.addIfNotAlreadyThere (i); + break; + } + } + } + + if (bufferSize > 0) + newBufferSizes.addIfNotAlreadyThere (bufferSize); + } + } + + if (newBufferSizes.size() == 0 && bufferSize > 0) + newBufferSizes.add (bufferSize); + + return newBufferSizes; + } + + int getLatencyFromDevice (AudioObjectPropertyScope scope) const + { + UInt32 lat = 0; + UInt32 size = sizeof (lat); + AudioObjectPropertyAddress pa; + pa.mElement = kAudioObjectPropertyElementMaster; + pa.mSelector = kAudioDevicePropertyLatency; + pa.mScope = scope; + AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &lat); + return (int) lat; + } + + int getBitDepthFromDevice (AudioObjectPropertyScope scope) const + { + AudioObjectPropertyAddress pa; + pa.mElement = kAudioObjectPropertyElementMaster; + pa.mSelector = kAudioStreamPropertyPhysicalFormat; + pa.mScope = scope; + + AudioStreamBasicDescription asbd; + UInt32 size = sizeof (asbd); + + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &asbd))) + return (int) asbd.mBitsPerChannel; + + return 0; } void updateDetailsFromDevice() @@ -175,8 +376,8 @@ public: if (deviceID == 0) return; - const ScopedLock sl (callbackLock); - + // this collects all the new details from the device without any locking, then + // locks + swaps them afterwards. AudioObjectPropertyAddress pa; pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; @@ -184,131 +385,58 @@ public: UInt32 isAlive; UInt32 size = sizeof (isAlive); pa.mSelector = kAudioDevicePropertyDeviceIsAlive; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &isAlive)) - && isAlive == 0) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) && isAlive == 0) return; Float64 sr; size = sizeof (sr); pa.mSelector = kAudioDevicePropertyNominalSampleRate; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &sr))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr))) sampleRate = sr; - UInt32 framesPerBuf; + UInt32 framesPerBuf = bufferSize; size = sizeof (framesPerBuf); pa.mSelector = kAudioDevicePropertyBufferFrameSize; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &framesPerBuf))) - { - bufferSize = (int) framesPerBuf; - allocateTempBuffers(); - } + AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); - bufferSizes.clear(); + Array newBufferSizes (getBufferSizesFromDevice()); + Array newSampleRates (getSampleRatesFromDevice()); - pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; + inputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput); + outputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput); - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) - { - HeapBlock ranges; - ranges.calloc (size, 1); + Array newInChans, newOutChans; + StringArray newInNames (getChannelInfo (true, newInChans)); + StringArray newOutNames (getChannelInfo (false, newOutChans)); - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) - { - bufferSizes.add ((int) ranges[0].mMinimum); + const int inputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeInput); + const int outputBitDepth = getBitDepthFromDevice (kAudioDevicePropertyScopeOutput); - for (int i = 32; i < 2048; i += 32) - { - for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) - { - if (i >= ranges[j].mMinimum && i <= ranges[j].mMaximum) - { - bufferSizes.addIfNotAlreadyThere (i); - break; - } - } - } + bitDepth = jmax (inputBitDepth, outputBitDepth); + if (bitDepth <= 0) + bitDepth = 32; - if (bufferSize > 0) - bufferSizes.addIfNotAlreadyThere (bufferSize); - } - } + // after getting the new values, lock + apply them + const ScopedLock sl (callbackLock); - if (bufferSizes.size() == 0 && bufferSize > 0) - bufferSizes.add (bufferSize); + bufferSize = (int) framesPerBuf; + allocateTempBuffers(); - sampleRates.clear(); - const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; - String rates; + sampleRates.swapWith (newSampleRates); + bufferSizes.swapWith (newBufferSizes); - pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; + inChanNames.swapWith (newInNames); + outChanNames.swapWith (newOutNames); - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) - { - HeapBlock ranges; - ranges.calloc (size, 1); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) - { - for (int i = 0; i < numElementsInArray (possibleRates); ++i) - { - bool ok = false; - - for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;) - if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) - ok = true; - - if (ok) - { - sampleRates.add (possibleRates[i]); - rates << possibleRates[i] << ' '; - } - } - } - } - - if (sampleRates.size() == 0 && sampleRate > 0) - { - sampleRates.add (sampleRate); - rates << sampleRate; - } - - JUCE_COREAUDIOLOG ("sr: " + rates); - - inputLatency = 0; - outputLatency = 0; - UInt32 lat; - size = sizeof (lat); - pa.mSelector = kAudioDevicePropertyLatency; - pa.mScope = kAudioDevicePropertyScopeInput; - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) - inputLatency = (int) lat; - - pa.mScope = kAudioDevicePropertyScopeOutput; - size = sizeof (lat); - - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat) == noErr) - outputLatency = (int) lat; - - JUCE_COREAUDIOLOG ("lat: " + String (inputLatency) + " " + String (outputLatency)); - - inChanNames.clear(); - outChanNames.clear(); - - inputChannelInfo.calloc ((size_t) numInputChans + 2); - numInputChannelInfos = 0; - - outputChannelInfo.calloc ((size_t) numOutputChans + 2); - numOutputChannelInfos = 0; - - fillInChannelInfo (true); - fillInChannelInfo (false); + inputChannelInfo.swapWith (newInChans); + outputChannelInfo.swapWith (newOutChans); } //============================================================================== StringArray getSources (bool input) { StringArray s; - HeapBlock types; + HeapBlock types; const int num = getAllDataSourcesForDevice (deviceID, types); for (int i = 0; i < num; ++i) @@ -328,11 +456,8 @@ public: pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &transSize, &avt))) - { - DBG (buffer); + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &transSize, &avt))) s.add (buffer); - } } return s; @@ -351,9 +476,9 @@ public: if (deviceID != 0) { - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ¤tSourceID))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ¤tSourceID))) { - HeapBlock types; + HeapBlock types; const int num = getAllDataSourcesForDevice (deviceID, types); for (int i = 0; i < num; ++i) @@ -374,7 +499,7 @@ public: { if (deviceID != 0) { - HeapBlock types; + HeapBlock types; const int num = getAllDataSourcesForDevice (deviceID, types); if (isPositiveAndBelow (index, num)) @@ -394,11 +519,9 @@ public: //============================================================================== String reopen (const BigInteger& inputChannels, const BigInteger& outputChannels, - double newSampleRate, - int bufferSizeSamples) + double newSampleRate, int bufferSizeSamples) { String error; - JUCE_COREAUDIOLOG ("CoreAudio reopen"); callbacksAllowed = false; stopTimer(); @@ -452,11 +575,6 @@ public: error = "Device has no available sample-rates"; else if (bufferSizes.size() == 0) error = "Device has no available buffer-sizes"; - else if (inputDevice != 0) - error = inputDevice->reopen (inputChannels, - outputChannels, - newSampleRate, - bufferSizeSamples); } } @@ -464,7 +582,7 @@ public: return error; } - bool start (AudioIODeviceCallback* cb) + bool start() { if (! started) { @@ -495,13 +613,13 @@ public: } } - if (started) - { - const ScopedLock sl (callbackLock); - callback = cb; - } + return started; + } - return started && (inputDevice == nullptr || inputDevice->start (cb)); + void setCallback (AudioIODeviceCallback* cb) + { + const ScopedLock sl (callbackLock); + callback = cb; } void stop (bool leaveInterruptRunning) @@ -541,7 +659,7 @@ public: pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; - OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &running)); + OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &running)); if (running == 0) break; @@ -549,9 +667,6 @@ public: const ScopedLock sl (callbackLock); } - - if (inputDevice != nullptr) - inputDevice->stop (leaveInterruptRunning); } double getSampleRate() const { return sampleRate; } @@ -560,82 +675,38 @@ public: void audioCallback (const AudioBufferList* inInputData, AudioBufferList* outOutputData) { - int i; const ScopedLock sl (callbackLock); if (callback != nullptr) { - if (inputDevice == 0) + for (int i = numInputChans; --i >= 0;) { - for (i = numInputChans; --i >= 0;) - { - const CallbackDetailsForChannel& info = inputChannelInfo[i]; - float* dest = tempInputBuffers [i]; - const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) - + info.dataOffsetSamples; - const int stride = info.dataStrideSamples; - - if (stride != 0) // if this is zero, info is invalid - { - for (int j = bufferSize; --j >= 0;) - { - *dest++ = *src; - src += stride; - } - } - } - } - - if (! isSlaveDevice) - { - if (inputDevice == 0) - { - callback->audioDeviceIOCallback (const_cast (tempInputBuffers.getData()), - numInputChans, - tempOutputBuffers, - numOutputChans, - bufferSize); - } - else - { - jassert (inputDevice->bufferSize == bufferSize); - - // Sometimes the two linked devices seem to get their callbacks in - // parallel, so we need to lock both devices to stop the input data being - // changed while inside our callback.. - const ScopedLock sl2 (inputDevice->callbackLock); - - callback->audioDeviceIOCallback (const_cast (inputDevice->tempInputBuffers.getData()), - inputDevice->numInputChans, - tempOutputBuffers, - numOutputChans, - bufferSize); - } - - for (i = numOutputChans; --i >= 0;) - { - const CallbackDetailsForChannel& info = outputChannelInfo[i]; - const float* src = tempOutputBuffers [i]; - float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + const CallbackDetailsForChannel& info = inputChannelInfo.getReference(i); + float* dest = tempInputBuffers [i]; + const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; - const int stride = info.dataStrideSamples; + const int stride = info.dataStrideSamples; - if (stride != 0) // if this is zero, info is invalid + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) { - for (int j = bufferSize; --j >= 0;) - { - *dest = *src++; - dest += stride; - } + *dest++ = *src; + src += stride; } } } - } - else - { - for (i = jmin (numOutputChans, numOutputChannelInfos); --i >= 0;) + + callback->audioDeviceIOCallback (const_cast (tempInputBuffers.getData()), + numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); + + for (int i = numOutputChans; --i >= 0;) { - const CallbackDetailsForChannel& info = outputChannelInfo[i]; + const CallbackDetailsForChannel& info = outputChannelInfo.getReference(i); + const float* src = tempOutputBuffers [i]; float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; const int stride = info.dataStrideSamples; @@ -644,12 +715,18 @@ public: { for (int j = bufferSize; --j >= 0;) { - *dest = 0.0f; + *dest = *src++; dest += stride; } } } } + else + { + for (UInt32 i = 0; i < outOutputData->mNumberBuffers; ++i) + zeromem (outOutputData->mBuffers[i].mData, + outOutputData->mBuffers[i].mDataByteSize); + } } // called by callbacks @@ -659,100 +736,44 @@ public: startTimer (100); } - void timerCallback() + void timerCallback() override { - stopTimer(); - JUCE_COREAUDIOLOG ("CoreAudio device changed callback"); + JUCE_COREAUDIOLOG ("Device changed"); + stopTimer(); const double oldSampleRate = sampleRate; const int oldBufferSize = bufferSize; updateDetailsFromDevice(); if (oldBufferSize != bufferSize || oldSampleRate != sampleRate) - { - callbacksAllowed = false; - stop (false); - updateDetailsFromDevice(); - callbacksAllowed = true; - } - } - - CoreAudioInternal* getRelatedDevice() const - { - UInt32 size = 0; - ScopedPointer result; - - AudioObjectPropertyAddress pa; - pa.mSelector = kAudioDevicePropertyRelatedDevices; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; - - if (deviceID != 0 - && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size) == noErr - && size > 0) - { - HeapBlock devs; - devs.calloc (size, 1); - - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, devs))) - { - for (unsigned int i = 0; i < size / sizeof (AudioDeviceID); ++i) - { - if (devs[i] != deviceID && devs[i] != 0) - { - result = new CoreAudioInternal (devs[i]); - - const bool thisIsInput = inChanNames.size() > 0 && outChanNames.size() == 0; - const bool otherIsInput = result->inChanNames.size() > 0 && result->outChanNames.size() == 0; - - if (thisIsInput != otherIsInput - || (inChanNames.size() + outChanNames.size() == 0) - || (result->inChanNames.size() + result->outChanNames.size()) == 0) - break; - - result = 0; - } - } - } - } - - return result.release(); + owner.restart(); } //============================================================================== + CoreAudioIODevice& owner; int inputLatency, outputLatency; + int bitDepth; BigInteger activeInputChans, activeOutputChans; StringArray inChanNames, outChanNames; - Array sampleRates; - Array bufferSizes; + Array sampleRates; + Array bufferSizes; AudioIODeviceCallback* callback; #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 AudioDeviceIOProcID audioProcID; #endif - ScopedPointer inputDevice; - bool isSlaveDevice; - private: CriticalSection callbackLock; AudioDeviceID deviceID; bool started; double sampleRate; int bufferSize; - HeapBlock audioBuffer; + HeapBlock audioBuffer; int numInputChans, numOutputChans; bool callbacksAllowed; - struct CallbackDetailsForChannel - { - int streamNum; - int dataOffsetSamples; - int dataStrideSamples; - }; - - int numInputChannelInfos, numOutputChannelInfos; - HeapBlock inputChannelInfo, outputChannelInfo; - HeapBlock tempInputBuffers, tempOutputBuffers; + Array inputChannelInfo, outputChannelInfo; + HeapBlock tempInputBuffers, tempOutputBuffers; //============================================================================== static OSStatus audioIOProc (AudioDeviceID /*inDevice*/, @@ -763,13 +784,13 @@ private: const AudioTimeStamp* /*inOutputTime*/, void* device) { - static_cast (device)->audioCallback (inInputData, outOutputData); + static_cast (device)->audioCallback (inInputData, outOutputData); return noErr; } static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { - CoreAudioInternal* const intern = static_cast (inClientData); + CoreAudioInternal* const intern = static_cast (inClientData); switch (pa->mSelector) { @@ -795,7 +816,7 @@ private: } //============================================================================== - static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock & types) + static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock& types) { AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyDataSources; @@ -804,11 +825,11 @@ private: UInt32 size = 0; if (deviceID != 0 - && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size) == noErr) + && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) { types.calloc (size, 1); - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, types) == noErr) + if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, types) == noErr) return size / (int) sizeof (OSType); } @@ -829,7 +850,7 @@ private: return false; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioInternal); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioInternal) }; @@ -838,10 +859,8 @@ class CoreAudioIODevice : public AudioIODevice { public: CoreAudioIODevice (const String& deviceName, - AudioDeviceID inputDeviceId, - const int inputIndex_, - AudioDeviceID outputDeviceId, - const int outputIndex_) + AudioDeviceID inputDeviceId, const int inputIndex_, + AudioDeviceID outputDeviceId, const int outputIndex_) : AudioIODevice (deviceName, "CoreAudio"), inputIndex (inputIndex_), outputIndex (outputIndex_), @@ -853,20 +872,11 @@ public: if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) { jassert (inputDeviceId != 0); - - device = new CoreAudioInternal (inputDeviceId); + device = new CoreAudioInternal (*this, inputDeviceId); } else { - device = new CoreAudioInternal (outputDeviceId); - - if (inputDeviceId != 0) - { - CoreAudioInternal* secondDevice = new CoreAudioInternal (inputDeviceId); - - device->inputDevice = secondDevice; - secondDevice->isSlaveDevice = true; - } + device = new CoreAudioInternal (*this, outputDeviceId); } internal = device; @@ -874,8 +884,8 @@ public: AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); } @@ -892,37 +902,24 @@ public: AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); } - StringArray getOutputChannelNames() - { - return internal->outChanNames; - } + StringArray getOutputChannelNames() override { return internal->outChanNames; } + StringArray getInputChannelNames() override { return internal->inChanNames; } - StringArray getInputChannelNames() - { - if (internal->inputDevice != 0) - return internal->inputDevice->inChanNames; - else - return internal->inChanNames; - } + bool isOpen() override { return isOpen_; } - bool isOpen() { return isOpen_; } + Array getAvailableSampleRates() override { return internal->sampleRates; } + Array getAvailableBufferSizes() override { return internal->bufferSizes; } - int getNumSampleRates() { return internal->sampleRates.size(); } - double getSampleRate (int index) { return internal->sampleRates [index]; } - double getCurrentSampleRate() { return internal->getSampleRate(); } + double getCurrentSampleRate() override { return internal->getSampleRate(); } + int getCurrentBitDepth() override { return internal->bitDepth; } + int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); } - int getCurrentBitDepth() { return 32; } // no way to find out, so just assume it's high.. - - int getNumBufferSizesAvailable() { return internal->bufferSizes.size(); } - int getBufferSizeSamples (int index) { return internal->bufferSizes [index]; } - int getCurrentBufferSizeSamples() { return internal->getBufferSize(); } - - int getDefaultBufferSize() + int getDefaultBufferSize() override { int best = 0; - for (int i = 0; best < 512 && i < getNumBufferSizesAvailable(); ++i) - best = getBufferSizeSamples(i); + for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i) + best = internal->bufferSizes.getUnchecked(i); if (best == 0) best = 512; @@ -932,8 +929,7 @@ public: String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, - int bufferSizeSamples) + double sampleRate, int bufferSizeSamples) override { isOpen_ = true; @@ -941,57 +937,59 @@ public: bufferSizeSamples = getDefaultBufferSize(); lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); + + JUCE_COREAUDIOLOG ("Opened: " << getName()); + JUCE_COREAUDIOLOG ("Latencies: " << getInputLatencyInSamples() << ' ' << getOutputLatencyInSamples()); + isOpen_ = lastError.isEmpty(); return lastError; } - void close() + void close() override { isOpen_ = false; internal->stop (false); } - BigInteger getActiveOutputChannels() const + void restart() { - return internal->activeOutputChans; + JUCE_COREAUDIOLOG ("Restarting"); + AudioIODeviceCallback* oldCallback = internal->callback; + stop(); + start (oldCallback); } - BigInteger getActiveInputChannels() const - { - BigInteger chans (internal->activeInputChans); + BigInteger getActiveOutputChannels() const override { return internal->activeOutputChans; } + BigInteger getActiveInputChannels() const override { return internal->activeInputChans; } - if (internal->inputDevice != nullptr) - chans |= internal->inputDevice->activeInputChans; - - return chans; - } - - int getOutputLatencyInSamples() + int getOutputLatencyInSamples() override { // this seems like a good guess at getting the latency right - comparing // this with a round-trip measurement, it gets it to within a few millisecs // for the built-in mac soundcard - return internal->outputLatency + internal->getBufferSize() * 2; + return internal->outputLatency; } - int getInputLatencyInSamples() + int getInputLatencyInSamples() override { - return internal->inputLatency + internal->getBufferSize() * 2; + return internal->inputLatency; } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback) override { if (! isStarted) { if (callback != nullptr) callback->audioDeviceAboutToStart (this); - isStarted = true; - internal->start (callback); + isStarted = internal->start(); + + if (isStarted) + internal->setCallback (callback); } } - void stop() + void stop() override { if (isStarted) { @@ -1005,7 +1003,7 @@ public: } } - bool isPlaying() + bool isPlaying() override { if (internal->callback == nullptr) isStarted = false; @@ -1013,7 +1011,7 @@ public: return isStarted; } - String getLastError() + String getLastError() override { return lastError; } @@ -1027,12 +1025,10 @@ private: static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { - CoreAudioInternal* const intern = static_cast (inClientData); - switch (pa->mSelector) { case kAudioHardwarePropertyDevices: - intern->deviceDetailsChanged(); + static_cast (inClientData)->deviceDetailsChanged(); break; case kAudioHardwarePropertyDefaultOutputDevice: @@ -1044,14 +1040,664 @@ private: return noErr; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODevice) }; +//============================================================================== +class AudioIODeviceCombiner : public AudioIODevice, + private Thread +{ +public: + AudioIODeviceCombiner (const String& deviceName) + : AudioIODevice (deviceName, "CoreAudio"), + Thread (deviceName), callback (nullptr), + currentSampleRate (0), currentBufferSize (0), active (false) + { + } + + ~AudioIODeviceCombiner() + { + close(); + devices.clear(); + } + + void addDevice (AudioIODevice* device, bool useInputs, bool useOutputs) + { + jassert (device != nullptr); + jassert (! isOpen()); + jassert (! device->isOpen()); + devices.add (new DeviceWrapper (*this, device, useInputs, useOutputs)); + } + + Array getDevices() const + { + Array devs; + + for (int i = 0; i < devices.size(); ++i) + devs.add (devices.getUnchecked(i)->device); + + return devs; + } + + StringArray getOutputChannelNames() override + { + StringArray names; + + for (int i = 0; i < devices.size(); ++i) + names.addArray (devices.getUnchecked(i)->getOutputChannelNames()); + + names.appendNumbersToDuplicates (false, true); + return names; + } + + StringArray getInputChannelNames() override + { + StringArray names; + + for (int i = 0; i < devices.size(); ++i) + names.addArray (devices.getUnchecked(i)->getInputChannelNames()); + + names.appendNumbersToDuplicates (false, true); + return names; + } + + Array getAvailableSampleRates() override + { + Array commonRates; + + for (int i = 0; i < devices.size(); ++i) + { + Array rates (devices.getUnchecked(i)->device->getAvailableSampleRates()); + + if (i == 0) + commonRates = rates; + else + commonRates.removeValuesNotIn (rates); + } + + return commonRates; + } + + Array getAvailableBufferSizes() override + { + Array commonSizes; + + for (int i = 0; i < devices.size(); ++i) + { + Array sizes (devices.getUnchecked(i)->device->getAvailableBufferSizes()); + + if (i == 0) + commonSizes = sizes; + else + commonSizes.removeValuesNotIn (sizes); + } + + return commonSizes; + } + + bool isOpen() override { return active; } + bool isPlaying() override { return callback != nullptr; } + double getCurrentSampleRate() override { return currentSampleRate; } + int getCurrentBufferSizeSamples() override { return currentBufferSize; } + + int getCurrentBitDepth() override + { + int depth = 32; + + for (int i = 0; i < devices.size(); ++i) + depth = jmin (depth, devices.getUnchecked(i)->device->getCurrentBitDepth()); + + return depth; + } + + int getDefaultBufferSize() override + { + int size = 0; + + for (int i = 0; i < devices.size(); ++i) + size = jmax (size, devices.getUnchecked(i)->device->getDefaultBufferSize()); + + return size; + } + + String open (const BigInteger& inputChannels, + const BigInteger& outputChannels, + double sampleRate, int bufferSize) override + { + close(); + active = true; + + if (bufferSize <= 0) + bufferSize = getDefaultBufferSize(); + + if (sampleRate <= 0) + { + Array rates (getAvailableSampleRates()); + + for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i) + sampleRate = rates.getUnchecked(i); + } + + currentSampleRate = sampleRate; + currentBufferSize = bufferSize; + + const int fifoSize = bufferSize * 3 + 1; + int totalInputChanIndex = 0, totalOutputChanIndex = 0; + int chanIndex = 0; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + BigInteger ins (inputChannels >> totalInputChanIndex); + BigInteger outs (outputChannels >> totalOutputChanIndex); + + int numIns = d.getInputChannelNames().size(); + int numOuts = d.getOutputChannelNames().size(); + + totalInputChanIndex += numIns; + totalOutputChanIndex += numOuts; + + String err = d.open (ins, outs, sampleRate, bufferSize, + chanIndex, fifoSize); + + if (err.isNotEmpty()) + { + close(); + lastError = err; + return err; + } + + chanIndex += d.numInputChans + d.numOutputChans; + } + + fifos.setSize (chanIndex, fifoSize); + fifos.clear(); + startThread (9); + + return String(); + } + + void close() override + { + stop(); + stopThread (10000); + fifos.clear(); + active = false; + + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->close(); + } + + BigInteger getActiveOutputChannels() const override + { + BigInteger chans; + int start = 0; + + for (int i = 0; i < devices.size(); ++i) + { + const int numChans = devices.getUnchecked(i)->getOutputChannelNames().size(); + + if (numChans > 0) + { + chans |= (devices.getUnchecked(i)->device->getActiveOutputChannels() << start); + start += numChans; + } + } + + return chans; + } + + BigInteger getActiveInputChannels() const override + { + BigInteger chans; + int start = 0; + + for (int i = 0; i < devices.size(); ++i) + { + const int numChans = devices.getUnchecked(i)->getInputChannelNames().size(); + + if (numChans > 0) + { + chans |= (devices.getUnchecked(i)->device->getActiveInputChannels() << start); + start += numChans; + } + } + + return chans; + } + + int getOutputLatencyInSamples() override + { + int lat = 0; + + for (int i = 0; i < devices.size(); ++i) + lat = jmax (lat, devices.getUnchecked(i)->device->getOutputLatencyInSamples()); + + return lat + currentBufferSize * 2; + } + + int getInputLatencyInSamples() override + { + int lat = 0; + + for (int i = 0; i < devices.size(); ++i) + lat = jmax (lat, devices.getUnchecked(i)->device->getInputLatencyInSamples()); + + return lat + currentBufferSize * 2; + } + + void start (AudioIODeviceCallback* newCallback) override + { + if (callback != newCallback) + { + stop(); + fifos.clear(); + + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->start(); + + if (newCallback != nullptr) + newCallback->audioDeviceAboutToStart (this); + + const ScopedLock sl (callbackLock); + callback = newCallback; + } + } + + void stop() override + { + AudioIODeviceCallback* lastCallback = nullptr; + + { + const ScopedLock sl (callbackLock); + std::swap (callback, lastCallback); + } + + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->device->stop(); + + if (lastCallback != nullptr) + lastCallback->audioDeviceStopped(); + } + + String getLastError() override + { + return lastError; + } + +private: + CriticalSection callbackLock; + AudioIODeviceCallback* callback; + double currentSampleRate; + int currentBufferSize; + bool active; + String lastError; + + AudioSampleBuffer fifos; + + void run() override + { + const int numSamples = currentBufferSize; + + AudioSampleBuffer buffer (fifos.getNumChannels(), numSamples); + buffer.clear(); + + Array inputChans; + Array outputChans; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + for (int j = 0; j < d.numInputChans; ++j) inputChans.add (buffer.getReadPointer (d.inputIndex + j)); + for (int j = 0; j < d.numOutputChans; ++j) outputChans.add (buffer.getWritePointer (d.outputIndex + j)); + } + + const int numInputChans = inputChans.size(); + const int numOutputChans = outputChans.size(); + + inputChans.add (nullptr); + outputChans.add (nullptr); + + const int blockSizeMs = jmax (1, (int) (1000 * numSamples / currentSampleRate)); + + jassert (numInputChans + numOutputChans == buffer.getNumChannels()); + + while (! threadShouldExit()) + { + readInput (buffer, numSamples, blockSizeMs); + + bool didCallback = true; + + { + const ScopedLock sl (callbackLock); + + if (callback != nullptr) + callback->audioDeviceIOCallback ((const float**) inputChans.getRawDataPointer(), numInputChans, + outputChans.getRawDataPointer(), numOutputChans, numSamples); + else + didCallback = false; + } + + if (didCallback) + { + pushOutputData (buffer, numSamples, blockSizeMs); + } + else + { + for (int i = 0; i < numOutputChans; ++i) + FloatVectorOperations::clear (outputChans[i], numSamples); + + reset(); + } + } + } + + void reset() + { + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->reset(); + } + + void underrun() + { + } + + void readInput (AudioSampleBuffer& buffer, const int numSamples, const int blockSizeMs) + { + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + d.done = (d.numInputChans == 0); + } + + for (int tries = 5;;) + { + bool anyRemaining = false; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + if (! d.done) + { + if (d.isInputReady (numSamples)) + { + d.readInput (buffer, numSamples); + d.done = true; + } + else + anyRemaining = true; + } + } + + if (! anyRemaining) + return; + + if (--tries == 0) + break; + + wait (blockSizeMs); + } + + for (int j = 0; j < devices.size(); ++j) + { + DeviceWrapper& d = *devices.getUnchecked(j); + + if (! d.done) + for (int i = 0; i < d.numInputChans; ++i) + buffer.clear (d.inputIndex + i, 0, numSamples); + } + } + + void pushOutputData (AudioSampleBuffer& buffer, const int numSamples, const int blockSizeMs) + { + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + d.done = (d.numOutputChans == 0); + } + + for (int tries = 5;;) + { + bool anyRemaining = false; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + if (! d.done) + { + if (d.isOutputReady (numSamples)) + { + d.pushOutputData (buffer, numSamples); + d.done = true; + } + else + anyRemaining = true; + } + } + + if ((! anyRemaining) || --tries == 0) + return; + + wait (blockSizeMs); + } + } + + //============================================================================== + struct DeviceWrapper : private AudioIODeviceCallback + { + DeviceWrapper (AudioIODeviceCombiner& cd, AudioIODevice* d, bool useIns, bool useOuts) + : owner (cd), device (d), inputIndex (0), outputIndex (0), + useInputs (useIns), useOutputs (useOuts), + inputFifo (32), outputFifo (32), done (false) + { + } + + ~DeviceWrapper() + { + close(); + } + + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, + double sampleRate, int bufferSize, + int channelIndex, + int fifoSize) + { + inputFifo.setTotalSize (fifoSize); + outputFifo.setTotalSize (fifoSize); + inputFifo.reset(); + outputFifo.reset(); + + String err (device->open (useInputs ? inputChannels : BigInteger(), + useOutputs ? outputChannels : BigInteger(), + sampleRate, bufferSize)); + + numInputChans = useInputs ? device->getActiveInputChannels().countNumberOfSetBits() : 0; + numOutputChans = useOutputs ? device->getActiveOutputChannels().countNumberOfSetBits() : 0; + + inputIndex = channelIndex; + outputIndex = channelIndex + numInputChans; + + return err; + } + + void close() + { + device->close(); + } + + void start() + { + reset(); + device->start (this); + } + + void reset() + { + inputFifo.reset(); + outputFifo.reset(); + } + + StringArray getOutputChannelNames() const { return useOutputs ? device->getOutputChannelNames() : StringArray(); } + StringArray getInputChannelNames() const { return useInputs ? device->getInputChannelNames() : StringArray(); } + + bool isInputReady (int numSamples) const noexcept + { + return numInputChans == 0 || inputFifo.getNumReady() >= numSamples; + } + + void readInput (AudioSampleBuffer& destBuffer, int numSamples) + { + if (numInputChans == 0) + return; + + int start1, size1, start2, size2; + inputFifo.prepareToRead (numSamples, start1, size1, start2, size2); + + for (int i = 0; i < numInputChans; ++i) + { + const int index = inputIndex + i; + float* const dest = destBuffer.getWritePointer (index); + const float* const src = owner.fifos.getReadPointer (index); + + if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); + } + + inputFifo.finishedRead (size1 + size2); + } + + bool isOutputReady (int numSamples) const noexcept + { + return numOutputChans == 0 || outputFifo.getFreeSpace() >= numSamples; + } + + void pushOutputData (AudioSampleBuffer& srcBuffer, int numSamples) + { + if (numOutputChans == 0) + return; + + int start1, size1, start2, size2; + outputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); + + for (int i = 0; i < numOutputChans; ++i) + { + const int index = outputIndex + i; + float* const dest = owner.fifos.getWritePointer (index); + const float* const src = srcBuffer.getReadPointer (index); + + if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); + } + + outputFifo.finishedWrite (size1 + size2); + } + + void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels, + float** outputChannelData, int numOutputChannels, + int numSamples) override + { + AudioSampleBuffer& buf = owner.fifos; + + if (numInputChannels > 0) + { + int start1, size1, start2, size2; + inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); + + if (size1 + size2 < numSamples) + { + inputFifo.reset(); + inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); + } + + for (int i = 0; i < numInputChannels; ++i) + { + float* const dest = buf.getWritePointer (inputIndex + i); + const float* const src = inputChannelData[i]; + + if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); + } + + inputFifo.finishedWrite (size1 + size2); + + if (numSamples > size1 + size2) + { + for (int i = 0; i < numInputChans; ++i) + buf.clear (inputIndex + i, size1 + size2, numSamples - (size1 + size2)); + + owner.underrun(); + } + } + + if (numOutputChannels > 0) + { + int start1, size1, start2, size2; + outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); + + if (size1 + size2 < numSamples) + { + Thread::sleep (1); + outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); + } + + for (int i = 0; i < numOutputChannels; ++i) + { + float* const dest = outputChannelData[i]; + const float* const src = buf.getReadPointer (outputIndex + i); + + if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); + } + + outputFifo.finishedRead (size1 + size2); + + if (numSamples > size1 + size2) + { + for (int i = 0; i < numOutputChannels; ++i) + FloatVectorOperations::clear (outputChannelData[i] + (size1 + size2), numSamples - (size1 + size2)); + + owner.underrun(); + } + } + + owner.notify(); + } + + void audioDeviceAboutToStart (AudioIODevice*) override {} + void audioDeviceStopped() override {} + + void audioDeviceError (const String& errorMessage) override + { + const ScopedLock sl (owner.callbackLock); + + if (owner.callback != nullptr) + owner.callback->audioDeviceError (errorMessage); + } + + AudioIODeviceCombiner& owner; + ScopedPointer device; + int inputIndex, numInputChans, outputIndex, numOutputChans; + bool useInputs, useOutputs; + AbstractFifo inputFifo, outputFifo; + bool done; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper) + }; + + OwnedArray devices; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner) +}; + + //============================================================================== class CoreAudioIODeviceType : public AudioIODeviceType { public: - //============================================================================== CoreAudioIODeviceType() : AudioIODeviceType ("CoreAudio"), hasScanned (false) @@ -1091,12 +1737,12 @@ public: pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; - if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, 0, &size) == noErr) + if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr) { - HeapBlock devs; + HeapBlock devs; devs.calloc (size, 1); - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, devs) == noErr) + if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr) { const int num = size / (int) sizeof (AudioDeviceID); for (int i = 0; i < num; ++i) @@ -1105,7 +1751,7 @@ public: size = sizeof (name); pa.mSelector = kAudioDevicePropertyDeviceName; - if (AudioObjectGetPropertyData (devs[i], &pa, 0, 0, &size, name) == noErr) + if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr) { const String nameString (String::fromUTF8 (name, (int) strlen (name))); const int numIns = getNumChannels (devs[i], true); @@ -1150,11 +1796,12 @@ public: // get the built-in mic rather than the built-in output with no inputs.. AudioObjectPropertyAddress pa; - pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; + pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice + : kAudioHardwarePropertyDefaultOutputDevice; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, &deviceID) == noErr) + if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr) { if (forInput) { @@ -1177,12 +1824,24 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - CoreAudioIODevice* const d = dynamic_cast (device); - if (d == nullptr) - return -1; + if (CoreAudioIODevice* const d = dynamic_cast (device)) + return asInput ? d->inputIndex + : d->outputIndex; - return asInput ? d->inputIndex - : d->outputIndex; + if (AudioIODeviceCombiner* const d = dynamic_cast (device)) + { + const Array devs (d->getDevices()); + + for (int i = 0; i < devs.size(); ++i) + { + const int index = getIndexOfDevice (devs.getUnchecked(i), asInput); + + if (index >= 0) + return index; + } + } + + return -1; } bool hasSeparateInputsAndOutputs() const { return true; } @@ -1192,27 +1851,41 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); + const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); - String deviceName (outputDeviceName); - if (deviceName.isEmpty()) - deviceName = inputDeviceName; + AudioDeviceID inputDeviceID = inputIds [inputIndex]; + AudioDeviceID outputDeviceID = outputIds [outputIndex]; - if (index >= 0) - return new CoreAudioIODevice (deviceName, - inputIds [inputIndex], - inputIndex, - outputIds [outputIndex], - outputIndex); + if (inputDeviceID == 0 && outputDeviceID == 0) + return nullptr; - return nullptr; + String combinedName (outputDeviceName.isEmpty() ? inputDeviceName : outputDeviceName); + + if (inputDeviceID == outputDeviceID) + return new CoreAudioIODevice (combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex); + + ScopedPointer in, out; + + if (inputDeviceID != 0) + in = new CoreAudioIODevice (inputDeviceName, inputDeviceID, inputIndex, 0, -1); + + if (outputDeviceID != 0) + out = new CoreAudioIODevice (outputDeviceName, 0, -1, outputDeviceID, outputIndex); + + if (in == nullptr) return out.release(); + if (out == nullptr) return in.release(); + + ScopedPointer combo (new AudioIODeviceCombiner (combinedName)); + combo->addDevice (in.release(), true, false); + combo->addDevice (out.release(), false, true); + return combo.release(); } //============================================================================== private: StringArray inputDeviceNames, outputDeviceNames; - Array inputIds, outputIds; + Array inputIds, outputIds; bool hasScanned; @@ -1226,12 +1899,12 @@ private: pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; - if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size) == noErr) + if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) { - HeapBlock bufList; + HeapBlock bufList; bufList.calloc (size, 1); - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList) == noErr) + if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr) { const int numStreams = (int) bufList->mNumberBuffers; @@ -1254,17 +1927,19 @@ private: static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) { - static_cast (clientData)->audioDeviceListChanged(); + static_cast (clientData)->audioDeviceListChanged(); return noErr; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType) +}; + }; //============================================================================== AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { - return new CoreAudioIODeviceType(); + return new CoreAudioClasses::CoreAudioIODeviceType(); } #undef JUCE_COREAUDIOLOG diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp index 7fdca63..5f8b3d5 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreMidi.cpp @@ -1,100 +1,99 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +#ifndef JUCE_LOG_COREMIDI_ERRORS + #define JUCE_LOG_COREMIDI_ERRORS 1 +#endif + namespace CoreMidiHelpers { - static bool logError (const OSStatus err, const int lineNum) + static bool checkError (const OSStatus err, const int lineNum) { if (err == noErr) return true; - Logger::writeToLog ("CoreMidi error: " + String (lineNum) + " - " + String::toHexString ((int) err)); - jassertfalse; + #if JUCE_LOG_COREMIDI_ERRORS + Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err)); + #endif + + (void) lineNum; return false; } #undef CHECK_ERROR - #define CHECK_ERROR(a) CoreMidiHelpers::logError (a, __LINE__) + #define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__) //============================================================================== - static String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) + static String getMidiObjectName (MIDIObjectRef entity) { String result; - CFStringRef str = 0; + CFStringRef str = nullptr; + MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); - MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &str); - - if (str != 0) + if (str != nullptr) { result = String::fromCFString (str); CFRelease (str); - str = 0; } - MIDIEntityRef entity = 0; + return result; + } + + static String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) + { + String result (getMidiObjectName (endpoint)); + + MIDIEntityRef entity = 0; // NB: don't attempt to use nullptr for refs - it fails in some types of build. MIDIEndpointGetEntity (endpoint, &entity); if (entity == 0) return result; // probably virtual if (result.isEmpty()) - { - // endpoint name has zero length - try the entity - MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); - - if (str != 0) - { - result += String::fromCFString (str); - CFRelease (str); - str = 0; - } - } + result = getMidiObjectName (entity); // endpoint name is empty - try the entity // now consider the device's name MIDIDeviceRef device = 0; MIDIEntityGetDevice (entity, &device); - if (device == 0) - return result; - MIDIObjectGetStringProperty (device, kMIDIPropertyName, &str); - - if (str != 0) + if (device != 0) { - const String s (String::fromCFString (str)); - CFRelease (str); + const String deviceName (getMidiObjectName (device)); - // if an external device has only one entity, throw away - // the endpoint name and just use the device name - if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2) + if (deviceName.isNotEmpty()) { - result = s; - } - else if (! result.startsWithIgnoreCase (s)) - { - // prepend the device name to the entity name - result = (s + " " + result).trimEnd(); + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2) + { + result = deviceName; + } + else if (! result.startsWithIgnoreCase (deviceName)) + { + // prepend the device name to the entity name + result = (deviceName + " " + result).trimEnd(); + } } } @@ -106,12 +105,12 @@ namespace CoreMidiHelpers String result; // Does the endpoint have connections? - CFDataRef connections = 0; + CFDataRef connections = nullptr; int numConnections = 0; MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); - if (connections != 0) + if (connections != nullptr) { numConnections = ((int) CFDataGetLength (connections)) / (int) sizeof (MIDIUniqueID); @@ -139,14 +138,7 @@ namespace CoreMidiHelpers else { // Connected to an external device (10.2) (or something else, catch-all) - CFStringRef str = 0; - MIDIObjectGetStringProperty (connObject, kMIDIPropertyName, &str); - - if (str != 0) - { - s = String::fromCFString (str); - CFRelease (str); - } + s = getMidiObjectName (connObject); } if (s.isNotEmpty()) @@ -163,11 +155,47 @@ namespace CoreMidiHelpers CFRelease (connections); } - if (result.isNotEmpty()) - return result; + if (result.isEmpty()) // Here, either the endpoint had no connections, or we failed to obtain names for them. + result = getEndpointName (endpoint, false); - // Here, either the endpoint had no connections, or we failed to obtain names for any of them. - return getEndpointName (endpoint, false); + return result; + } + + static StringArray findDevices (const bool forInput) + { + const ItemCount num = forInput ? MIDIGetNumberOfSources() + : MIDIGetNumberOfDestinations(); + StringArray s; + + for (ItemCount i = 0; i < num; ++i) + { + MIDIEndpointRef dest = forInput ? MIDIGetSource (i) + : MIDIGetDestination (i); + String name; + + if (dest != 0) + name = getConnectedEndpointName (dest); + + if (name.isEmpty()) + name = ""; + + s.add (name); + } + + return s; + } + + static void globalSystemChangeCallback (const MIDINotification*, void*) + { + // TODO.. Should pass-on this notification.. + } + + static String getGlobalMidiClientName() + { + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + return app->getApplicationName(); + + return "JUCE"; } static MIDIClientRef getGlobalMidiClient() @@ -176,14 +204,13 @@ namespace CoreMidiHelpers if (globalMidiClient == 0) { - String name ("JUCE"); + // Since OSX 10.6, the MIDIClientCreate function will only work + // correctly when called from the message thread! + jassert (MessageManager::getInstance()->isThisTheMessageThread()); - if (JUCEApplicationBase::getInstance() != nullptr) - name = JUCEApplicationBase::getInstance()->getApplicationName(); - - CFStringRef appName = name.toCFString(); - CHECK_ERROR (MIDIClientCreate (appName, 0, 0, &globalMidiClient)); - CFRelease (appName); + CFStringRef name = getGlobalMidiClientName().toCFString(); + CHECK_ERROR (MIDIClientCreate (name, &globalSystemChangeCallback, nullptr, &globalMidiClient)); + CFRelease (name); } return globalMidiClient; @@ -193,8 +220,8 @@ namespace CoreMidiHelpers class MidiPortAndEndpoint { public: - MidiPortAndEndpoint (MIDIPortRef port_, MIDIEndpointRef endPoint_) - : port (port_), endPoint (endPoint_) + MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) + : port (p), endPoint (ep) { } @@ -203,7 +230,7 @@ namespace CoreMidiHelpers if (port != 0) MIDIPortDispose (port); - if (port == 0 && endPoint != 0) // if port == 0, it means we created the endpoint, so it's safe to delete it + if (port == 0 && endPoint != 0) // if port == nullptr, it means we created the endpoint, so it's safe to delete it MIDIEndpointDispose (endPoint); } @@ -227,8 +254,8 @@ namespace CoreMidiHelpers class MidiPortAndCallback { public: - MidiPortAndCallback (MidiInputCallback& callback_) - : input (nullptr), active (false), callback (callback_), concatenator (2048) + MidiPortAndCallback (MidiInputCallback& cb) + : input (nullptr), active (false), callback (cb), concatenator (2048) { } @@ -238,10 +265,10 @@ namespace CoreMidiHelpers { const ScopedLock sl (callbackLock); - activeCallbacks.removeValue (this); + activeCallbacks.removeFirstMatchingValue (this); } - if (portAndEndpoint != nullptr && portAndEndpoint->port != 0) + if (portAndEndpoint != 0 && portAndEndpoint->port != 0) CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint)); } @@ -280,32 +307,8 @@ namespace CoreMidiHelpers } //============================================================================== -StringArray MidiOutput::getDevices() -{ - StringArray s; - - const ItemCount num = MIDIGetNumberOfDestinations(); - for (ItemCount i = 0; i < num; ++i) - { - MIDIEndpointRef dest = MIDIGetDestination (i); - String name; - - if (dest != 0) - name = CoreMidiHelpers::getConnectedEndpointName (dest); - - if (name.isEmpty()) - name = ""; - - s.add (name); - } - - return s; -} - -int MidiOutput::getDefaultDeviceIndex() -{ - return 0; -} +StringArray MidiOutput::getDevices() { return CoreMidiHelpers::findDevices (false); } +int MidiOutput::getDefaultDeviceIndex() { return 0; } MidiOutput* MidiOutput::openDevice (int index) { @@ -354,29 +357,34 @@ MidiOutput* MidiOutput::createNewDevice (const String& deviceName) MidiOutput::~MidiOutput() { + stopBackgroundThread(); + delete static_cast (internal); } void MidiOutput::sendMessageNow (const MidiMessage& message) { - CoreMidiHelpers::MidiPortAndEndpoint* const mpe = static_cast (internal); - #if JUCE_IOS const MIDITimeStamp timeStamp = mach_absolute_time(); #else const MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); #endif + HeapBlock allocatedPackets; + MIDIPacketList stackPacket; + MIDIPacketList* packetToSend = &stackPacket; + const size_t dataSize = (size_t) message.getRawDataSize(); + if (message.isSysEx()) { const int maxPacketSize = 256; - int pos = 0, bytesLeft = message.getRawDataSize(); + int pos = 0, bytesLeft = (int) dataSize; const int numPackets = (bytesLeft + maxPacketSize - 1) / maxPacketSize; - HeapBlock packets; - packets.malloc ((size_t) (32 * numPackets + message.getRawDataSize()), 1); - packets->numPackets = (UInt32) numPackets; + allocatedPackets.malloc ((size_t) (32 * (size_t) numPackets + dataSize), 1); + packetToSend = allocatedPackets; + packetToSend->numPackets = (UInt32) numPackets; - MIDIPacket* p = packets->packet; + MIDIPacket* p = packetToSend->packet; for (int i = 0; i < numPackets; ++i) { @@ -387,76 +395,59 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) bytesLeft -= p->length; p = MIDIPacketNext (p); } + } + else if (dataSize < 65536) // max packet size + { + const size_t stackCapacity = sizeof (stackPacket.packet->data); - mpe->send (packets); + if (dataSize > stackCapacity) + { + allocatedPackets.malloc ((sizeof (MIDIPacketList) - stackCapacity) + dataSize, 1); + packetToSend = allocatedPackets; + } + + packetToSend->numPackets = 1; + MIDIPacket& p = *(packetToSend->packet); + p.timeStamp = timeStamp; + p.length = (UInt16) dataSize; + memcpy (p.data, message.getRawData(), dataSize); } else { - MIDIPacketList packets; - packets.numPackets = 1; - packets.packet[0].timeStamp = timeStamp; - packets.packet[0].length = (UInt16) message.getRawDataSize(); - *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); - - mpe->send (&packets); + jassertfalse; // packet too large to send! + return; } + + static_cast (internal)->send (packetToSend); } //============================================================================== -StringArray MidiInput::getDevices() -{ - StringArray s; - - const ItemCount num = MIDIGetNumberOfSources(); - for (ItemCount i = 0; i < num; ++i) - { - MIDIEndpointRef source = MIDIGetSource (i); - String name; - - if (source != 0) - name = CoreMidiHelpers::getConnectedEndpointName (source); - - if (name.isEmpty()) - name = ""; - - s.add (name); - } - - return s; -} - -int MidiInput::getDefaultDeviceIndex() -{ - return 0; -} +StringArray MidiInput::getDevices() { return CoreMidiHelpers::findDevices (true); } +int MidiInput::getDefaultDeviceIndex() { return 0; } MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { - jassert (callback != 0); + jassert (callback != nullptr); using namespace CoreMidiHelpers; MidiInput* newInput = nullptr; if (isPositiveAndBelow (index, (int) MIDIGetNumberOfSources())) { - MIDIEndpointRef endPoint = MIDIGetSource ((ItemCount) index); - - if (endPoint != 0) + if (MIDIEndpointRef endPoint = MIDIGetSource ((ItemCount) index)) { CFStringRef name; if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name))) { - MIDIClientRef client = getGlobalMidiClient(); - - if (client != 0) + if (MIDIClientRef client = getGlobalMidiClient()) { MIDIPortRef port; ScopedPointer mpc (new MidiPortAndCallback (*callback)); if (CHECK_ERROR (MIDIInputPortCreate (client, name, midiInputProc, mpc, &port))) { - if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, 0))) + if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, nullptr))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint); @@ -488,9 +479,8 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba using namespace CoreMidiHelpers; MidiInput* mi = nullptr; - MIDIClientRef client = getGlobalMidiClient(); - if (client != 0) + if (MIDIClientRef client = getGlobalMidiClient()) { ScopedPointer mpc (new MidiPortAndCallback (*callback)); mpc->active = false; @@ -516,8 +506,7 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba return mi; } -MidiInput::MidiInput (const String& name_) - : name (name_) +MidiInput::MidiInput (const String& nm) : name (nm) { } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index 8bf1790..1b520dd 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -1,38 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ #undef WINDOWS -#undef log - -// #define ASIO_DEBUGGING 1 - -#if ASIO_DEBUGGING - #define log(a) { Logger::writeToLog (a); DBG (a) } -#else - #define log(a) {} -#endif /* The ASIO SDK *should* declare its callback functions as being __cdecl, but different versions seem to be pretty random about whether or not they do this. If you hit an error using these functions @@ -44,26 +34,41 @@ //============================================================================== namespace ASIODebugging { - #if ASIO_DEBUGGING - static void log (const String& context, long error) + #if JUCE_ASIO_DEBUGGING + #define JUCE_ASIO_LOG(msg) ASIODebugging::logMessage (msg) + #define JUCE_ASIO_LOG_ERROR(msg, errNum) ASIODebugging::logError ((msg), (errNum)) + + static void logMessage (String message) { - const char* err = "unknown error"; - - if (error == ASE_NotPresent) err = "Not Present"; - else if (error == ASE_HWMalfunction) err = "Hardware Malfunction"; - else if (error == ASE_InvalidParameter) err = "Invalid Parameter"; - else if (error == ASE_InvalidMode) err = "Invalid Mode"; - else if (error == ASE_SPNotAdvancing) err = "Sample position not advancing"; - else if (error == ASE_NoClock) err = "No Clock"; - else if (error == ASE_NoMemory) err = "Out of memory"; - - log ("!!error: " + context + " - " + err); + message = "ASIO: " + message; + DBG (message); + Logger::writeToLog (message); } - #define logError(a, b) ASIODebugging::log ((a), (b)) - #else - #define logError(a, b) {} - #endif + static void logError (const String& context, long error) + { + const char* err = "Unknown error"; + + switch (error) + { + case ASE_OK: return; + case ASE_NotPresent: err = "Not Present"; break; + case ASE_HWMalfunction: err = "Hardware Malfunction"; break; + case ASE_InvalidParameter: err = "Invalid Parameter"; break; + case ASE_InvalidMode: err = "Invalid Mode"; break; + case ASE_SPNotAdvancing: err = "Sample position not advancing"; break; + case ASE_NoClock: err = "No Clock"; break; + case ASE_NoMemory: err = "Out of memory"; break; + default: break; + } + + logMessage ("error: " + context + " - " + err); + } + #else + static void dummyLog() {} + #define JUCE_ASIO_LOG(msg) ASIODebugging::dummyLog() + #define JUCE_ASIO_LOG_ERROR(msg, errNum) (void) errNum; ASIODebugging::dummyLog() + #endif } //============================================================================== @@ -186,7 +191,7 @@ private: { while (--numSamples >= 0) { - *(uint16*) dest = ByteOrder::swapIfBigEndian ((uint16) (short) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + *(uint16*) dest = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } @@ -194,7 +199,7 @@ private: { while (--numSamples >= 0) { - *(uint16*) dest = ByteOrder::swapIfLittleEndian ((uint16) (short) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + *(uint16*) dest = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } @@ -232,7 +237,7 @@ private: { while (--numSamples >= 0) { - ByteOrder::littleEndian24BitToChars ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); + ByteOrder::littleEndian24BitToChars ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); dest += dstStrideBytes; } } @@ -240,7 +245,7 @@ private: { while (--numSamples >= 0) { - ByteOrder::bigEndian24BitToChars ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); + ByteOrder::bigEndian24BitToChars ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++)), dest); dest += dstStrideBytes; } } @@ -278,7 +283,7 @@ private: { while (--numSamples >= 0) { - *(uint32*) dest = ByteOrder::swapIfBigEndian ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + *(uint32*) dest = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } @@ -286,7 +291,7 @@ private: { while (--numSamples >= 0) { - *(uint32*) dest = ByteOrder::swapIfLittleEndian ((uint32) roundDoubleToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); + *(uint32*) dest = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * *src++))); dest += dstStrideBytes; } } @@ -299,27 +304,46 @@ static ASIOAudioIODevice* volatile currentASIODev[3] = { 0 }; extern HWND juce_messageWindowHandle; +class ASIOAudioIODeviceType; +static void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType*); + //============================================================================== class ASIOAudioIODevice : public AudioIODevice, private Timer { public: - ASIOAudioIODevice (const String& name_, const CLSID classId_, const int slotNumber, - const String& optionalDllForDirectLoading_) - : AudioIODevice (name_, "ASIO"), + ASIOAudioIODevice (ASIOAudioIODeviceType* ownerType, const String& devName, + const CLSID clsID, const int slotNumber) + : AudioIODevice (devName, "ASIO"), + owner (ownerType), asioObject (nullptr), - classId (classId_), - optionalDllForDirectLoading (optionalDllForDirectLoading_), + classId (clsID), + inputLatency (0), + outputLatency (0), + minSize (0), maxSize (0), + preferredSize (0), + granularity (0), + numClockSources (0), + currentBlockSizeSamples (0), currentBitDepth (16), currentSampleRate (0), + currentCallback (nullptr), + bufferIndex (0), + numActiveInputChans (0), + numActiveOutputChans (0), deviceIsOpen (false), isStarted (false), buffersCreated (false), + calledback (false), + littleEndian (false), postOutput (true), + needToReset (false), insideControlPanelModalLoop (false), shouldUsePreferredSize (false) { - name = name_; + name = devName; + inBuffers.calloc (4); + outBuffers.calloc (4); jassert (currentASIODev [slotNumber] == nullptr); currentASIODev [slotNumber] = this; @@ -334,120 +358,88 @@ public: currentASIODev[i] = nullptr; close(); - log ("ASIO - exiting"); + JUCE_ASIO_LOG ("closed"); removeCurrentDriver(); } void updateSampleRates() { // find a list of sample rates.. - const double possibleSampleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; - sampleRates.clear(); + const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; + Array newRates; if (asioObject != nullptr) { for (int index = 0; index < numElementsInArray (possibleSampleRates); ++index) - { - const long err = asioObject->canSampleRate (possibleSampleRates[index]); + if (asioObject->canSampleRate ((double) possibleSampleRates[index]) == 0) + newRates.add ((double) possibleSampleRates[index]); + } - if (err == 0) - { - sampleRates.add ((int) possibleSampleRates[index]); - log ("rate: " + String ((int) possibleSampleRates[index])); - } - else if (err != ASE_NoClock) - { - logError ("CanSampleRate", err); - } - } + if (newRates.size() == 0) + { + double cr = getSampleRate(); + JUCE_ASIO_LOG ("No sample rates supported - current rate: " + String ((int) cr)); - if (sampleRates.size() == 0) - { - double cr = 0; - const long err = asioObject->getSampleRate (&cr); - log ("No sample rates supported - current rate: " + String ((int) cr)); + if (cr > 0) + newRates.add ((int) cr); + } - if (err == 0) - sampleRates.add ((int) cr); - } + if (sampleRates != newRates) + { + sampleRates.swapWith (newRates); + + #if JUCE_ASIO_DEBUGGING + StringArray s; + for (int i = 0; i < sampleRates.size(); ++i) + s.add (String (sampleRates.getUnchecked(i))); + + JUCE_ASIO_LOG ("Rates: " + s.joinIntoString (" ")); + #endif } } - StringArray getOutputChannelNames() { return outputChannelNames; } - StringArray getInputChannelNames() { return inputChannelNames; } + StringArray getOutputChannelNames() override { return outputChannelNames; } + StringArray getInputChannelNames() override { return inputChannelNames; } - int getNumSampleRates() { return sampleRates.size(); } - double getSampleRate (int index) { return sampleRates [index]; } - - int getNumBufferSizesAvailable() { return bufferSizes.size(); } - int getBufferSizeSamples (int index) { return bufferSizes [index]; } - int getDefaultBufferSize() { return preferredSize; } + Array getAvailableSampleRates() override { return sampleRates; } + Array getAvailableBufferSizes() override { return bufferSizes; } + int getDefaultBufferSize() override { return preferredSize; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sr, - int bufferSizeSamples) + double sr, int bufferSizeSamples) override { - close(); - currentCallback = nullptr; + if (isOpen()) + close(); - if (bufferSizeSamples <= 0) + jassert (currentCallback == nullptr); + + if (bufferSizeSamples < 8 || bufferSizeSamples > 16384) shouldUsePreferredSize = true; - if (asioObject == nullptr || ! isASIOOpen) + if (asioObject == nullptr) { - log ("Warning: device not open"); - const String err (openDevice()); + const String openingError (openDevice()); - if (asioObject == nullptr || ! isASIOOpen) - return err; + if (asioObject == nullptr) + return openingError; } isStarted = false; bufferIndex = -1; - long err = 0; - long newPreferredSize = 0; - minSize = 0; - maxSize = 0; - granularity = 0; - if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == 0) - { - if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize) - shouldUsePreferredSize = true; + long err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans); + jassert (err == ASE_OK); - preferredSize = newPreferredSize; - } + bufferSizeSamples = readBufferSizes (bufferSizeSamples); - // unfortunate workaround for certain manufacturers whose drivers crash horribly if you make - // dynamic changes to the buffer size... - shouldUsePreferredSize = shouldUsePreferredSize - || getName().containsIgnoreCase ("Digidesign"); - - if (shouldUsePreferredSize) - { - log ("Using preferred size for buffer.."); - - if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == 0) - { - bufferSizeSamples = preferredSize; - } - else - { - bufferSizeSamples = 1024; - logError ("GetBufferSize1", err); - } - - shouldUsePreferredSize = false; - } - - int sampleRate = roundDoubleToInt (sr); + double sampleRate = sr; currentSampleRate = sampleRate; currentBlockSizeSamples = bufferSizeSamples; currentChansOut.clear(); currentChansIn.clear(); - zeromem (inBuffers, sizeof (inBuffers)); - zeromem (outBuffers, sizeof (outBuffers)); + inBuffers.clear (totalNumInputChans + 1); + outBuffers.clear (totalNumOutputChans + 1); updateSampleRates(); @@ -456,285 +448,155 @@ public: jassert (sampleRate != 0); if (sampleRate == 0) - sampleRate = 44100; + sampleRate = 44100.0; - ASIOClockSource clocks[32] = { 0 }; - long numSources = numElementsInArray (clocks); - asioObject->getClockSources (clocks, &numSources); - bool isSourceSet = false; + updateClockSources(); + currentSampleRate = getSampleRate(); - // careful not to remove this loop because it does more than just logging! - int i; - for (i = 0; i < numSources; ++i) - { - String s ("clock: "); - s += clocks[i].name; - - if (clocks[i].isCurrentSource) - { - isSourceSet = true; - s << " (cur)"; - } - - log (s); - } - - if (numSources > 1 && ! isSourceSet) - { - log ("setting clock source"); - asioObject->setClockSource (clocks[0].index); - Thread::sleep (20); - } - else - { - if (numSources == 0) - { - log ("ASIO - no clock sources!"); - } - } - - double cr = 0; - err = asioObject->getSampleRate (&cr); - if (err == 0) - { - currentSampleRate = cr; - } - else - { - logError ("GetSampleRate", err); - currentSampleRate = 0; - } - - error = String::empty; - needToReset = false; - isReSync = false; - err = 0; + error.clear(); buffersCreated = false; - if (currentSampleRate != sampleRate) + setSampleRate (sampleRate); + + if (needToReset) { - log ("ASIO samplerate: " + String (currentSampleRate) + " to " + String (sampleRate)); - err = asioObject->setSampleRate (sampleRate); + JUCE_ASIO_LOG (" Resetting"); + removeCurrentDriver(); - if (err == ASE_NoClock && numSources > 0) - { - log ("trying to set a clock source.."); - Thread::sleep (10); - err = asioObject->setClockSource (clocks[0].index); - if (err != 0) - { - logError ("SetClock", err); - } + loadDriver(); + const String error (initDriver()); - Thread::sleep (10); - err = asioObject->setSampleRate (sampleRate); - } + if (error.isNotEmpty()) + JUCE_ASIO_LOG ("ASIOInit: " + error); + + needToReset = false; } - if (err == 0) + const int totalBuffers = resetBuffers (inputChannels, outputChannels); + + setCallbackFunctions(); + + JUCE_ASIO_LOG ("disposing buffers"); + err = asioObject->disposeBuffers(); + + JUCE_ASIO_LOG ("creating buffers: " + String (totalBuffers) + ", " + String (currentBlockSizeSamples)); + err = asioObject->createBuffers (bufferInfos, totalBuffers, currentBlockSizeSamples, &callbacks); + + if (err != ASE_OK) { - currentSampleRate = sampleRate; + currentBlockSizeSamples = preferredSize; + JUCE_ASIO_LOG_ERROR ("create buffers 2", err); - if (needToReset) - { - if (isReSync) - { - log ("Resync request"); - } + asioObject->disposeBuffers(); + err = asioObject->createBuffers (bufferInfos, totalBuffers, currentBlockSizeSamples, &callbacks); + } - log ("! Resetting ASIO after sample rate change"); - removeCurrentDriver(); + if (err == ASE_OK) + { + buffersCreated = true; - loadDriver(); - const String error (initDriver()); + tempBuffer.calloc (totalBuffers * currentBlockSizeSamples + 32); - if (error.isNotEmpty()) - { - log ("ASIOInit: " + error); - } + int n = 0; + Array types; + currentBitDepth = 16; - needToReset = false; - isReSync = false; - } - - numActiveInputChans = 0; - numActiveOutputChans = 0; - - ASIOBufferInfo* info = bufferInfos; - int i; - for (i = 0; i < totalNumInputChans; ++i) + for (int i = 0; i < (int) totalNumInputChans; ++i) { if (inputChannels[i]) { - currentChansIn.setBit (i); - info->isInput = 1; - info->channelNum = i; - info->buffers[0] = info->buffers[1] = nullptr; - ++info; - ++numActiveInputChans; + inBuffers[n] = tempBuffer + (currentBlockSizeSamples * n); + + ASIOChannelInfo channelInfo = { 0 }; + channelInfo.channel = i; + channelInfo.isInput = 1; + asioObject->getChannelInfo (&channelInfo); + + types.addIfNotAlreadyThere (channelInfo.type); + inputFormat[n] = ASIOSampleFormat (channelInfo.type); + + currentBitDepth = jmax (currentBitDepth, inputFormat[n].bitDepth); + ++n; } } - for (i = 0; i < totalNumOutputChans; ++i) + jassert (numActiveInputChans == n); + n = 0; + + for (int i = 0; i < (int) totalNumOutputChans; ++i) { if (outputChannels[i]) { - currentChansOut.setBit (i); - info->isInput = 0; - info->channelNum = i; - info->buffers[0] = info->buffers[1] = nullptr; - ++info; - ++numActiveOutputChans; + outBuffers[n] = tempBuffer + (currentBlockSizeSamples * (numActiveInputChans + n)); + + ASIOChannelInfo channelInfo = { 0 }; + channelInfo.channel = i; + channelInfo.isInput = 0; + asioObject->getChannelInfo (&channelInfo); + + types.addIfNotAlreadyThere (channelInfo.type); + outputFormat[n] = ASIOSampleFormat (channelInfo.type); + + currentBitDepth = jmax (currentBitDepth, outputFormat[n].bitDepth); + ++n; } } - const int totalBuffers = numActiveInputChans + numActiveOutputChans; + jassert (numActiveOutputChans == n); - setCallbackFunctions(); + for (int i = types.size(); --i >= 0;) + JUCE_ASIO_LOG ("channel format: " + String (types[i])); - log ("disposing buffers"); - err = asioObject->disposeBuffers(); + jassert (n <= totalBuffers); - log ("creating buffers: " + String (totalBuffers) + ", " + String (currentBlockSizeSamples)); - err = asioObject->createBuffers (bufferInfos, - totalBuffers, - currentBlockSizeSamples, - &callbacks); + for (int i = 0; i < numActiveOutputChans; ++i) + { + outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[0], currentBlockSizeSamples); + outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[1], currentBlockSizeSamples); + } + + readLatencies(); + + asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); + deviceIsOpen = true; + + JUCE_ASIO_LOG ("starting"); + calledback = false; + err = asioObject->start(); if (err != 0) { - currentBlockSizeSamples = preferredSize; - logError ("create buffers 2", err); - - asioObject->disposeBuffers(); - err = asioObject->createBuffers (bufferInfos, - totalBuffers, - currentBlockSizeSamples, - &callbacks); - } - - if (err == 0) - { - buffersCreated = true; - - tempBuffer.calloc (totalBuffers * currentBlockSizeSamples + 32); - - int n = 0; - Array types; - currentBitDepth = 16; - - for (i = 0; i < jmin ((int) totalNumInputChans, (int) maxASIOChannels); ++i) - { - if (inputChannels[i]) - { - inBuffers[n] = tempBuffer + (currentBlockSizeSamples * n); - - ASIOChannelInfo channelInfo = { 0 }; - channelInfo.channel = i; - channelInfo.isInput = 1; - asioObject->getChannelInfo (&channelInfo); - - types.addIfNotAlreadyThere (channelInfo.type); - inputFormat[n] = ASIOSampleFormat (channelInfo.type); - - currentBitDepth = jmax (currentBitDepth, inputFormat[n].bitDepth); - ++n; - } - } - - jassert (numActiveInputChans == n); - n = 0; - - for (i = 0; i < jmin ((int) totalNumOutputChans, (int) maxASIOChannels); ++i) - { - if (outputChannels[i]) - { - outBuffers[n] = tempBuffer + (currentBlockSizeSamples * (numActiveInputChans + n)); - - ASIOChannelInfo channelInfo = { 0 }; - channelInfo.channel = i; - channelInfo.isInput = 0; - asioObject->getChannelInfo (&channelInfo); - - types.addIfNotAlreadyThere (channelInfo.type); - outputFormat[n] = ASIOSampleFormat (channelInfo.type); - - currentBitDepth = jmax (currentBitDepth, outputFormat[n].bitDepth); - ++n; - } - } - - jassert (numActiveOutputChans == n); - - for (i = types.size(); --i >= 0;) - { - log ("channel format: " + String (types[i])); - } - - jassert (n <= totalBuffers); - - for (i = 0; i < numActiveOutputChans; ++i) - { - outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[0], currentBlockSizeSamples); - outputFormat[i].clear (bufferInfos [numActiveInputChans + i].buffers[1], currentBlockSizeSamples); - } - - inputLatency = outputLatency = 0; - - if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) - { - log ("ASIO - no latencies"); - } - else - { - log ("ASIO latencies: " + String ((int) outputLatency) + ", " + String ((int) inputLatency)); - } - - deviceIsOpen = true; - - log ("starting ASIO"); - calledback = false; - err = asioObject->start(); - - if (err != 0) - { - deviceIsOpen = false; - log ("ASIO - stop on failure"); - Thread::sleep (10); - asioObject->stop(); - error = "Can't start device"; - Thread::sleep (10); - } - else - { - int count = 300; - while (--count > 0 && ! calledback) - Thread::sleep (10); - - isStarted = true; - - if (! calledback) - { - error = "Device didn't start correctly"; - log ("ASIO didn't callback - stopping.."); - asioObject->stop(); - } - } + deviceIsOpen = false; + JUCE_ASIO_LOG ("stop on failure"); + Thread::sleep (10); + asioObject->stop(); + error = "Can't start device"; + Thread::sleep (10); } else { - error = "Can't create i/o buffers"; + int count = 300; + while (--count > 0 && ! calledback) + Thread::sleep (10); + + isStarted = true; + + if (! calledback) + { + error = "Device didn't start correctly"; + JUCE_ASIO_LOG ("no callbacks - stopping.."); + asioObject->stop(); + } } } else { - error = "Can't set sample rate: "; - error << sampleRate; + error = "Can't create i/o buffers"; } if (error.isNotEmpty()) { - logError (error, err); + JUCE_ASIO_LOG_ERROR (error, err); disposeBuffers(); Thread::sleep (20); @@ -747,27 +609,24 @@ public: } needToReset = false; - isReSync = false; - return error; } - void close() + void close() override { - error = String::empty; + error.clear(); stopTimer(); stop(); - if (isASIOOpen && deviceIsOpen) + if (asioObject != nullptr && deviceIsOpen) { const ScopedLock sl (callbackLock); deviceIsOpen = false; isStarted = false; needToReset = false; - isReSync = false; - log ("ASIO - stopping"); + JUCE_ASIO_LOG ("stopping"); if (asioObject != nullptr) { @@ -781,20 +640,20 @@ public: } } - bool isOpen() { return deviceIsOpen || insideControlPanelModalLoop; } - bool isPlaying() { return isASIOOpen && (currentCallback != nullptr); } + bool isOpen() override { return deviceIsOpen || insideControlPanelModalLoop; } + bool isPlaying() override { return asioObject != nullptr && currentCallback != nullptr; } - int getCurrentBufferSizeSamples() { return currentBlockSizeSamples; } - double getCurrentSampleRate() { return currentSampleRate; } - int getCurrentBitDepth() { return currentBitDepth; } + int getCurrentBufferSizeSamples() override { return currentBlockSizeSamples; } + double getCurrentSampleRate() override { return currentSampleRate; } + int getCurrentBitDepth() override { return currentBitDepth; } - BigInteger getActiveOutputChannels() const { return currentChansOut; } - BigInteger getActiveInputChannels() const { return currentChansIn; } + BigInteger getActiveOutputChannels() const override { return currentChansOut; } + BigInteger getActiveInputChannels() const override { return currentChansIn; } - int getOutputLatencyInSamples() { return outputLatency + currentBlockSizeSamples / 4; } - int getInputLatencyInSamples() { return inputLatency + currentBlockSizeSamples / 4; } + int getOutputLatencyInSamples() override { return outputLatency + currentBlockSizeSamples / 4; } + int getInputLatencyInSamples() override { return inputLatency + currentBlockSizeSamples / 4; } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback) override { if (callback != nullptr) { @@ -805,7 +664,7 @@ public: } } - void stop() + void stop() override { AudioIODeviceCallback* const lastCallback = currentCallback; @@ -823,7 +682,7 @@ public: bool showControlPanel() { - log ("ASIO - showing control panel"); + JUCE_ASIO_LOG ("showing control panel"); bool done = false; @@ -841,7 +700,7 @@ public: const int spent = (int) Time::getMillisecondCounter() - (int) started; - log ("spent: " + String (spent)); + JUCE_ASIO_LOG ("spent: " + String (spent)); if (spent > 300) { @@ -858,35 +717,31 @@ public: void resetRequest() noexcept { - needToReset = true; + startTimer (500); } - void resyncRequest() noexcept - { - needToReset = true; - isReSync = true; - } - - void timerCallback() + void timerCallback() override { if (! insideControlPanelModalLoop) { stopTimer(); - // used to cause a reset - log ("! ASIO restart request!"); + JUCE_ASIO_LOG ("restart request!"); - if (deviceIsOpen) - { - AudioIODeviceCallback* const oldCallback = currentCallback; + AudioIODeviceCallback* const oldCallback = currentCallback; - close(); - open (BigInteger (currentChansIn), BigInteger (currentChansOut), - currentSampleRate, currentBlockSizeSamples); + close(); - if (oldCallback != nullptr) - start (oldCallback); - } + needToReset = true; + open (BigInteger (currentChansIn), BigInteger (currentChansOut), + currentSampleRate, currentBlockSizeSamples); + + reloadChannelNames(); + + if (oldCallback != nullptr) + start (oldCallback); + + sendASIODeviceChangeToListeners (owner); } else { @@ -896,19 +751,22 @@ public: private: //============================================================================== + WeakReference owner; IASIO* volatile asioObject; ASIOCallbacks callbacks; CLSID classId; - const String optionalDllForDirectLoading; String error; long totalNumInputChans, totalNumOutputChans; StringArray inputChannelNames, outputChannelNames; - Array sampleRates, bufferSizes; + Array sampleRates; + Array bufferSizes; long inputLatency, outputLatency; long minSize, maxSize, preferredSize, granularity; + ASIOClockSource clocks[32]; + int numClockSources; int volatile currentBlockSizeSamples; int volatile currentBitDepth; @@ -917,27 +775,323 @@ private: AudioIODeviceCallback* volatile currentCallback; CriticalSection callbackLock; - enum { maxASIOChannels = 160 }; - - ASIOBufferInfo bufferInfos [maxASIOChannels]; - float* inBuffers [maxASIOChannels]; - float* outBuffers [maxASIOChannels]; - - ASIOSampleFormat inputFormat [maxASIOChannels]; - ASIOSampleFormat outputFormat [maxASIOChannels]; + HeapBlock bufferInfos; + HeapBlock inBuffers, outBuffers; + HeapBlock inputFormat, outputFormat; WaitableEvent event1; HeapBlock tempBuffer; int volatile bufferIndex, numActiveInputChans, numActiveOutputChans; bool deviceIsOpen, isStarted, buffersCreated; - bool volatile isASIOOpen; bool volatile calledback; - bool volatile littleEndian, postOutput, needToReset, isReSync; + bool volatile littleEndian, postOutput, needToReset; bool volatile insideControlPanelModalLoop; bool volatile shouldUsePreferredSize; //============================================================================== + static String convertASIOString (char* const text, int length) + { + if (CharPointer_UTF8::isValidString (text, length)) + return String::fromUTF8 (text, length); + + WCHAR wideVersion [64] = { 0 }; + MultiByteToWideChar (CP_ACP, 0, text, length, wideVersion, numElementsInArray (wideVersion)); + return wideVersion; + } + + String getChannelName (int index, bool isInput) const + { + ASIOChannelInfo channelInfo = { 0 }; + channelInfo.channel = index; + channelInfo.isInput = isInput ? 1 : 0; + asioObject->getChannelInfo (&channelInfo); + + return convertASIOString (channelInfo.name, sizeof (channelInfo.name)); + } + + void reloadChannelNames() + { + if (asioObject != nullptr + && asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans) == ASE_OK) + { + inputChannelNames.clear(); + outputChannelNames.clear(); + + for (int i = 0; i < totalNumInputChans; ++i) + inputChannelNames.add (getChannelName (i, true)); + + for (int i = 0; i < totalNumOutputChans; ++i) + outputChannelNames.add (getChannelName (i, false)); + + outputChannelNames.trim(); + inputChannelNames.trim(); + outputChannelNames.appendNumbersToDuplicates (false, true); + inputChannelNames.appendNumbersToDuplicates (false, true); + } + } + + int readBufferSizes (int bufferSizeSamples) + { + minSize = 0; + maxSize = 0; + granularity = 0; + + long newPreferredSize = 0; + + if (asioObject->getBufferSize (&minSize, &maxSize, &newPreferredSize, &granularity) == ASE_OK) + { + if (preferredSize != 0 && newPreferredSize != 0 && newPreferredSize != preferredSize) + shouldUsePreferredSize = true; + + if (bufferSizeSamples < minSize || bufferSizeSamples > maxSize) + shouldUsePreferredSize = true; + + preferredSize = newPreferredSize; + } + + // unfortunate workaround for certain drivers which crash if you make + // dynamic changes to the buffer size... + shouldUsePreferredSize = shouldUsePreferredSize || getName().containsIgnoreCase ("Digidesign"); + + if (shouldUsePreferredSize) + { + JUCE_ASIO_LOG ("Using preferred size for buffer.."); + long err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity); + + if (err == ASE_OK) + { + bufferSizeSamples = (int) preferredSize; + } + else + { + bufferSizeSamples = 1024; + JUCE_ASIO_LOG_ERROR ("getBufferSize1", err); + } + + shouldUsePreferredSize = false; + } + + return bufferSizeSamples; + } + + int resetBuffers (const BigInteger& inputChannels, + const BigInteger& outputChannels) + { + numActiveInputChans = 0; + numActiveOutputChans = 0; + + ASIOBufferInfo* info = bufferInfos; + for (int i = 0; i < totalNumInputChans; ++i) + { + if (inputChannels[i]) + { + currentChansIn.setBit (i); + info->isInput = 1; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = nullptr; + ++info; + ++numActiveInputChans; + } + } + + for (int i = 0; i < totalNumOutputChans; ++i) + { + if (outputChannels[i]) + { + currentChansOut.setBit (i); + info->isInput = 0; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = nullptr; + ++info; + ++numActiveOutputChans; + } + } + + return numActiveInputChans + numActiveOutputChans; + } + + void addBufferSizes (long minSize, long maxSize, long preferredSize, long granularity) + { + // find a list of buffer sizes.. + JUCE_ASIO_LOG (String ((int) minSize) + "->" + String ((int) maxSize) + ", " + + String ((int) preferredSize) + ", " + String ((int) granularity)); + + if (granularity >= 0) + { + granularity = jmax (16, (int) granularity); + + for (int i = jmax ((int) (minSize + 15) & ~15, (int) granularity); i < jmin (6400, (int) maxSize); i += granularity) + bufferSizes.addIfNotAlreadyThere (granularity * (i / granularity)); + } + else if (granularity < 0) + { + for (int i = 0; i < 18; ++i) + { + const int s = (1 << i); + + if (s >= minSize && s <= maxSize) + bufferSizes.add (s); + } + } + + bufferSizes.addIfNotAlreadyThere (preferredSize); + + DefaultElementComparator comparator; + bufferSizes.sort (comparator); + } + + double getSampleRate() const + { + double cr = 0; + long err = asioObject->getSampleRate (&cr); + JUCE_ASIO_LOG_ERROR ("getSampleRate", err); + return cr; + } + + void setSampleRate (double newRate) + { + if (currentSampleRate != newRate) + { + JUCE_ASIO_LOG ("rate change: " + String (currentSampleRate) + " to " + String (newRate)); + long err = asioObject->setSampleRate (newRate); + + if (err == ASE_NoClock && numClockSources > 0) + { + JUCE_ASIO_LOG ("trying to set a clock source.."); + Thread::sleep (10); + err = asioObject->setClockSource (clocks[0].index); + JUCE_ASIO_LOG_ERROR ("setClockSource2", err); + + Thread::sleep (10); + err = asioObject->setSampleRate (newRate); + } + + if (err == 0) + currentSampleRate = newRate; + + // on fail, ignore the attempt to change rate, and run with the current one.. + } + } + + void updateClockSources() + { + zeromem (clocks, sizeof (clocks)); + long numSources = numElementsInArray (clocks); + asioObject->getClockSources (clocks, &numSources); + numClockSources = (int) numSources; + + bool isSourceSet = false; + + // careful not to remove this loop because it does more than just logging! + for (int i = 0; i < numClockSources; ++i) + { + String s ("clock: "); + s += clocks[i].name; + + if (clocks[i].isCurrentSource) + { + isSourceSet = true; + s << " (cur)"; + } + + JUCE_ASIO_LOG (s); + } + + if (numClockSources > 1 && ! isSourceSet) + { + JUCE_ASIO_LOG ("setting clock source"); + long err = asioObject->setClockSource (clocks[0].index); + JUCE_ASIO_LOG_ERROR ("setClockSource1", err); + Thread::sleep (20); + } + else + { + if (numClockSources == 0) + JUCE_ASIO_LOG ("no clock sources!"); + } + } + + void readLatencies() + { + inputLatency = outputLatency = 0; + + if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) + JUCE_ASIO_LOG ("getLatencies() failed"); + else + JUCE_ASIO_LOG ("Latencies: in = " + String ((int) inputLatency) + ", out = " + String ((int) outputLatency)); + } + + void createDummyBuffers (long preferredSize) + { + numActiveInputChans = 0; + numActiveOutputChans = 0; + + ASIOBufferInfo* info = bufferInfos; + int numChans = 0; + + for (int i = 0; i < jmin (2, (int) totalNumInputChans); ++i) + { + info->isInput = 1; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = nullptr; + ++info; + ++numChans; + } + + const int outputBufferIndex = numChans; + + for (int i = 0; i < jmin (2, (int) totalNumOutputChans); ++i) + { + info->isInput = 0; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = nullptr; + ++info; + ++numChans; + } + + setCallbackFunctions(); + + JUCE_ASIO_LOG ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); + + if (preferredSize > 0) + { + long err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks); + JUCE_ASIO_LOG_ERROR ("dummy buffers", err); + } + + long newInps = 0, newOuts = 0; + asioObject->getChannels (&newInps, &newOuts); + + if (totalNumInputChans != newInps || totalNumOutputChans != newOuts) + { + totalNumInputChans = newInps; + totalNumOutputChans = newOuts; + + JUCE_ASIO_LOG (String ((int) totalNumInputChans) + " in; " + String ((int) totalNumOutputChans) + " out"); + } + + updateSampleRates(); + reloadChannelNames(); + + for (int i = 0; i < totalNumOutputChans; ++i) + { + ASIOChannelInfo channelInfo = { 0 }; + channelInfo.channel = i; + channelInfo.isInput = 0; + asioObject->getChannelInfo (&channelInfo); + + outputFormat[i] = ASIOSampleFormat (channelInfo.type); + + if (i < 2) + { + // clear the channels that are used with the dummy stuff + outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredSize); + outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredSize); + } + } + } + void removeCurrentDriver() { if (asioObject != nullptr) @@ -951,80 +1105,73 @@ private: { removeCurrentDriver(); - JUCE_TRY + bool crashed = false; + bool ok = tryCreatingDriver (crashed); + + if (crashed) + JUCE_ASIO_LOG ("** Driver crashed while being opened"); + + return ok; + } + + bool tryCreatingDriver (bool& crashed) + { + #if ! JUCE_MINGW + __try + #endif { - if (CoCreateInstance (classId, 0, CLSCTX_INPROC_SERVER, - classId, (void**) &asioObject) == S_OK) - { - return true; - } - - // If a class isn't registered but we have a path for it, we can fallback to - // doing a direct load of the COM object (only available via the juce_createASIOAudioIODeviceForGUID function). - if (optionalDllForDirectLoading.isNotEmpty()) - { - HMODULE h = LoadLibrary (optionalDllForDirectLoading.toWideCharPointer()); - - if (h != 0) - { - typedef HRESULT (CALLBACK* DllGetClassObjectFunc) (REFCLSID clsid, REFIID iid, LPVOID* ppv); - DllGetClassObjectFunc dllGetClassObject = (DllGetClassObjectFunc) GetProcAddress (h, "DllGetClassObject"); - - if (dllGetClassObject != 0) - { - IClassFactory* classFactory = nullptr; - HRESULT hr = dllGetClassObject (classId, IID_IClassFactory, (void**) &classFactory); - - if (classFactory != nullptr) - { - hr = classFactory->CreateInstance (0, classId, (void**) &asioObject); - classFactory->Release(); - } - - return asioObject != nullptr; - } - } - } + return CoCreateInstance (classId, 0, CLSCTX_INPROC_SERVER, + classId, (void**) &asioObject) == S_OK; } - JUCE_CATCH_ALL - - asioObject = nullptr; + #if ! JUCE_MINGW + __except (EXCEPTION_EXECUTE_HANDLER) { crashed = true; } return false; + #endif + } + + String getLastDriverError() const + { + jassert (asioObject != nullptr); + char buffer [512] = { 0 }; + asioObject->getErrorMessage (buffer); + return String (buffer, sizeof (buffer) - 1); } String initDriver() { - if (asioObject != nullptr) + if (asioObject == nullptr) + return "No Driver"; + + const bool initOk = !! asioObject->init (juce_messageWindowHandle); + String driverError; + + // Get error message if init() failed, or if it's a buggy Denon driver, + // which returns true from init() even when it fails. + if ((! initOk) || getName().containsIgnoreCase ("denon dj")) + driverError = getLastDriverError(); + + if ((! initOk) && driverError.isEmpty()) + driverError = "Driver failed to initialise"; + + if (driverError.isEmpty()) { - char buffer [256] = { 0 }; - - if (! asioObject->init (juce_messageWindowHandle)) - { - asioObject->getErrorMessage (buffer); - return String (buffer, sizeof (buffer) - 1); - } - - // just in case any daft drivers expect this to be called.. - asioObject->getDriverName (buffer); - - return String::empty; + char buffer [512]; + asioObject->getDriverName (buffer); // just in case any flimsy drivers expect this to be called.. } - return "No Driver"; + return driverError; } String openDevice() { // open the device and get its info.. - log ("opening ASIO device: " + getName()); + JUCE_ASIO_LOG ("opening device: " + getName()); needToReset = false; - isReSync = false; outputChannelNames.clear(); inputChannelNames.clear(); bufferSizes.clear(); sampleRates.clear(); - isASIOOpen = false; deviceIsOpen = false; totalNumInputChans = 0; totalNumOutputChans = 0; @@ -1032,7 +1179,7 @@ private: numActiveOutputChans = 0; currentCallback = nullptr; - error = String::empty; + error.clear(); if (getName().isEmpty()) return error; @@ -1051,163 +1198,48 @@ private: if (asioObject != nullptr && (err = asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans)) == 0) { - log (String ((int) totalNumInputChans) + " in, " + String ((int) totalNumOutputChans) + " out"); + JUCE_ASIO_LOG (String ((int) totalNumInputChans) + " in, " + String ((int) totalNumOutputChans) + " out"); + + const int chansToAllocate = totalNumInputChans + totalNumOutputChans + 4; + bufferInfos.calloc (chansToAllocate); + inBuffers.calloc (chansToAllocate); + outBuffers.calloc (chansToAllocate); + inputFormat.calloc (chansToAllocate); + outputFormat.calloc (chansToAllocate); if ((err = asioObject->getBufferSize (&minSize, &maxSize, &preferredSize, &granularity)) == 0) { - // find a list of buffer sizes.. - log (String ((int) minSize) + " " + String ((int) maxSize) + " " + String ((int) preferredSize) + " " + String ((int) granularity)); + addBufferSizes (minSize, maxSize, preferredSize, granularity); - if (granularity >= 0) + double currentRate = getSampleRate(); + + if (currentRate < 1.0 || currentRate > 192001.0) { - granularity = jmax (1, (int) granularity); - - for (int i = jmax ((int) minSize, (int) granularity); i < jmin (6400, (int) maxSize); i += granularity) - bufferSizes.addIfNotAlreadyThere (granularity * (i / granularity)); - } - else if (granularity < 0) - { - for (int i = 0; i < 18; ++i) - { - const int s = (1 << i); - - if (s >= minSize && s <= maxSize) - bufferSizes.add (s); - } - } - - if (! bufferSizes.contains (preferredSize)) - bufferSizes.insert (0, preferredSize); - - double currentRate = 0; - asioObject->getSampleRate (¤tRate); - - if (currentRate <= 0.0 || currentRate > 192001.0) - { - log ("setting sample rate"); + JUCE_ASIO_LOG ("setting default sample rate"); err = asioObject->setSampleRate (44100.0); - if (err != 0) - { - logError ("setting sample rate", err); - } + JUCE_ASIO_LOG_ERROR ("setting sample rate", err); - asioObject->getSampleRate (¤tRate); + currentRate = getSampleRate(); } currentSampleRate = currentRate; postOutput = (asioObject->outputReady() == 0); if (postOutput) - { - log ("ASIO outputReady = ok"); - } + JUCE_ASIO_LOG ("outputReady true"); updateSampleRates(); - // ..because cubase does it at this point - inputLatency = outputLatency = 0; - if (asioObject->getLatencies (&inputLatency, &outputLatency) != 0) - { - log ("ASIO - no latencies"); - } - - log ("latencies: " + String ((int) inputLatency) + ", " + String ((int) outputLatency)); - - // create some dummy buffers now.. because cubase does.. - numActiveInputChans = 0; - numActiveOutputChans = 0; - - ASIOBufferInfo* info = bufferInfos; - int i, numChans = 0; - for (i = 0; i < jmin (2, (int) totalNumInputChans); ++i) - { - info->isInput = 1; - info->channelNum = i; - info->buffers[0] = info->buffers[1] = nullptr; - ++info; - ++numChans; - } - - const int outputBufferIndex = numChans; - - for (i = 0; i < jmin (2, (int) totalNumOutputChans); ++i) - { - info->isInput = 0; - info->channelNum = i; - info->buffers[0] = info->buffers[1] = nullptr; - ++info; - ++numChans; - } - - setCallbackFunctions(); - - log ("creating buffers (dummy): " + String (numChans) + ", " + String ((int) preferredSize)); - - if (preferredSize > 0) - { - err = asioObject->createBuffers (bufferInfos, numChans, preferredSize, &callbacks); - if (err != 0) - { - logError ("dummy buffers", err); - } - } - - long newInps = 0, newOuts = 0; - asioObject->getChannels (&newInps, &newOuts); - - if (totalNumInputChans != newInps || totalNumOutputChans != newOuts) - { - totalNumInputChans = newInps; - totalNumOutputChans = newOuts; - - log (String ((int) totalNumInputChans) + " in; " + String ((int) totalNumOutputChans) + " out"); - } - - updateSampleRates(); - - for (i = 0; i < totalNumInputChans; ++i) - { - ASIOChannelInfo channelInfo = { 0 }; - channelInfo.channel = i; - channelInfo.isInput = 1; - asioObject->getChannelInfo (&channelInfo); - - inputChannelNames.add (String (CharPointer_UTF8 (channelInfo.name))); - } - - for (i = 0; i < totalNumOutputChans; ++i) - { - ASIOChannelInfo channelInfo = { 0 }; - channelInfo.channel = i; - channelInfo.isInput = 0; - asioObject->getChannelInfo (&channelInfo); - - outputChannelNames.add (String (CharPointer_UTF8 (channelInfo.name))); - outputFormat[i] = ASIOSampleFormat (channelInfo.type); - - if (i < 2) - { - // clear the channels that are used with the dummy stuff - outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[0], preferredSize); - outputFormat[i].clear (bufferInfos [outputBufferIndex + i].buffers[1], preferredSize); - } - } - - outputChannelNames.trim(); - inputChannelNames.trim(); - outputChannelNames.appendNumbersToDuplicates (false, true); - inputChannelNames.appendNumbersToDuplicates (false, true); + readLatencies(); // ..doing these steps because cubase does so at this stage + createDummyBuffers (preferredSize); // in initialisation, and some devices fail if we don't. + readLatencies(); // start and stop because cubase does it.. - asioObject->getLatencies (&inputLatency, &outputLatency); + err = asioObject->start(); + // ignore an error here, as it might start later after setting other stuff up + JUCE_ASIO_LOG_ERROR ("start", err); - if ((err = asioObject->start()) != 0) - { - // ignore an error here, as it might start later after setting other stuff up - logError ("ASIO start", err); - } - - Thread::sleep (100); + Thread::sleep (80); asioObject->stop(); } else @@ -1228,21 +1260,18 @@ private: if (error.isNotEmpty()) { - logError (error, err); + JUCE_ASIO_LOG_ERROR (error, err); disposeBuffers(); removeCurrentDriver(); - isASIOOpen = false; } else { - isASIOOpen = true; - log ("ASIO device open"); + JUCE_ASIO_LOG ("device open"); } deviceIsOpen = false; needToReset = false; - isReSync = false; - + stopTimer(); return error; } @@ -1279,38 +1308,22 @@ private: const ScopedLock sl (callbackLock); - if (needToReset) - { - needToReset = false; - - if (isReSync) - { - log ("! ASIO resync"); - isReSync = false; - } - else - { - startTimer (20); - } - } - if (bi >= 0) { const int samps = currentBlockSizeSamples; if (currentCallback != nullptr) { - int i; - for (i = 0; i < numActiveInputChans; ++i) + for (int i = 0; i < numActiveInputChans; ++i) { - jassert (inBuffers[i]!= nullptr); + jassert (inBuffers[i] != nullptr); inputFormat[i].convertToFloat (infos[i].buffers[bi], inBuffers[i], samps); } - currentCallback->audioDeviceIOCallback ((const float**) inBuffers, numActiveInputChans, + currentCallback->audioDeviceIOCallback (const_cast (inBuffers.getData()), numActiveInputChans, outBuffers, numActiveOutputChans, samps); - for (i = 0; i < numActiveOutputChans; ++i) + for (int i = 0; i < numActiveOutputChans; ++i) { jassert (outBuffers[i] != nullptr); outputFormat[i].convertFromFloat (outBuffers[i], infos [numActiveInputChans + i].buffers[bi], samps); @@ -1347,7 +1360,39 @@ private: static long JUCE_ASIOCALLBACK asioMessagesCallback (long selector, long value, void*, double*) { - return ASIOAudioIODevice::asioMessagesCallback (selector, value, deviceIndex); + switch (selector) + { + case kAsioSelectorSupported: + if (value == kAsioResetRequest || value == kAsioEngineVersion || value == kAsioResyncRequest + || value == kAsioLatenciesChanged || value == kAsioSupportsInputMonitor) + return 1; + break; + + case kAsioBufferSizeChange: JUCE_ASIO_LOG ("kAsioBufferSizeChange"); return sendResetRequest (deviceIndex); + case kAsioResetRequest: JUCE_ASIO_LOG ("kAsioResetRequest"); return sendResetRequest (deviceIndex); + case kAsioResyncRequest: JUCE_ASIO_LOG ("kAsioResyncRequest"); return sendResetRequest (deviceIndex); + case kAsioLatenciesChanged: JUCE_ASIO_LOG ("kAsioLatenciesChanged"); return 1; + case kAsioEngineVersion: return 2; + + case kAsioSupportsTimeInfo: + case kAsioSupportsTimeCode: + return 0; + } + + return 0; + } + + static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate) + { + sendResetRequest (deviceIndex); + } + + static long sendResetRequest (int index) + { + if (currentASIODev[index] != nullptr) + currentASIODev[index]->resetRequest(); + + return 1; } static void setCallbacks (ASIOCallbacks& callbacks) @@ -1355,67 +1400,19 @@ private: callbacks.bufferSwitch = &bufferSwitchCallback; callbacks.asioMessage = &asioMessagesCallback; callbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfoCallback; + callbacks.sampleRateDidChange = &sampleRateChangedCallback; } }; void setCallbackFunctions() { - callbacks.sampleRateDidChange = &sampleRateChangedCallback; - if (currentASIODev[0] == this) ASIOCallbackFunctions<0>::setCallbacks (callbacks); else if (currentASIODev[1] == this) ASIOCallbackFunctions<1>::setCallbacks (callbacks); else if (currentASIODev[2] == this) ASIOCallbackFunctions<2>::setCallbacks (callbacks); else jassertfalse; } - //============================================================================== - static long asioMessagesCallback (long selector, long value, const int deviceIndex) - { - switch (selector) - { - case kAsioSelectorSupported: - if (value == kAsioResetRequest - || value == kAsioEngineVersion - || value == kAsioResyncRequest - || value == kAsioLatenciesChanged - || value == kAsioSupportsInputMonitor) - return 1; - break; - - case kAsioBufferSizeChange: - break; - - case kAsioResetRequest: - if (currentASIODev[deviceIndex] != nullptr) - currentASIODev[deviceIndex]->resetRequest(); - - return 1; - - case kAsioResyncRequest: - if (currentASIODev[deviceIndex] != nullptr) - currentASIODev[deviceIndex]->resyncRequest(); - - return 1; - - case kAsioLatenciesChanged: - return 1; - - case kAsioEngineVersion: - return 2; - - case kAsioSupportsTimeInfo: - case kAsioSupportsTimeCode: - return 0; - } - - return 0; - } - - static void JUCE_ASIOCALLBACK sampleRateChangedCallback (ASIOSampleRate) - { - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODevice) }; //============================================================================== @@ -1426,7 +1423,11 @@ public: : AudioIODeviceType ("ASIO"), hasScanned (false) { - CoInitialize (0); + } + + ~ASIOAudioIODeviceType() + { + masterReference.clear(); } //============================================================================== @@ -1510,16 +1511,23 @@ public: const int freeSlot = findFreeSlot(); if (freeSlot >= 0) - return new ASIOAudioIODevice (outputDeviceName, *(classIds [index]), freeSlot, String::empty); + return new ASIOAudioIODevice (this, outputDeviceName, + classIds.getReference (index), freeSlot); } return nullptr; } - //============================================================================== + void sendDeviceChangeToListeners() + { + callDeviceChangeListeners(); + } + + WeakReference::Master masterReference; + private: StringArray deviceNames; - OwnedArray classIds; + Array classIds; bool hasScanned; @@ -1596,9 +1604,9 @@ private: else deviceName = keyName; - log ("found " + deviceName); + JUCE_ASIO_LOG ("found " + deviceName); deviceNames.add (deviceName); - classIds.add (new CLSID (classId)); + classIds.add (classId); } } @@ -1607,25 +1615,16 @@ private: } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ASIOAudioIODeviceType) }; +void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType* type) +{ + if (type != nullptr) + type->sendDeviceChangeToListeners(); +} + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); } - -AudioIODevice* juce_createASIOAudioIODeviceForGUID (const String& name, - void* guid, - const String& optionalDllForDirectLoading) -{ - const int freeSlot = ASIOAudioIODeviceType::findFreeSlot(); - - if (freeSlot < 0) - return nullptr; - - return new ASIOAudioIODevice (name, *(CLSID*) guid, freeSlot, optionalDllForDirectLoading); -} - -#undef logError -#undef log diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp index 6f19c87..3c3befb 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDBurner.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -148,7 +147,7 @@ public: private: Pimpl& pimpl; - JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener); + JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener) }; DiskState getDiskState() @@ -166,8 +165,8 @@ public: if (type == 0) return noDisc; - else - return readOnlyDiskPresent; + + return readOnlyDiskPresent; } int getIntProperty (const LPOLESTR name, const int defaultReturn) const @@ -204,7 +203,7 @@ public: && SUCCEEDED (discRecorder->SetRecorderProperties (prop)); } - void timerCallback() + void timerCallback() override { const DiskState state = getDiskState(); @@ -377,10 +376,7 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) while (ok) { { - AudioSourceChannelInfo info; - info.buffer = &sourceBuffer; - info.numSamples = samplesPerBlock; - info.startSample = 0; + AudioSourceChannelInfo info (&sourceBuffer, 0, samplesPerBlock); sourceBuffer.clear(); source->getNextAudioBlock (info); @@ -395,9 +391,9 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat; CDSampleFormat left (buffer, 2); - left.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (0)), samplesPerBlock); + left.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (0)), samplesPerBlock); CDSampleFormat right (buffer + 2, 2); - right.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (1)), samplesPerBlock); + right.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (1)), samplesPerBlock); hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp index c32444d..064ca91 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_AudioCDReader.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -1069,7 +1068,7 @@ bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int sta } else { - const int framesInBuffer = buffer.getSize() / bytesPerFrame; + const int framesInBuffer = (int) (buffer.getSize() / bytesPerFrame); const int frameNeeded = (int) (startSampleInFile / samplesPerFrame); if (firstFrameInBuffer + framesInBuffer != frameNeeded) @@ -1297,7 +1296,7 @@ Array AudioCDReader::findIndexesInTrack (const int trackNumber) pos += samplesPerFrame * framesPerIndexRead; } - indexes.removeValue (trackStart); + indexes.removeFirstMatchingValue (trackStart); } return indexes; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 87d7b98..9c43a1d 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -125,15 +124,17 @@ extern "C" STDMETHOD(Stop) (THIS) PURE; STDMETHOD(Unlock) (THIS_ LPVOID, DWORD, LPVOID, DWORD) PURE; }; + + #undef INTERFACE } -//============================================================================== namespace juce { -namespace +//============================================================================== +namespace DSoundLogging { - String getDSErrorMessage (HRESULT hr) + String getErrorMessage (HRESULT hr) { const char* result = nullptr; @@ -163,38 +164,44 @@ namespace } //============================================================================== - #define DS_DEBUGGING 1 + #if JUCE_DIRECTSOUND_LOGGING + static void logMessage (String message) + { + message = "DSOUND: " + message; + DBG (message); + Logger::writeToLog (message); + } - #ifdef DS_DEBUGGING - #define CATCH JUCE_CATCH_EXCEPTION - #undef log - #define log(a) Logger::writeToLog(a); - #undef logError - #define logError(a) logDSError(a, __LINE__); - - static void logDSError (HRESULT hr, int lineNum) + static void logError (HRESULT hr, int lineNum) + { + if (FAILED (hr)) { - if (hr != S_OK) - { - String error ("DS error at line "); - error << lineNum << " - " << getDSErrorMessage (hr); - log (error); - } + String error ("Error at line "); + error << lineNum << ": " << getErrorMessage (hr); + logMessage (error); } - #else - #define CATCH JUCE_CATCH_ALL - #define log(a) - #define logError(a) - #endif + } - //============================================================================== + #define CATCH JUCE_CATCH_EXCEPTION + #define JUCE_DS_LOG(a) DSoundLogging::logMessage(a); + #define JUCE_DS_LOG_ERROR(a) DSoundLogging::logError(a, __LINE__); + #else + #define CATCH JUCE_CATCH_ALL + #define JUCE_DS_LOG(a) + #define JUCE_DS_LOG_ERROR(a) + #endif +} + +//============================================================================== +namespace +{ #define DSOUND_FUNCTION(functionName, params) \ typedef HRESULT (WINAPI *type##functionName) params; \ - static type##functionName ds##functionName = 0; + static type##functionName ds##functionName = nullptr; #define DSOUND_FUNCTION_LOAD(functionName) \ ds##functionName = (type##functionName) GetProcAddress (h, #functionName); \ - jassert (ds##functionName != 0); + jassert (ds##functionName != nullptr); typedef BOOL (CALLBACK *LPDSENUMCALLBACKW) (LPGUID, LPCWSTR, LPCWSTR, LPVOID); typedef BOOL (CALLBACK *LPDSENUMCALLBACKA) (LPGUID, LPCSTR, LPCSTR, LPVOID); @@ -206,7 +213,7 @@ namespace void initialiseDSoundFunctions() { - if (dsDirectSoundCreate == 0) + if (dsDirectSoundCreate == nullptr) { HMODULE h = LoadLibraryA ("dsound.dll"); @@ -216,6 +223,9 @@ namespace DSOUND_FUNCTION_LOAD (DirectSoundCaptureEnumerateW) } } + + // the overall size of buffer used is this value x the block size + enum { blocksPerOverallBuffer = 16 }; } //============================================================================== @@ -237,13 +247,11 @@ public: void close() { - HRESULT hr; - if (pOutputBuffer != nullptr) { - log ("closing dsound out: " + name); - hr = pOutputBuffer->Stop(); - logError (hr); + JUCE_DS_LOG ("closing output: " + name); + HRESULT hr = pOutputBuffer->Stop(); + JUCE_DS_LOG_ERROR (hr); (void) hr; pOutputBuffer->Release(); pOutputBuffer = nullptr; @@ -258,8 +266,8 @@ public: String open() { - log ("opening dsound out device: " + name + " rate=" + String (sampleRate) - + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples)); + JUCE_DS_LOG ("opening output: " + name + " rate=" + String (sampleRate) + + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples)); pDirectSound = nullptr; pOutputBuffer = nullptr; @@ -268,19 +276,19 @@ public: String error; HRESULT hr = E_NOINTERFACE; - if (dsDirectSoundCreate != 0) - hr = dsDirectSoundCreate (&guid, &pDirectSound, 0); + if (dsDirectSoundCreate != nullptr) + hr = dsDirectSoundCreate (&guid, &pDirectSound, nullptr); - if (hr == S_OK) + if (SUCCEEDED (hr)) { bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15; - totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15; + totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15; const int numChannels = 2; - hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */); - logError (hr); + hr = pDirectSound->SetCooperativeLevel (GetDesktopWindow(), 2 /* DSSCL_PRIORITY */); + JUCE_DS_LOG_ERROR (hr); - if (hr == S_OK) + if (SUCCEEDED (hr)) { IDirectSoundBuffer* pPrimaryBuffer; @@ -290,25 +298,25 @@ public: primaryDesc.dwBufferBytes = 0; primaryDesc.lpwfxFormat = 0; - log ("opening dsound out step 2"); + JUCE_DS_LOG ("co-op level set"); hr = pDirectSound->CreateSoundBuffer (&primaryDesc, &pPrimaryBuffer, 0); - logError (hr); + JUCE_DS_LOG_ERROR (hr); - if (hr == S_OK) + if (SUCCEEDED (hr)) { WAVEFORMATEX wfFormat; - wfFormat.wFormatTag = WAVE_FORMAT_PCM; - wfFormat.nChannels = (unsigned short) numChannels; - wfFormat.nSamplesPerSec = (DWORD) sampleRate; - wfFormat.wBitsPerSample = (unsigned short) bitDepth; - wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8); - wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = (unsigned short) numChannels; + wfFormat.nSamplesPerSec = (DWORD) sampleRate; + wfFormat.wBitsPerSample = (unsigned short) bitDepth; + wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * wfFormat.wBitsPerSample / 8); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; wfFormat.cbSize = 0; hr = pPrimaryBuffer->SetFormat (&wfFormat); - logError (hr); + JUCE_DS_LOG_ERROR (hr); - if (hr == S_OK) + if (SUCCEEDED (hr)) { DSBUFFERDESC secondaryDesc = { 0 }; secondaryDesc.dwSize = sizeof (DSBUFFERDESC); @@ -318,34 +326,34 @@ public: secondaryDesc.lpwfxFormat = &wfFormat; hr = pDirectSound->CreateSoundBuffer (&secondaryDesc, &pOutputBuffer, 0); - logError (hr); + JUCE_DS_LOG_ERROR (hr); - if (hr == S_OK) + if (SUCCEEDED (hr)) { - log ("opening dsound out step 3"); + JUCE_DS_LOG ("buffer created"); DWORD dwDataLen; unsigned char* pDSBuffData; hr = pOutputBuffer->Lock (0, (DWORD) totalBytesPerBuffer, (LPVOID*) &pDSBuffData, &dwDataLen, 0, 0, 0); - logError (hr); + JUCE_DS_LOG_ERROR (hr); - if (hr == S_OK) + if (SUCCEEDED (hr)) { zeromem (pDSBuffData, dwDataLen); hr = pOutputBuffer->Unlock (pDSBuffData, dwDataLen, 0, 0); - if (hr == S_OK) + if (SUCCEEDED (hr)) { hr = pOutputBuffer->SetCurrentPosition (0); - if (hr == S_OK) + if (SUCCEEDED (hr)) { hr = pOutputBuffer->Play (0, 0, 1 /* DSBPLAY_LOOPING */); - if (hr == S_OK) + if (SUCCEEDED (hr)) return String::empty; } } @@ -356,7 +364,7 @@ public: } } - error = getDSErrorMessage (hr); + error = DSoundLogging::getErrorMessage (hr); close(); return error; } @@ -387,10 +395,10 @@ public: continue; } - if (hr == S_OK) + if (SUCCEEDED (hr)) break; - logError (hr); + JUCE_DS_LOG_ERROR (hr); jassertfalse; return true; } @@ -400,7 +408,6 @@ public: playWriteGap += totalBytesPerBuffer; int bytesEmpty = (int) (playCursor - writeOffset); - if (bytesEmpty < 0) bytesEmpty += totalBytesPerBuffer; @@ -412,71 +419,47 @@ public: if (bytesEmpty >= bytesPerBuffer) { - void* lpbuf1 = nullptr; - void* lpbuf2 = nullptr; + int* buf1 = nullptr; + int* buf2 = nullptr; DWORD dwSize1 = 0; DWORD dwSize2 = 0; - HRESULT hr = pOutputBuffer->Lock ((DWORD) writeOffset, (DWORD) bytesPerBuffer, - &lpbuf1, &dwSize1, - &lpbuf2, &dwSize2, 0); + HRESULT hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer, + (void**) &buf1, &dwSize1, + (void**) &buf2, &dwSize2, 0); if (hr == MAKE_HRESULT (1, 0x878, 150)) // DSERR_BUFFERLOST { pOutputBuffer->Restore(); - hr = pOutputBuffer->Lock ((DWORD) writeOffset, (DWORD) bytesPerBuffer, - &lpbuf1, &dwSize1, - &lpbuf2, &dwSize2, 0); + hr = pOutputBuffer->Lock (writeOffset, (DWORD) bytesPerBuffer, + (void**) &buf1, &dwSize1, + (void**) &buf2, &dwSize2, 0); } - if (hr == S_OK) + if (SUCCEEDED (hr)) { if (bitDepth == 16) { - int* dest = static_cast (lpbuf1); const float* left = leftBuffer; const float* right = rightBuffer; int samples1 = (int) (dwSize1 >> 2); int samples2 = (int) (dwSize2 >> 2); - if (left == 0) + if (left == nullptr) { - while (--samples1 >= 0) - *dest++ = (convertInputValue (*right++) << 16); - - dest = static_cast (lpbuf2); - - while (--samples2 >= 0) - *dest++ = (convertInputValue (*right++) << 16); + for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (0, *right++); + for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (0, *right++); } - else if (right == 0) + else if (right == nullptr) { - while (--samples1 >= 0) - *dest++ = (0xffff & convertInputValue (*left++)); - - dest = static_cast (lpbuf2); - - while (--samples2 >= 0) - *dest++ = (0xffff & convertInputValue (*left++)); + for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, 0); + for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, 0); } else { - while (--samples1 >= 0) - { - const int l = convertInputValue (*left++); - const int r = convertInputValue (*right++); - *dest++ = (r << 16) | (0xffff & l); - } - - dest = static_cast (lpbuf2); - - while (--samples2 >= 0) - { - const int l = convertInputValue (*left++); - const int r = convertInputValue (*right++); - *dest++ = (r << 16) | (0xffff & l); - } + for (int* dest = buf1; --samples1 >= 0;) *dest++ = convertInputValues (*left++, *right++); + for (int* dest = buf2; --samples2 >= 0;) *dest++ = convertInputValues (*left++, *right++); } } else @@ -486,12 +469,12 @@ public: writeOffset = (writeOffset + dwSize1 + dwSize2) % totalBytesPerBuffer; - pOutputBuffer->Unlock (lpbuf1, dwSize1, lpbuf2, dwSize2); + pOutputBuffer->Unlock (buf1, dwSize1, buf2, dwSize2); } else { jassertfalse; - logError (hr); + JUCE_DS_LOG_ERROR (hr); } bytesEmpty -= bytesPerBuffer; @@ -519,12 +502,13 @@ private: int totalBytesPerBuffer, bytesPerBuffer; unsigned int lastPlayCursor; - static inline int convertInputValue (const float v) noexcept + static inline int convertInputValues (const float l, const float r) noexcept { - return jlimit (-32768, 32767, roundToInt (32767.0f * v)); + return jlimit (-32768, 32767, roundToInt (32767.0f * r)) << 16 + | (0xffff & jlimit (-32768, 32767, roundToInt (32767.0f * l))); } - JUCE_DECLARE_NON_COPYABLE (DSoundInternalOutChannel); + JUCE_DECLARE_NON_COPYABLE (DSoundInternalOutChannel) }; //============================================================================== @@ -546,13 +530,11 @@ public: void close() { - HRESULT hr; - if (pInputBuffer != nullptr) { - log ("closing dsound in: " + name); - hr = pInputBuffer->Stop(); - logError (hr); + JUCE_DS_LOG ("closing input: " + name); + HRESULT hr = pInputBuffer->Stop(); + JUCE_DS_LOG_ERROR (hr); (void) hr; pInputBuffer->Release(); pInputBuffer = nullptr; @@ -573,8 +555,8 @@ public: String open() { - log ("opening dsound in device: " + name - + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples)); + JUCE_DS_LOG ("opening input: " + name + + " rate=" + String (sampleRate) + " bits=" + String (bitDepth) + " buf=" + String (bufferSizeSamples)); pDirectSound = nullptr; pDirectSoundCapture = nullptr; @@ -582,27 +564,23 @@ public: readOffset = 0; totalBytesPerBuffer = 0; - String error; - HRESULT hr = E_NOINTERFACE; + HRESULT hr = dsDirectSoundCaptureCreate != nullptr + ? dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, nullptr) + : E_NOINTERFACE; - if (dsDirectSoundCaptureCreate != 0) - hr = dsDirectSoundCaptureCreate (&guid, &pDirectSoundCapture, 0); - - logError (hr); - - if (hr == S_OK) + if (SUCCEEDED (hr)) { const int numChannels = 2; bytesPerBuffer = (bufferSizeSamples * (bitDepth >> 2)) & ~15; - totalBytesPerBuffer = (3 * bytesPerBuffer) & ~15; + totalBytesPerBuffer = (blocksPerOverallBuffer * bytesPerBuffer) & ~15; WAVEFORMATEX wfFormat; - wfFormat.wFormatTag = WAVE_FORMAT_PCM; - wfFormat.nChannels = (unsigned short)numChannels; - wfFormat.nSamplesPerSec = (DWORD) sampleRate; - wfFormat.wBitsPerSample = (unsigned short)bitDepth; - wfFormat.nBlockAlign = (unsigned short)(wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); - wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = (unsigned short)numChannels; + wfFormat.nSamplesPerSec = (DWORD) sampleRate; + wfFormat.wBitsPerSample = (unsigned short) bitDepth; + wfFormat.nBlockAlign = (unsigned short) (wfFormat.nChannels * (wfFormat.wBitsPerSample / 8)); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; wfFormat.cbSize = 0; DSCBUFFERDESC captureDesc = { 0 }; @@ -611,22 +589,20 @@ public: captureDesc.dwBufferBytes = (DWORD) totalBytesPerBuffer; captureDesc.lpwfxFormat = &wfFormat; - log ("opening dsound in step 2"); + JUCE_DS_LOG ("object created"); hr = pDirectSoundCapture->CreateCaptureBuffer (&captureDesc, &pInputBuffer, 0); - logError (hr); - - if (hr == S_OK) + if (SUCCEEDED (hr)) { hr = pInputBuffer->Start (1 /* DSCBSTART_LOOPING */); - logError (hr); - if (hr == S_OK) + if (SUCCEEDED (hr)) return String::empty; } } - error = getDSErrorMessage (hr); + JUCE_DS_LOG_ERROR (hr); + const String error (DSoundLogging::getErrorMessage (hr)); close(); return error; @@ -637,7 +613,7 @@ public: if (pInputBuffer != nullptr) { DWORD capturePos; - pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*)&readOffset); + pInputBuffer->GetCurrentPosition (&capturePos, (DWORD*) &readOffset); } } @@ -648,9 +624,9 @@ public: DWORD capturePos, readPos; HRESULT hr = pInputBuffer->GetCurrentPosition (&capturePos, &readPos); - logError (hr); + JUCE_DS_LOG_ERROR (hr); - if (hr != S_OK) + if (FAILED (hr)) return true; int bytesFilled = (int) (readPos - readOffset); @@ -659,16 +635,16 @@ public: if (bytesFilled >= bytesPerBuffer) { - LPBYTE lpbuf1 = nullptr; - LPBYTE lpbuf2 = nullptr; + short* buf1 = nullptr; + short* buf2 = nullptr; DWORD dwsize1 = 0; DWORD dwsize2 = 0; HRESULT hr = pInputBuffer->Lock ((DWORD) readOffset, (DWORD) bytesPerBuffer, - (void**) &lpbuf1, &dwsize1, - (void**) &lpbuf2, &dwsize2, 0); + (void**) &buf1, &dwsize1, + (void**) &buf2, &dwsize2, 0); - if (hr == S_OK) + if (SUCCEEDED (hr)) { if (bitDepth == 16) { @@ -679,55 +655,20 @@ public: int samples1 = (int) (dwsize1 >> 2); int samples2 = (int) (dwsize2 >> 2); - const short* src = (const short*)lpbuf1; - - if (destL == 0) + if (destL == nullptr) { - while (--samples1 >= 0) - { - ++src; - *destR++ = *src++ * g; - } - - src = (const short*)lpbuf2; - - while (--samples2 >= 0) - { - ++src; - *destR++ = *src++ * g; - } + for (const short* src = buf1; --samples1 >= 0;) { ++src; *destR++ = *src++ * g; } + for (const short* src = buf2; --samples2 >= 0;) { ++src; *destR++ = *src++ * g; } } - else if (destR == 0) + else if (destR == nullptr) { - while (--samples1 >= 0) - { - *destL++ = *src++ * g; - ++src; - } - - src = (const short*)lpbuf2; - - while (--samples2 >= 0) - { - *destL++ = *src++ * g; - ++src; - } + for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; ++src; } + for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; ++src; } } else { - while (--samples1 >= 0) - { - *destL++ = *src++ * g; - *destR++ = *src++ * g; - } - - src = (const short*)lpbuf2; - - while (--samples2 >= 0) - { - *destL++ = *src++ * g; - *destR++ = *src++ * g; - } + for (const short* src = buf1; --samples1 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; } + for (const short* src = buf2; --samples2 >= 0;) { *destL++ = *src++ * g; *destR++ = *src++ * g; } } } else @@ -737,11 +678,11 @@ public: readOffset = (readOffset + dwsize1 + dwsize2) % totalBytesPerBuffer; - pInputBuffer->Unlock (lpbuf1, dwsize1, lpbuf2, dwsize2); + pInputBuffer->Unlock (buf1, dwsize1, buf2, dwsize2); } else { - logError (hr); + JUCE_DS_LOG_ERROR (hr); jassertfalse; } @@ -771,7 +712,7 @@ private: IDirectSoundCapture* pDirectSoundCapture; IDirectSoundCaptureBuffer* pInputBuffer; - JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel); + JUCE_DECLARE_NON_COPYABLE (DSoundInternalInChannel) }; //============================================================================== @@ -789,10 +730,7 @@ public: isOpen_ (false), isStarted (false), bufferSizeSamples (0), - totalSamplesOut (0), sampleRate (0.0), - inputBuffers (1, 1), - outputBuffers (1, 1), callback (nullptr) { if (outputDeviceIndex_ >= 0) @@ -815,7 +753,7 @@ public: String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, int bufferSizeSamples) + double sampleRate, int bufferSizeSamples) override { lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples); isOpen_ = lastError.isEmpty(); @@ -823,7 +761,7 @@ public: return lastError; } - void close() + void close() override { stop(); @@ -834,45 +772,48 @@ public: } } - bool isOpen() { return isOpen_ && isThreadRunning(); } - int getCurrentBufferSizeSamples() { return bufferSizeSamples; } - double getCurrentSampleRate() { return sampleRate; } - BigInteger getActiveOutputChannels() const { return enabledOutputs; } - BigInteger getActiveInputChannels() const { return enabledInputs; } - int getOutputLatencyInSamples() { return (int) (getCurrentBufferSizeSamples() * 1.5); } - int getInputLatencyInSamples() { return getOutputLatencyInSamples(); } - StringArray getOutputChannelNames() { return outChannels; } - StringArray getInputChannelNames() { return inChannels; } + bool isOpen() override { return isOpen_ && isThreadRunning(); } + int getCurrentBufferSizeSamples() override { return bufferSizeSamples; } + double getCurrentSampleRate() override { return sampleRate; } + BigInteger getActiveOutputChannels() const override { return enabledOutputs; } + BigInteger getActiveInputChannels() const override { return enabledInputs; } + int getOutputLatencyInSamples() override { return (int) (getCurrentBufferSizeSamples() * 1.5); } + int getInputLatencyInSamples() override { return getOutputLatencyInSamples(); } + StringArray getOutputChannelNames() override { return outChannels; } + StringArray getInputChannelNames() override { return inChannels; } - int getNumSampleRates() { return 4; } - int getDefaultBufferSize() { return 2560; } - int getNumBufferSizesAvailable() { return 50; } - - double getSampleRate (int index) + Array getAvailableSampleRates() override { - const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 }; - return samps [jlimit (0, 3, index)]; + static const double rates[] = { 44100.0, 48000.0, 88200.0, 96000.0 }; + return Array (rates, numElementsInArray (rates)); } - int getBufferSizeSamples (int index) + Array getAvailableBufferSizes() override { + Array r; int n = 64; - for (int i = 0; i < index; ++i) + + for (int i = 0; i < 50; ++i) + { + r.add (n); n += (n < 512) ? 32 : ((n < 1024) ? 64 : ((n < 2048) ? 128 : 256)); + } - return n; + return r; } - int getCurrentBitDepth() - { - int i, bits = 256; + int getDefaultBufferSize() override { return 2560; } - for (i = inChans.size(); --i >= 0;) + int getCurrentBitDepth() override + { + int bits = 256; + + for (int i = inChans.size(); --i >= 0;) bits = jmin (bits, inChans[i]->bitDepth); - for (i = outChans.size(); --i >= 0;) + for (int i = outChans.size(); --i >= 0;) bits = jmin (bits, outChans[i]->bitDepth); if (bits > 32) @@ -881,7 +822,7 @@ public: return bits; } - void start (AudioIODeviceCallback* call) + void start (AudioIODeviceCallback* call) override { if (isOpen_ && call != nullptr && ! isStarted) { @@ -900,7 +841,7 @@ public: } } - void stop() + void stop() override { if (isStarted) { @@ -916,8 +857,8 @@ public: } } - bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); } - String getLastError() { return lastError; } + bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); } + String getLastError() override { return lastError; } //============================================================================== StringArray inChannels, outChannels; @@ -928,13 +869,11 @@ private: bool isStarted; String lastError; - OwnedArray inChans; - OwnedArray outChans; + OwnedArray inChans; + OwnedArray outChans; WaitableEvent startEvent; int bufferSizeSamples; - int volatile totalSamplesOut; - int64 volatile lastBlockTime; double sampleRate; BigInteger enabledInputs, enabledOutputs; AudioSampleBuffer inputBuffers, outputBuffers; @@ -963,17 +902,16 @@ private: { sleep (5); - int i; - for (i = 0; i < outChans.size(); ++i) + for (int i = 0; i < outChans.size(); ++i) outChans.getUnchecked(i)->synchronisePosition(); - for (i = 0; i < inChans.size(); ++i) + for (int i = 0; i < inChans.size(); ++i) inChans.getUnchecked(i)->synchronisePosition(); } } public: - void run() + void run() override { while (! threadShouldExit()) { @@ -989,14 +927,13 @@ public: int numToDo = 0; uint32 startTime = Time::getMillisecondCounter(); - int i; - for (i = inChans.size(); --i >= 0;) + for (int i = inChans.size(); --i >= 0;) { inChans.getUnchecked(i)->doneFlag = false; ++numToDo; } - for (i = outChans.size(); --i >= 0;) + for (int i = outChans.size(); --i >= 0;) { outChans.getUnchecked(i)->doneFlag = false; ++numToDo; @@ -1009,7 +946,7 @@ public: for (;;) { - for (i = inChans.size(); --i >= 0;) + for (int i = inChans.size(); --i >= 0;) { DSoundInternalInChannel* const in = inChans.getUnchecked(i); @@ -1020,7 +957,7 @@ public: } } - for (i = outChans.size(); --i >= 0;) + for (int i = outChans.size(); --i >= 0;) { DSoundInternalOutChannel* const out = outChans.getUnchecked(i); @@ -1059,28 +996,19 @@ public: if (isStarted) { - JUCE_TRY - { - callback->audioDeviceIOCallback (const_cast (inputBuffers.getArrayOfChannels()), - inputBuffers.getNumChannels(), - outputBuffers.getArrayOfChannels(), - outputBuffers.getNumChannels(), - bufferSizeSamples); - } - JUCE_CATCH_EXCEPTION - - totalSamplesOut += bufferSizeSamples; + callback->audioDeviceIOCallback (inputBuffers.getArrayOfReadPointers(), inputBuffers.getNumChannels(), + outputBuffers.getArrayOfWritePointers(), outputBuffers.getNumChannels(), + bufferSizeSamples); } else { outputBuffers.clear(); - totalSamplesOut = 0; sleep (1); } } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODevice) }; //============================================================================== @@ -1132,7 +1060,7 @@ private: } BOOL outputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, outputDeviceNames, outputGuids); } - BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); } + BOOL inputEnumProc (LPGUID guid, LPCWSTR desc) { return enumProc (guid, desc, inputDeviceNames, inputGuids); } static BOOL CALLBACK outputEnumProcW (LPGUID lpGUID, LPCWSTR description, LPCWSTR, LPVOID object) { @@ -1151,7 +1079,6 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, double sampleRate_, int bufferSizeSamples_) { closeDevice(); - totalSamplesOut = 0; sampleRate = sampleRate_; @@ -1170,17 +1097,12 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, inputBuffers.setSize (jmax (1, enabledInputs.countNumberOfSetBits()), bufferSizeSamples); inputBuffers.clear(); - int i, numIns = 0; + int numIns = 0; - for (i = 0; i <= enabledInputs.getHighestBit(); i += 2) + for (int i = 0; i <= enabledInputs.getHighestBit(); i += 2) { - float* left = nullptr; - if (enabledInputs[i]) - left = inputBuffers.getSampleData (numIns++); - - float* right = nullptr; - if (enabledInputs[i + 1]) - right = inputBuffers.getSampleData (numIns++); + float* left = enabledInputs[i] ? inputBuffers.getWritePointer (numIns++) : nullptr; + float* right = enabledInputs[i + 1] ? inputBuffers.getWritePointer (numIns++) : nullptr; if (left != nullptr || right != nullptr) inChans.add (new DSoundInternalInChannel (dlh.inputDeviceNames [inputDeviceIndex], @@ -1198,15 +1120,10 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, outputBuffers.clear(); int numOuts = 0; - for (i = 0; i <= enabledOutputs.getHighestBit(); i += 2) + for (int i = 0; i <= enabledOutputs.getHighestBit(); i += 2) { - float* left = nullptr; - if (enabledOutputs[i]) - left = outputBuffers.getSampleData (numOuts++); - - float* right = nullptr; - if (enabledOutputs[i + 1]) - right = outputBuffers.getSampleData (numOuts++); + float* left = enabledOutputs[i] ? outputBuffers.getWritePointer (numOuts++) : nullptr; + float* right = enabledOutputs[i + 1] ? outputBuffers.getWritePointer (numOuts++) : nullptr; if (left != nullptr || right != nullptr) outChans.add (new DSoundInternalOutChannel (dlh.outputDeviceNames[outputDeviceIndex], @@ -1223,7 +1140,7 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); SetPriorityClass (GetCurrentProcess(), REALTIME_PRIORITY_CLASS); - for (i = 0; i < outChans.size(); ++i) + for (int i = 0; i < outChans.size(); ++i) { error = outChans[i]->open(); @@ -1236,7 +1153,7 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, if (error.isEmpty()) { - for (i = 0; i < inChans.size(); ++i) + for (int i = 0; i < inChans.size(); ++i) { error = inChans[i]->open(); @@ -1250,12 +1167,10 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, if (error.isEmpty()) { - totalSamplesOut = 0; - - for (i = 0; i < outChans.size(); ++i) + for (int i = 0; i < outChans.size(); ++i) outChans.getUnchecked(i)->synchronisePosition(); - for (i = 0; i < inChans.size(); ++i) + for (int i = 0; i < inChans.size(); ++i) inChans.getUnchecked(i)->synchronisePosition(); startThread (9); @@ -1265,7 +1180,7 @@ String DSoundAudioIODevice::openDevice (const BigInteger& inputChannels, } else { - log (error); + JUCE_DS_LOG ("Opening failed: " + error); } SetThreadPriority (GetCurrentThread(), oldThreadPri); @@ -1287,7 +1202,6 @@ public: initialiseDSoundFunctions(); } - //============================================================================== void scanForDevices() { hasScanned = true; @@ -1312,12 +1226,11 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - DSoundAudioIODevice* const d = dynamic_cast (device); - if (d == 0) - return -1; + if (DSoundAudioIODevice* const d = dynamic_cast (device)) + return asInput ? d->inputDeviceIndex + : d->outputDeviceIndex; - return asInput ? d->inputDeviceIndex - : d->outputDeviceIndex; + return -1; } bool hasSeparateInputsAndOutputs() const { return true; } @@ -1339,7 +1252,6 @@ public: } private: - //============================================================================== DSoundDeviceList deviceList; bool hasScanned; @@ -1355,8 +1267,7 @@ private: } } - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType) }; //============================================================================== @@ -1364,6 +1275,3 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); } - -#undef log -#undef logError diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp index 771cdb7..2737b1e 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_Midi.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,7 +25,6 @@ class MidiInCollector { public: - //============================================================================== MidiInCollector (MidiInput* const input_, MidiInputCallback& callback_) : deviceHandle (0), @@ -44,8 +42,7 @@ public: if (deviceHandle != 0) { - int count = 5; - while (--count >= 0) + for (int count = 5; --count >= 0;) { if (midiInClose (deviceHandle) == MMSYSERR_NOERROR) break; @@ -56,11 +53,12 @@ public: } //============================================================================== - void handleMessage (const uint32 message, const uint32 timeStamp) + void handleMessage (const uint8* bytes, const uint32 timeStamp) { - if ((message & 0xff) >= 0x80 && isStarted) + if (bytes[0] >= 0x80 && isStarted) { - concatenator.pushMidiData (&message, 3, convertTimeStamp (timeStamp), input, callback); + concatenator.pushMidiData (bytes, MidiMessage::getMessageLengthFromFirstByte (bytes[0]), + convertTimeStamp (timeStamp), input, callback); writeFinishedBlocks(); } } @@ -69,22 +67,25 @@ public: { if (isStarted && hdr->dwBytesRecorded > 0) { - concatenator.pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded, convertTimeStamp (timeStamp), input, callback); + concatenator.pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded, + convertTimeStamp (timeStamp), input, callback); writeFinishedBlocks(); } } void start() { - jassert (deviceHandle != 0); if (deviceHandle != 0 && ! isStarted) { activeMidiCollectors.addIfNotAlreadyThere (this); for (int i = 0; i < (int) numHeaders; ++i) + { + headers[i].prepare (deviceHandle); headers[i].write (deviceHandle); + } - startTime = Time::getMillisecondCounter(); + startTime = Time::getMillisecondCounterHiRes(); MMRESULT res = midiInStart (deviceHandle); if (res == MMSYSERR_NOERROR) @@ -106,20 +107,21 @@ public: isStarted = false; midiInReset (deviceHandle); midiInStop (deviceHandle); - activeMidiCollectors.removeValue (this); + activeMidiCollectors.removeFirstMatchingValue (this); unprepareAllHeaders(); concatenator.reset(); } } - static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR midiMessage, DWORD_PTR timeStamp) + static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR midiMessage, DWORD_PTR timeStamp) { MidiInCollector* const collector = reinterpret_cast (dwInstance); if (activeMidiCollectors.contains (collector)) { if (uMsg == MIM_DATA) - collector->handleMessage ((uint32) midiMessage, (uint32) timeStamp); + collector->handleMessage ((const uint8*) &midiMessage, (uint32) timeStamp); else if (uMsg == MIM_LONGDATA) collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); } @@ -134,33 +136,20 @@ private: MidiInputCallback& callback; MidiDataConcatenator concatenator; bool volatile isStarted; - uint32 startTime; + double startTime; class MidiHeader { public: - MidiHeader() + MidiHeader() {} + + void prepare (HMIDIIN deviceHandle) { zerostruct (hdr); hdr.lpData = data; hdr.dwBufferLength = (DWORD) numElementsInArray (data); - } - void write (HMIDIIN deviceHandle) - { - hdr.dwBytesRecorded = 0; - MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr, sizeof (hdr)); - res = midiInAddBuffer (deviceHandle, &hdr, sizeof (hdr)); - } - - void writeIfFinished (HMIDIIN deviceHandle) - { - if ((hdr.dwFlags & WHDR_DONE) != 0) - { - MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)); - (void) res; - write (deviceHandle); - } + midiInPrepareHeader (deviceHandle, &hdr, sizeof (hdr)); } void unprepare (HMIDIIN deviceHandle) @@ -175,11 +164,23 @@ private: } } + void write (HMIDIIN deviceHandle) + { + hdr.dwBytesRecorded = 0; + midiInAddBuffer (deviceHandle, &hdr, sizeof (hdr)); + } + + void writeIfFinished (HMIDIIN deviceHandle) + { + if ((hdr.dwFlags & WHDR_DONE) != 0) + write (deviceHandle); + } + private: MIDIHDR hdr; char data [256]; - JUCE_DECLARE_NON_COPYABLE (MidiHeader); + JUCE_DECLARE_NON_COPYABLE (MidiHeader) }; enum { numHeaders = 32 }; @@ -199,21 +200,21 @@ private: double convertTimeStamp (uint32 timeStamp) { - timeStamp += startTime; + double t = startTime + timeStamp; - const uint32 now = Time::getMillisecondCounter(); - if (timeStamp > now) + const double now = Time::getMillisecondCounterHiRes(); + if (t > now) { - if (timeStamp > now + 2) - --startTime; + if (t > now + 2.0) + startTime -= 1.0; - timeStamp = now; + t = now; } - return timeStamp * 0.001; + return t * 0.001; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector) }; Array MidiInCollector::activeMidiCollectors; @@ -296,18 +297,11 @@ MidiInput::MidiInput (const String& name_) MidiInput::~MidiInput() { - delete static_cast (internal); + delete static_cast (internal); } -void MidiInput::start() -{ - static_cast (internal)->start(); -} - -void MidiInput::stop() -{ - static_cast (internal)->stop(); -} +void MidiInput::start() { static_cast (internal)->start(); } +void MidiInput::stop() { static_cast (internal)->stop(); } //============================================================================== @@ -320,7 +314,7 @@ struct MidiOutHandle static Array activeHandles; private: - JUCE_LEAK_DETECTOR (MidiOutHandle); + JUCE_LEAK_DETECTOR (MidiOutHandle) }; Array MidiOutHandle::activeHandles; @@ -369,31 +363,28 @@ MidiOutput* MidiOutput::openDevice (int index) const UINT num = midiOutGetNumDevs(); int n = 0; + for (UINT i = 0; i < num; ++i) { - for (UINT i = 0; i < num; ++i) + MIDIOUTCAPS mc = { 0 }; + + if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) { - MIDIOUTCAPS mc = { 0 }; + // use the microsoft sw synth as a default - best not to allow deviceId + // to be MIDI_MAPPER, or else device sharing breaks + if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase ("microsoft")) + deviceId = i; - if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) + if (index == n) { - // use the microsoft sw synth as a default - best not to allow deviceId - // to be MIDI_MAPPER, or else device sharing breaks - if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase ("microsoft")) - deviceId = i; - - if (index == n) - { - deviceId = i; - break; - } - - ++n; + deviceId = i; + break; } + + ++n; } } - int i; - for (i = MidiOutHandle::activeHandles.size(); --i >= 0;) + for (int i = MidiOutHandle::activeHandles.size(); --i >= 0;) { MidiOutHandle* const han = MidiOutHandle::activeHandles.getUnchecked(i); @@ -407,7 +398,7 @@ MidiOutput* MidiOutput::openDevice (int index) } } - for (i = 4; --i >= 0;) + for (int i = 4; --i >= 0;) { HMIDIOUT h = 0; MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL); @@ -441,28 +432,26 @@ MidiOutput::~MidiOutput() { stopBackgroundThread(); - MidiOutHandle* const h = static_cast (internal); + MidiOutHandle* const h = static_cast (internal); if (MidiOutHandle::activeHandles.contains (h) && --(h->refCount) == 0) { midiOutClose (h->handle); - MidiOutHandle::activeHandles.removeValue (h); + MidiOutHandle::activeHandles.removeFirstMatchingValue (h); delete h; } } void MidiOutput::sendMessageNow (const MidiMessage& message) { - const MidiOutHandle* const handle = static_cast (internal); + const MidiOutHandle* const handle = static_cast (internal); - if (message.getRawDataSize() > 3 - || message.isSysEx()) + if (message.getRawDataSize() > 3 || message.isSysEx()) { MIDIHDR h = { 0 }; h.lpData = (char*) message.getRawData(); - h.dwBufferLength = (DWORD) message.getRawDataSize(); - h.dwBytesRecorded = (DWORD) message.getRawDataSize(); + h.dwBytesRecorded = h.dwBufferLength = (DWORD) message.getRawDataSize(); if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR) { @@ -489,7 +478,12 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) } else { - midiOutShortMsg (handle->handle, - *(unsigned int*) message.getRawData()); + for (int i = 0; i < 50; ++i) + { + if (midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()) != MIDIERR_NOTREADY) + break; + + Sleep (1); + } } } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index 951f304..98e276c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef WASAPI_ENABLE_LOGGING - #define WASAPI_ENABLE_LOGGING 0 +#ifndef JUCE_WASAPI_LOGGING + #define JUCE_WASAPI_LOGGING 0 #endif //============================================================================== @@ -34,48 +33,54 @@ namespace WasapiClasses void logFailure (HRESULT hr) { (void) hr; + jassert (hr != 0x800401f0); // If you hit this, it means you're trying to call from + // a thread which hasn't been initialised with CoInitialize(). - #if WASAPI_ENABLE_LOGGING + #if JUCE_WASAPI_LOGGING if (FAILED (hr)) { - String e; - e << Time::getCurrentTime().toString (true, true, true, true) - << " -- WASAPI error: "; + const char* m = nullptr; switch (hr) { - case E_POINTER: e << "E_POINTER"; break; - case E_INVALIDARG: e << "E_INVALIDARG"; break; - case AUDCLNT_E_NOT_INITIALIZED: e << "AUDCLNT_E_NOT_INITIALIZED"; break; - case AUDCLNT_E_ALREADY_INITIALIZED: e << "AUDCLNT_E_ALREADY_INITIALIZED"; break; - case AUDCLNT_E_WRONG_ENDPOINT_TYPE: e << "AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break; - case AUDCLNT_E_DEVICE_INVALIDATED: e << "AUDCLNT_E_DEVICE_INVALIDATED"; break; - case AUDCLNT_E_NOT_STOPPED: e << "AUDCLNT_E_NOT_STOPPED"; break; - case AUDCLNT_E_BUFFER_TOO_LARGE: e << "AUDCLNT_E_BUFFER_TOO_LARGE"; break; - case AUDCLNT_E_OUT_OF_ORDER: e << "AUDCLNT_E_OUT_OF_ORDER"; break; - case AUDCLNT_E_UNSUPPORTED_FORMAT: e << "AUDCLNT_E_UNSUPPORTED_FORMAT"; break; - case AUDCLNT_E_INVALID_SIZE: e << "AUDCLNT_E_INVALID_SIZE"; break; - case AUDCLNT_E_DEVICE_IN_USE: e << "AUDCLNT_E_DEVICE_IN_USE"; break; - case AUDCLNT_E_BUFFER_OPERATION_PENDING: e << "AUDCLNT_E_BUFFER_OPERATION_PENDING"; break; - case AUDCLNT_E_THREAD_NOT_REGISTERED: e << "AUDCLNT_E_THREAD_NOT_REGISTERED"; break; - case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: e << "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break; - case AUDCLNT_E_ENDPOINT_CREATE_FAILED: e << "AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break; - case AUDCLNT_E_SERVICE_NOT_RUNNING: e << "AUDCLNT_E_SERVICE_NOT_RUNNING"; break; - case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: e << "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break; - case AUDCLNT_E_EXCLUSIVE_MODE_ONLY: e << "AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break; - case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: e << "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break; - case AUDCLNT_E_EVENTHANDLE_NOT_SET: e << "AUDCLNT_E_EVENTHANDLE_NOT_SET"; break; - case AUDCLNT_E_INCORRECT_BUFFER_SIZE: e << "AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break; - case AUDCLNT_E_BUFFER_SIZE_ERROR: e << "AUDCLNT_E_BUFFER_SIZE_ERROR"; break; - case AUDCLNT_S_BUFFER_EMPTY: e << "AUDCLNT_S_BUFFER_EMPTY"; break; - case AUDCLNT_S_THREAD_ALREADY_REGISTERED: e << "AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break; - default: e << String::toHexString ((int) hr); break; + case E_POINTER: m = "E_POINTER"; break; + case E_INVALIDARG: m = "E_INVALIDARG"; break; + + #define JUCE_WASAPI_ERR(desc, n) \ + case MAKE_HRESULT(1, 0x889, n): m = #desc; break; + + JUCE_WASAPI_ERR (AUDCLNT_E_NOT_INITIALIZED, 0x001) + JUCE_WASAPI_ERR (AUDCLNT_E_ALREADY_INITIALIZED, 0x002) + JUCE_WASAPI_ERR (AUDCLNT_E_WRONG_ENDPOINT_TYPE, 0x003) + JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_INVALIDATED, 0x004) + JUCE_WASAPI_ERR (AUDCLNT_E_NOT_STOPPED, 0x005) + JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_TOO_LARGE, 0x006) + JUCE_WASAPI_ERR (AUDCLNT_E_OUT_OF_ORDER, 0x007) + JUCE_WASAPI_ERR (AUDCLNT_E_UNSUPPORTED_FORMAT, 0x008) + JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_SIZE, 0x009) + JUCE_WASAPI_ERR (AUDCLNT_E_DEVICE_IN_USE, 0x00a) + JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_OPERATION_PENDING, 0x00b) + JUCE_WASAPI_ERR (AUDCLNT_E_THREAD_NOT_REGISTERED, 0x00c) + JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED, 0x00e) + JUCE_WASAPI_ERR (AUDCLNT_E_ENDPOINT_CREATE_FAILED, 0x00f) + JUCE_WASAPI_ERR (AUDCLNT_E_SERVICE_NOT_RUNNING, 0x010) + JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED, 0x011) + JUCE_WASAPI_ERR (AUDCLNT_E_EXCLUSIVE_MODE_ONLY, 0x012) + JUCE_WASAPI_ERR (AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL, 0x013) + JUCE_WASAPI_ERR (AUDCLNT_E_EVENTHANDLE_NOT_SET, 0x014) + JUCE_WASAPI_ERR (AUDCLNT_E_INCORRECT_BUFFER_SIZE, 0x015) + JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_ERROR, 0x016) + JUCE_WASAPI_ERR (AUDCLNT_E_CPUUSAGE_EXCEEDED, 0x017) + JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_ERROR, 0x018) + JUCE_WASAPI_ERR (AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED, 0x019) + JUCE_WASAPI_ERR (AUDCLNT_E_INVALID_DEVICE_PERIOD, 0x020) + default: break; } - DBG (e); - jassertfalse; + Logger::writeToLog ("WASAPI error: " + (m != nullptr ? String (m) + : String::toHexString ((int) hr))); } - #endif + #endif } #undef check @@ -87,6 +92,213 @@ bool check (HRESULT hr) } //============================================================================== +} + +#if JUCE_MINGW + #define JUCE_COMCLASS(name, guid) \ + struct name; \ + template<> struct UUIDGetter { static CLSID get() { return uuidFromString (guid); } }; \ + struct name + + struct PROPERTYKEY + { + GUID fmtid; + DWORD pid; + }; + + WINOLEAPI PropVariantClear (PROPVARIANT*); +#else + #define JUCE_COMCLASS(name, guid) struct __declspec (uuid (guid)) name +#endif + +#ifndef KSDATAFORMAT_SUBTYPE_PCM + #define KSDATAFORMAT_SUBTYPE_PCM uuidFromString ("00000001-0000-0010-8000-00aa00389b71") + #define KSDATAFORMAT_SUBTYPE_IEEE_FLOAT uuidFromString ("00000003-0000-0010-8000-00aa00389b71") +#endif + +#define JUCE_IUNKNOWNCLASS(name, guid) JUCE_COMCLASS(name, guid) : public IUnknown +#define JUCE_COMCALL virtual HRESULT STDMETHODCALLTYPE + +enum EDataFlow +{ + eRender = 0, + eCapture = (eRender + 1), + eAll = (eCapture + 1) +}; + +enum { DEVICE_STATE_ACTIVE = 1 }; + +JUCE_IUNKNOWNCLASS (IPropertyStore, "886d8eeb-8cf2-4446-8d02-cdba1dbdcf99") +{ + JUCE_COMCALL GetCount (DWORD*) = 0; + JUCE_COMCALL GetAt (DWORD, PROPERTYKEY*) = 0; + JUCE_COMCALL GetValue (const PROPERTYKEY&, PROPVARIANT*) = 0; + JUCE_COMCALL SetValue (const PROPERTYKEY&, const PROPVARIANT&) = 0; + JUCE_COMCALL Commit() = 0; +}; + +JUCE_IUNKNOWNCLASS (IMMDevice, "D666063F-1587-4E43-81F1-B948E807363F") +{ + JUCE_COMCALL Activate (REFIID, DWORD, PROPVARIANT*, void**) = 0; + JUCE_COMCALL OpenPropertyStore (DWORD, IPropertyStore**) = 0; + JUCE_COMCALL GetId (LPWSTR*) = 0; + JUCE_COMCALL GetState (DWORD*) = 0; +}; + +JUCE_IUNKNOWNCLASS (IMMEndpoint, "1BE09788-6894-4089-8586-9A2A6C265AC5") +{ + JUCE_COMCALL GetDataFlow (EDataFlow*) = 0; +}; + +struct IMMDeviceCollection : public IUnknown +{ + JUCE_COMCALL GetCount (UINT*) = 0; + JUCE_COMCALL Item (UINT, IMMDevice**) = 0; +}; + +enum ERole +{ + eConsole = 0, + eMultimedia = (eConsole + 1), + eCommunications = (eMultimedia + 1) +}; + +JUCE_IUNKNOWNCLASS (IMMNotificationClient, "7991EEC9-7E89-4D85-8390-6C703CEC60C0") +{ + JUCE_COMCALL OnDeviceStateChanged (LPCWSTR, DWORD) = 0; + JUCE_COMCALL OnDeviceAdded (LPCWSTR) = 0; + JUCE_COMCALL OnDeviceRemoved (LPCWSTR) = 0; + JUCE_COMCALL OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) = 0; + JUCE_COMCALL OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) = 0; +}; + +JUCE_IUNKNOWNCLASS (IMMDeviceEnumerator, "A95664D2-9614-4F35-A746-DE8DB63617E6") +{ + JUCE_COMCALL EnumAudioEndpoints (EDataFlow, DWORD, IMMDeviceCollection**) = 0; + JUCE_COMCALL GetDefaultAudioEndpoint (EDataFlow, ERole, IMMDevice**) = 0; + JUCE_COMCALL GetDevice (LPCWSTR, IMMDevice**) = 0; + JUCE_COMCALL RegisterEndpointNotificationCallback (IMMNotificationClient*) = 0; + JUCE_COMCALL UnregisterEndpointNotificationCallback (IMMNotificationClient*) = 0; +}; + +JUCE_COMCLASS (MMDeviceEnumerator, "BCDE0395-E52F-467C-8E3D-C4579291692E"); + +typedef LONGLONG REFERENCE_TIME; + +enum AVRT_PRIORITY +{ + AVRT_PRIORITY_LOW = -1, + AVRT_PRIORITY_NORMAL, + AVRT_PRIORITY_HIGH, + AVRT_PRIORITY_CRITICAL +}; + +enum AUDCLNT_SHAREMODE +{ + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_SHAREMODE_EXCLUSIVE +}; + +JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") +{ + JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0; + JUCE_COMCALL GetBufferSize (UINT32*) = 0; + JUCE_COMCALL GetStreamLatency (REFERENCE_TIME*) = 0; + JUCE_COMCALL GetCurrentPadding (UINT32*) = 0; + JUCE_COMCALL IsFormatSupported (AUDCLNT_SHAREMODE, const WAVEFORMATEX*, WAVEFORMATEX**) = 0; + JUCE_COMCALL GetMixFormat (WAVEFORMATEX**) = 0; + JUCE_COMCALL GetDevicePeriod (REFERENCE_TIME*, REFERENCE_TIME*) = 0; + JUCE_COMCALL Start() = 0; + JUCE_COMCALL Stop() = 0; + JUCE_COMCALL Reset() = 0; + JUCE_COMCALL SetEventHandle (HANDLE) = 0; + JUCE_COMCALL GetService (REFIID, void**) = 0; +}; + +JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317") +{ + JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0; + JUCE_COMCALL ReleaseBuffer (UINT32) = 0; + JUCE_COMCALL GetNextPacketSize (UINT32*) = 0; +}; + +JUCE_IUNKNOWNCLASS (IAudioRenderClient, "F294ACFC-3146-4483-A7BF-ADDCA7C260E2") +{ + JUCE_COMCALL GetBuffer (UINT32, BYTE**) = 0; + JUCE_COMCALL ReleaseBuffer (UINT32, DWORD) = 0; +}; + +JUCE_IUNKNOWNCLASS (IAudioEndpointVolume, "5CDF2C82-841E-4546-9722-0CF74078229A") +{ + JUCE_COMCALL RegisterControlChangeNotify (void*) = 0; + JUCE_COMCALL UnregisterControlChangeNotify (void*) = 0; + JUCE_COMCALL GetChannelCount (UINT*) = 0; + JUCE_COMCALL SetMasterVolumeLevel (float, LPCGUID) = 0; + JUCE_COMCALL SetMasterVolumeLevelScalar (float, LPCGUID) = 0; + JUCE_COMCALL GetMasterVolumeLevel (float*) = 0; + JUCE_COMCALL GetMasterVolumeLevelScalar (float*) = 0; + JUCE_COMCALL SetChannelVolumeLevel (UINT, float, LPCGUID) = 0; + JUCE_COMCALL SetChannelVolumeLevelScalar (UINT, float, LPCGUID) = 0; + JUCE_COMCALL GetChannelVolumeLevel (UINT, float*) = 0; + JUCE_COMCALL GetChannelVolumeLevelScalar (UINT, float*) = 0; + JUCE_COMCALL SetMute (BOOL, LPCGUID) = 0; + JUCE_COMCALL GetMute (BOOL*) = 0; + JUCE_COMCALL GetVolumeStepInfo (UINT*, UINT*) = 0; + JUCE_COMCALL VolumeStepUp (LPCGUID) = 0; + JUCE_COMCALL VolumeStepDown (LPCGUID) = 0; + JUCE_COMCALL QueryHardwareSupport (DWORD*) = 0; + JUCE_COMCALL GetVolumeRange (float*, float*, float*) = 0; +}; + +enum AudioSessionDisconnectReason +{ + DisconnectReasonDeviceRemoval = 0, + DisconnectReasonServerShutdown = 1, + DisconnectReasonFormatChanged = 2, + DisconnectReasonSessionLogoff = 3, + DisconnectReasonSessionDisconnected = 4, + DisconnectReasonExclusiveModeOverride = 5 +}; + +enum AudioSessionState +{ + AudioSessionStateInactive = 0, + AudioSessionStateActive = 1, + AudioSessionStateExpired = 2 +}; + +JUCE_IUNKNOWNCLASS (IAudioSessionEvents, "24918ACC-64B3-37C1-8CA9-74A66E9957A8") +{ + JUCE_COMCALL OnDisplayNameChanged (LPCWSTR, LPCGUID) = 0; + JUCE_COMCALL OnIconPathChanged (LPCWSTR, LPCGUID) = 0; + JUCE_COMCALL OnSimpleVolumeChanged (float, BOOL, LPCGUID) = 0; + JUCE_COMCALL OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) = 0; + JUCE_COMCALL OnGroupingParamChanged (LPCGUID, LPCGUID) = 0; + JUCE_COMCALL OnStateChanged (AudioSessionState) = 0; + JUCE_COMCALL OnSessionDisconnected (AudioSessionDisconnectReason) = 0; +}; + +JUCE_IUNKNOWNCLASS (IAudioSessionControl, "F4B1A599-7266-4319-A8CA-E70ACB11E8CD") +{ + JUCE_COMCALL GetState (AudioSessionState*) = 0; + JUCE_COMCALL GetDisplayName (LPWSTR*) = 0; + JUCE_COMCALL SetDisplayName (LPCWSTR, LPCGUID) = 0; + JUCE_COMCALL GetIconPath (LPWSTR*) = 0; + JUCE_COMCALL SetIconPath (LPCWSTR, LPCGUID) = 0; + JUCE_COMCALL GetGroupingParam (GUID*) = 0; + JUCE_COMCALL SetGroupingParam (LPCGUID, LPCGUID) = 0; + JUCE_COMCALL RegisterAudioSessionNotification (IAudioSessionEvents*) = 0; + JUCE_COMCALL UnregisterAudioSessionNotification (IAudioSessionEvents*) = 0; +}; + +#undef JUCE_COMCALL +#undef JUCE_COMCLASS +#undef JUCE_IUNKNOWNCLASS + +//============================================================================== +namespace WasapiClasses +{ + String getDeviceID (IMMDevice* const device) { String s; @@ -104,7 +316,7 @@ String getDeviceID (IMMDevice* const device) EDataFlow getDataFlow (const ComSmartPtr& device) { EDataFlow flow = eRender; - ComSmartPtr endPoint; + ComSmartPtr endPoint; if (check (device.QueryInterface (endPoint))) (void) check (endPoint->GetDataFlow (&flow)); @@ -113,7 +325,7 @@ EDataFlow getDataFlow (const ComSmartPtr& device) int refTimeToSamples (const REFERENCE_TIME& t, const double sampleRate) noexcept { - return roundDoubleToInt (sampleRate * ((double) t) * 0.0000001); + return roundToInt (sampleRate * ((double) t) * 0.0000001); } void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) noexcept @@ -126,8 +338,8 @@ void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) n class WASAPIDeviceBase { public: - WASAPIDeviceBase (const ComSmartPtr & device_, const bool useExclusiveMode_) - : device (device_), + WASAPIDeviceBase (const ComSmartPtr& d, const bool exclusiveMode) + : device (d), sampleRate (0), defaultSampleRate (0), numChannels (0), @@ -135,12 +347,12 @@ public: minBufferSize (0), defaultBufferSize (0), latencySamples (0), - useExclusiveMode (useExclusiveMode_), + useExclusiveMode (exclusiveMode), sampleRateHasChanged (false) { clientEvent = CreateEvent (0, false, false, _T("JuceWASAPI")); - ComSmartPtr tempClient (createClient()); + ComSmartPtr tempClient (createClient()); if (tempClient == nullptr) return; @@ -160,17 +372,18 @@ public: defaultSampleRate = format.Format.nSamplesPerSec; minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate); defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate); + mixFormatChannelMask = format.dwChannelMask; rates.addUsingDefaultSort (defaultSampleRate); - static const double ratesToTest[] = { 44100.0, 48000.0, 88200.0, 96000.0 }; + static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; for (int i = 0; i < numElementsInArray (ratesToTest); ++i) { if (ratesToTest[i] == defaultSampleRate) continue; - format.Format.nSamplesPerSec = (DWORD) roundDoubleToInt (ratesToTest[i]); + format.Format.nSamplesPerSec = (DWORD) ratesToTest[i]; if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*) &format, 0))) @@ -179,13 +392,13 @@ public: } } - ~WASAPIDeviceBase() + virtual ~WASAPIDeviceBase() { device = nullptr; CloseHandle (clientEvent); } - bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; } + bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; } bool openClient (const double newSampleRate, const BigInteger& newChannels) { @@ -240,16 +453,17 @@ public: } //============================================================================== - ComSmartPtr device; - ComSmartPtr client; + ComSmartPtr device; + ComSmartPtr client; double sampleRate, defaultSampleRate; int numChannels, actualNumChannels; int minBufferSize, defaultBufferSize, latencySamples; + DWORD mixFormatChannelMask; const bool useExclusiveMode; - Array rates; + Array rates; HANDLE clientEvent; BigInteger channels; - Array channelMaps; + Array channelMaps; UINT32 actualBufferSize; int bytesPerSample; bool sampleRateHasChanged; @@ -258,10 +472,10 @@ public: private: //============================================================================== - class SessionEventCallback : public ComBaseClassHelper + class SessionEventCallback : public ComBaseClassHelper { public: - SessionEventCallback (WASAPIDeviceBase& owner_) : owner (owner_) {} + SessionEventCallback (WASAPIDeviceBase& d) : owner (d) {} JUCE_COMRESULT OnDisplayNameChanged (LPCWSTR, LPCGUID) { return S_OK; } JUCE_COMRESULT OnIconPathChanged (LPCWSTR, LPCGUID) { return S_OK; } @@ -281,11 +495,11 @@ private: private: WASAPIDeviceBase& owner; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SessionEventCallback) }; - ComSmartPtr audioSessionControl; - ComSmartPtr sessionEventCallback; + ComSmartPtr audioSessionControl; + ComSmartPtr sessionEventCallback; void createSessionEventCallback() { @@ -311,22 +525,21 @@ private: } //============================================================================== - const ComSmartPtr createClient() + ComSmartPtr createClient() { - ComSmartPtr client; + ComSmartPtr client; if (device != nullptr) - { - HRESULT hr = device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER, 0, (void**) client.resetAndGetPointerAddress()); - logFailure (hr); - } + logFailure (device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER, + nullptr, (void**) client.resetAndGetPointerAddress())); return client; } bool tryInitialisingWithFormat (const bool useFloat, const int bytesPerSampleToTry) { - WAVEFORMATEXTENSIBLE format = { 0 }; + WAVEFORMATEXTENSIBLE format; + zerostruct (format); if (numChannels <= 2 && bytesPerSampleToTry <= 2) { @@ -338,28 +551,21 @@ private: format.Format.cbSize = sizeof (WAVEFORMATEXTENSIBLE) - sizeof (WAVEFORMATEX); } - format.Format.nSamplesPerSec = (DWORD) roundDoubleToInt (sampleRate); - format.Format.nChannels = (WORD) numChannels; - format.Format.wBitsPerSample = (WORD) (8 * bytesPerSampleToTry); - format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * numChannels * bytesPerSampleToTry); - format.Format.nBlockAlign = (WORD) (numChannels * bytesPerSampleToTry); - format.SubFormat = useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; + format.Format.nSamplesPerSec = (DWORD) sampleRate; + format.Format.nChannels = (WORD) numChannels; + format.Format.wBitsPerSample = (WORD) (8 * bytesPerSampleToTry); + format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * numChannels * bytesPerSampleToTry); + format.Format.nBlockAlign = (WORD) (numChannels * bytesPerSampleToTry); + format.SubFormat = useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; format.Samples.wValidBitsPerSample = format.Format.wBitsPerSample; - - switch (numChannels) - { - case 1: format.dwChannelMask = SPEAKER_FRONT_CENTER; break; - case 2: format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break; - case 4: format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 6: format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break; - case 8: format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break; - default: break; - } + format.dwChannelMask = mixFormatChannelMask; WAVEFORMATEXTENSIBLE* nearestFormat = nullptr; - HRESULT hr = client->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, - (WAVEFORMATEX*) &format, useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat); + HRESULT hr = client->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, + (WAVEFORMATEX*) &format, + useExclusiveMode ? nullptr : (WAVEFORMATEX**) &nearestFormat); logFailure (hr); if (hr == S_FALSE && format.Format.nSamplesPerSec == nearestFormat->Format.nSamplesPerSec) @@ -377,7 +583,7 @@ private: GUID session; if (hr == S_OK && check (client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/, defaultPeriod, defaultPeriod, (WAVEFORMATEX*) &format, &session))) { actualNumChannels = format.Format.nChannels; @@ -391,15 +597,15 @@ private: return false; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIDeviceBase); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIDeviceBase) }; //============================================================================== class WASAPIInputDevice : public WASAPIDeviceBase { public: - WASAPIInputDevice (const ComSmartPtr & device_, const bool useExclusiveMode_) - : WASAPIDeviceBase (device_, useExclusiveMode_), + WASAPIInputDevice (const ComSmartPtr& d, const bool exclusiveMode) + : WASAPIDeviceBase (d, exclusiveMode), reservoir (1, 1) { } @@ -423,14 +629,14 @@ public: { closeClient(); captureClient = nullptr; - reservoir.setSize (0); + reservoir.reset(); } - template + template void updateFormatWithType (SourceType*) { - typedef AudioData::Pointer NativeType; - converter = new AudioData::ConverterInstance , NativeType> (actualNumChannels, 1); + typedef AudioData::Pointer NativeType; + converter = new AudioData::ConverterInstance, NativeType> (actualNumChannels, 1); } void updateFormat (bool isFloat) @@ -503,21 +709,21 @@ public: } } - ComSmartPtr captureClient; + ComSmartPtr captureClient; MemoryBlock reservoir; int reservoirSize, reservoirCapacity; - ScopedPointer converter; + ScopedPointer converter; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIInputDevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIInputDevice) }; //============================================================================== class WASAPIOutputDevice : public WASAPIDeviceBase { public: - WASAPIOutputDevice (const ComSmartPtr & device_, const bool useExclusiveMode_) - : WASAPIDeviceBase (device_, useExclusiveMode_) + WASAPIOutputDevice (const ComSmartPtr& d, const bool exclusiveMode) + : WASAPIDeviceBase (d, exclusiveMode) { } @@ -538,11 +744,11 @@ public: renderClient = nullptr; } - template + template void updateFormatWithType (DestType*) { - typedef AudioData::Pointer NativeType; - converter = new AudioData::ConverterInstance > (1, actualNumChannels); + typedef AudioData::Pointer NativeType; + converter = new AudioData::ConverterInstance > (1, actualNumChannels); } void updateFormat (bool isFloat) @@ -592,27 +798,28 @@ public: } } - ComSmartPtr renderClient; - ScopedPointer converter; + ComSmartPtr renderClient; + ScopedPointer converter; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIOutputDevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIOutputDevice) }; //============================================================================== class WASAPIAudioIODevice : public AudioIODevice, - public Thread + public Thread, + private AsyncUpdater { public: WASAPIAudioIODevice (const String& deviceName, const String& outputDeviceId_, const String& inputDeviceId_, - const bool useExclusiveMode_) + const bool exclusiveMode) : AudioIODevice (deviceName, "Windows Audio"), Thread ("Juce WASAPI"), outputDeviceId (outputDeviceId_), inputDeviceId (inputDeviceId_), - useExclusiveMode (useExclusiveMode_), + useExclusiveMode (exclusiveMode), isOpen_ (false), isStarted (false), currentBufferSizeSamples (0), @@ -629,7 +836,7 @@ public: bool initialise() { latencyIn = latencyOut = 0; - Array ratesIn, ratesOut; + Array ratesIn, ratesOut; if (createDevices()) { @@ -672,7 +879,7 @@ public: return false; } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray outChannels; @@ -683,7 +890,7 @@ public: return outChannels; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray inChannels; @@ -694,47 +901,47 @@ public: return inChannels; } - int getNumSampleRates() { return sampleRates.size(); } - double getSampleRate (int index) { return sampleRates [index]; } - int getNumBufferSizesAvailable() { return bufferSizes.size(); } - int getBufferSizeSamples (int index) { return bufferSizes [index]; } - int getDefaultBufferSize() { return defaultBufferSize; } + Array getAvailableSampleRates() override { return sampleRates; } + Array getAvailableBufferSizes() override { return bufferSizes; } + int getDefaultBufferSize() override { return defaultBufferSize; } - int getCurrentBufferSizeSamples() { return currentBufferSizeSamples; } - double getCurrentSampleRate() { return currentSampleRate; } - int getCurrentBitDepth() { return 32; } - int getOutputLatencyInSamples() { return latencyOut; } - int getInputLatencyInSamples() { return latencyIn; } - BigInteger getActiveOutputChannels() const { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); } - BigInteger getActiveInputChannels() const { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); } - String getLastError() { return lastError; } + int getCurrentBufferSizeSamples() override { return currentBufferSizeSamples; } + double getCurrentSampleRate() override { return currentSampleRate; } + int getCurrentBitDepth() override { return 32; } + int getOutputLatencyInSamples() override { return latencyOut; } + int getInputLatencyInSamples() override { return latencyIn; } + BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); } + BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); } + String getLastError() override { return lastError; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, int bufferSizeSamples) + double sampleRate, int bufferSizeSamples) override { close(); - lastError = String::empty; + lastError.clear(); if (sampleRates.size() == 0 && inputDevice != nullptr && outputDevice != nullptr) { - lastError = "The input and output devices don't share a common sample rate!"; + lastError = TRANS("The input and output devices don't share a common sample rate!"); return lastError; } - currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize); - currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate; + currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize); + currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate; + lastKnownInputChannels = inputChannels; + lastKnownOutputChannels = outputChannels; if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels)) { - lastError = "Couldn't open the input device!"; + lastError = TRANS("Couldn't open the input device!"); return lastError; } if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels)) { close(); - lastError = "Couldn't open the output device!"; + lastError = TRANS("Couldn't open the output device!"); return lastError; } @@ -746,24 +953,24 @@ public: if (inputDevice != nullptr && inputDevice->client != nullptr) { - latencyIn = (int) (inputDevice->latencySamples + inputDevice->actualBufferSize + inputDevice->minBufferSize); + latencyIn = (int) (inputDevice->latencySamples + currentBufferSizeSamples); if (! check (inputDevice->client->Start())) { close(); - lastError = "Couldn't start the input device!"; + lastError = TRANS("Couldn't start the input device!"); return lastError; } } if (outputDevice != nullptr && outputDevice->client != nullptr) { - latencyOut = (int) (outputDevice->latencySamples + outputDevice->actualBufferSize + outputDevice->minBufferSize); + latencyOut = (int) (outputDevice->latencySamples + currentBufferSizeSamples); if (! check (outputDevice->client->Start())) { close(); - lastError = "Couldn't start the output device!"; + lastError = TRANS("Couldn't start the output device!"); return lastError; } } @@ -772,7 +979,7 @@ public: return lastError; } - void close() + void close() override { stop(); signalThreadShouldExit(); @@ -788,10 +995,10 @@ public: isOpen_ = false; } - bool isOpen() { return isOpen_ && isThreadRunning(); } - bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); } + bool isOpen() override { return isOpen_ && isThreadRunning(); } + bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); } - void start (AudioIODeviceCallback* call) + void start (AudioIODeviceCallback* call) override { if (isOpen_ && call != nullptr && ! isStarted) { @@ -810,7 +1017,7 @@ public: } } - void stop() + void stop() override { if (isStarted) { @@ -829,8 +1036,8 @@ public: void setMMThreadPriority() { DynamicLibrary dll ("avrt.dll"); - JUCE_DLL_FUNCTION (AvSetMmThreadCharacteristicsW, avSetMmThreadCharacteristics, HANDLE, dll, (LPCWSTR, LPDWORD)) - JUCE_DLL_FUNCTION (AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, dll, (HANDLE, AVRT_PRIORITY)) + JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadCharacteristicsW, avSetMmThreadCharacteristics, HANDLE, (LPCWSTR, LPDWORD)) + JUCE_LOAD_WINAPI_FUNCTION (dll, AvSetMmThreadPriority, avSetMmThreadPriority, HANDLE, (HANDLE, AVRT_PRIORITY)) if (avSetMmThreadCharacteristics != 0 && avSetMmThreadPriority != 0) { @@ -842,19 +1049,19 @@ public: } } - void run() + void run() override { setMMThreadPriority(); - const int bufferSize = currentBufferSizeSamples; - const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits(); - const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits(); - bool sampleRateChanged = false; + const int bufferSize = currentBufferSizeSamples; + const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits(); + const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits(); + bool sampleRateChanged = false; - AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32); + AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32); AudioSampleBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32); - float** const inputBuffers = ins.getArrayOfChannels(); - float** const outputBuffers = outs.getArrayOfChannels(); + float** const inputBuffers = ins.getArrayOfWritePointers(); + float** const outputBuffers = outs.getArrayOfWritePointers(); ins.clear(); while (! threadShouldExit()) @@ -867,33 +1074,37 @@ public: break; if (inputDevice->sampleRateHasChanged) + { sampleRateChanged = true; + sampleRateChangedByOutput = false; + } } - JUCE_TRY { const ScopedLock sl (startStopLock); if (isStarted) - callback->audioDeviceIOCallback (const_cast (inputBuffers), numInputBuffers, + callback->audioDeviceIOCallback (const_cast (inputBuffers), numInputBuffers, outputBuffers, numOutputBuffers, bufferSize); else outs.clear(); } - JUCE_CATCH_EXCEPTION if (outputDevice != nullptr) { - outputDevice->copyBuffers (const_cast (outputBuffers), numOutputBuffers, bufferSize, *this); + outputDevice->copyBuffers (const_cast (outputBuffers), numOutputBuffers, bufferSize, *this); if (outputDevice->sampleRateHasChanged) + { sampleRateChanged = true; + sampleRateChangedByOutput = true; + } } if (sampleRateChanged) { - // xxx one of the devices has had its sample rate changed externally.. not 100% sure how - // to handle this.. + triggerAsyncUpdate(); + break; // Quit the thread... will restart it later! } } } @@ -910,25 +1121,28 @@ private: double defaultSampleRate; int minBufferSize, defaultBufferSize; int latencyIn, latencyOut; - Array sampleRates; - Array bufferSizes; + Array sampleRates; + Array bufferSizes; // Active state... bool isOpen_, isStarted; int currentBufferSizeSamples; double currentSampleRate; + bool sampleRateChangedByOutput; AudioIODeviceCallback* callback; CriticalSection startStopLock; + BigInteger lastKnownInputChannels, lastKnownOutputChannels; + //============================================================================== bool createDevices() { - ComSmartPtr enumerator; + ComSmartPtr enumerator; if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) return false; - ComSmartPtr deviceCollection; + ComSmartPtr deviceCollection; if (! check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress()))) return false; @@ -938,7 +1152,7 @@ private: for (UINT32 i = 0; i < numDevices; ++i) { - ComSmartPtr device; + ComSmartPtr device; if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) continue; @@ -959,7 +1173,33 @@ private: } //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice); + void handleAsyncUpdate() override + { + stop(); + + outputDevice = nullptr; + inputDevice = nullptr; + initialise(); + + open (lastKnownInputChannels, lastKnownOutputChannels, + getChangedSampleRate(), currentBufferSizeSamples); + + start (callback); + } + + double getChangedSampleRate() const + { + if (outputDevice != nullptr && sampleRateChangedByOutput) + return outputDevice->defaultSampleRate; + + if (inputDevice != nullptr && ! sampleRateChangedByOutput) + return inputDevice->defaultSampleRate; + + return 0.0; + } + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice) }; @@ -975,6 +1215,12 @@ public: { } + ~WASAPIAudioIODeviceType() + { + if (notifyClient != nullptr) + enumerator->UnregisterEndpointNotificationCallback (notifyClient); + } + //============================================================================== void scanForDevices() { @@ -1006,9 +1252,12 @@ public: int getIndexOfDevice (AudioIODevice* device, bool asInput) const { jassert (hasScanned); // need to call scanForDevices() before doing this - WASAPIAudioIODevice* const d = dynamic_cast (device); - return d == nullptr ? -1 : (asInput ? inputDeviceIds.indexOf (d->inputDeviceId) - : outputDeviceIds.indexOf (d->outputDeviceId)); + + if (WASAPIAudioIODevice* const d = dynamic_cast (device)) + return asInput ? inputDeviceIds.indexOf (d->inputDeviceId) + : outputDeviceIds.indexOf (d->outputDeviceId); + + return -1; } bool hasSeparateInputsAndOutputs() const { return true; } @@ -1045,16 +1294,42 @@ public: private: bool hasScanned; + ComSmartPtr enumerator; + + //============================================================================== + class ChangeNotificationClient : public ComBaseClassHelper + { + public: + ChangeNotificationClient (WASAPIAudioIODeviceType& d) + : ComBaseClassHelper (0), device (d) {} + + HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); } + HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); } + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged (LPCWSTR, DWORD) { return notify(); } + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); } + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); } + + private: + WASAPIAudioIODeviceType& device; + + HRESULT notify() { device.triggerAsyncDeviceChangeCallback(); return S_OK; } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeNotificationClient) + }; + + ComSmartPtr notifyClient; //============================================================================== static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture) { String s; IMMDevice* dev = nullptr; + if (check (enumerator->GetDefaultAudioEndpoint (forCapture ? eCapture : eRender, eMultimedia, &dev))) { WCHAR* deviceId = nullptr; + if (check (dev->GetId (&deviceId))) { s = deviceId; @@ -1073,14 +1348,19 @@ private: StringArray& outputDeviceIds, StringArray& inputDeviceIds) { - ComSmartPtr enumerator; - if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) - return; + if (enumerator == nullptr) + { + if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) + return; + + notifyClient = new ChangeNotificationClient (*this); + enumerator->RegisterEndpointNotificationCallback (notifyClient); + } const String defaultRenderer (getDefaultEndpoint (enumerator, false)); - const String defaultCapture (getDefaultEndpoint (enumerator, true)); + const String defaultCapture (getDefaultEndpoint (enumerator, true)); - ComSmartPtr deviceCollection; + ComSmartPtr deviceCollection; UINT32 numDevices = 0; if (! (check (enumerator->EnumAudioEndpoints (eAll, DEVICE_STATE_ACTIVE, deviceCollection.resetAndGetPointerAddress())) @@ -1089,7 +1369,7 @@ private: for (UINT32 i = 0; i < numDevices; ++i) { - ComSmartPtr device; + ComSmartPtr device; if (! check (deviceCollection->Item (i, device.resetAndGetPointerAddress()))) continue; @@ -1101,12 +1381,16 @@ private: String name; { - ComSmartPtr properties; + ComSmartPtr properties; if (! check (device->OpenPropertyStore (STGM_READ, properties.resetAndGetPointerAddress()))) continue; PROPVARIANT value; - PropVariantInit (&value); + zerostruct (value); + + const PROPERTYKEY PKEY_Device_FriendlyName + = { { 0xa45c254e, 0xdf1c, 0x4efd, { 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0 } }, 14 }; + if (check (properties->GetValue (PKEY_Device_FriendlyName, &value))) name = value.pwszVal; @@ -1149,13 +1433,61 @@ private: inputDeviceNames = newInNames; outputDeviceIds = newOutIds; inputDeviceIds = newInIds; - - callDeviceChangeListeners(); } + + callDeviceChangeListeners(); } //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType) +}; + +//============================================================================== +struct MMDeviceMasterVolume +{ + MMDeviceMasterVolume() + { + ComSmartPtr enumerator; + if (check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator)))) + { + ComSmartPtr device; + if (check (enumerator->GetDefaultAudioEndpoint (eRender, eConsole, device.resetAndGetPointerAddress()))) + check (device->Activate (__uuidof (IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, + (void**) endpointVolume.resetAndGetPointerAddress())); + } + } + + float getGain() const + { + float vol = 0.0f; + if (endpointVolume != nullptr) + check (endpointVolume->GetMasterVolumeLevelScalar (&vol)); + + return vol; + } + + bool setGain (float newGain) const + { + return endpointVolume != nullptr + && check (endpointVolume->SetMasterVolumeLevelScalar (jlimit (0.0f, 1.0f, newGain), nullptr)); + } + + bool isMuted() const + { + BOOL mute = 0; + return endpointVolume != nullptr + && check (endpointVolume->GetMute (&mute)) && mute != 0; + } + + bool setMuted (bool shouldMute) const + { + return endpointVolume != nullptr + && check (endpointVolume->SetMute (shouldMute, nullptr)); + } + + ComSmartPtr endpointVolume; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MMDeviceMasterVolume) }; } @@ -1168,3 +1500,10 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI() return nullptr; } + +//============================================================================== +#define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 +float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); } +bool JUCE_CALLTYPE SystemAudioVolume::setGain (float gain) { return WasapiClasses::MMDeviceMasterVolume().setGain (gain); } +bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { return WasapiClasses::MMDeviceMasterVolume().isMuted(); } +bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool mute) { return WasapiClasses::MMDeviceMasterVolume().setMuted (mute); } diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp index 36e349f..c351795 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -27,7 +26,6 @@ AudioSourcePlayer::AudioSourcePlayer() : source (nullptr), sampleRate (0), bufferSize (0), - tempBuffer (2, 8), lastGain (1.0f), gain (1.0f) { @@ -75,12 +73,11 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, if (source != nullptr) { - AudioSourceChannelInfo info; - int i, numActiveChans = 0, numInputs = 0, numOutputs = 0; + int numActiveChans = 0, numInputs = 0, numOutputs = 0; // messy stuff needed to compact the channels down into an array // of non-zero pointers.. - for (i = 0; i < totalNumInputChannels; ++i) + for (int i = 0; i < totalNumInputChannels; ++i) { if (inputChannelData[i] != nullptr) { @@ -90,7 +87,7 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, } } - for (i = 0; i < totalNumOutputChannels; ++i) + for (int i = 0; i < totalNumOutputChannels; ++i) { if (outputChannelData[i] != nullptr) { @@ -108,30 +105,30 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, tempBuffer.setSize (numInputs - numOutputs, numSamples, false, false, true); - for (i = 0; i < numOutputs; ++i) + for (int i = 0; i < numOutputs; ++i) { channels[numActiveChans] = outputChans[i]; memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); ++numActiveChans; } - for (i = numOutputs; i < numInputs; ++i) + for (int i = numOutputs; i < numInputs; ++i) { - channels[numActiveChans] = tempBuffer.getSampleData (i - numOutputs, 0); + channels[numActiveChans] = tempBuffer.getWritePointer (i - numOutputs); memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); ++numActiveChans; } } else { - for (i = 0; i < numInputs; ++i) + for (int i = 0; i < numInputs; ++i) { channels[numActiveChans] = outputChans[i]; memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); ++numActiveChans; } - for (i = numInputs; i < numOutputs; ++i) + for (int i = numInputs; i < numOutputs; ++i) { channels[numActiveChans] = outputChans[i]; zeromem (channels[numActiveChans], sizeof (float) * (size_t) numSamples); @@ -141,14 +138,11 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, AudioSampleBuffer buffer (channels, numActiveChans, numSamples); - info.buffer = &buffer; - info.startSample = 0; - info.numSamples = numSamples; - + AudioSourceChannelInfo info (&buffer, 0, numSamples); source->getNextAudioBlock (info); - for (i = info.buffer->getNumChannels(); --i >= 0;) - info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain); + for (int i = info.buffer->getNumChannels(); --i >= 0;) + buffer.applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain); lastGain = gain; } @@ -162,8 +156,14 @@ void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, void AudioSourcePlayer::audioDeviceAboutToStart (AudioIODevice* device) { - sampleRate = device->getCurrentSampleRate(); - bufferSize = device->getCurrentBufferSizeSamples(); + prepareToPlay (device->getCurrentSampleRate(), + device->getCurrentBufferSizeSamples()); +} + +void AudioSourcePlayer::prepareToPlay (double newSampleRate, int newBufferSize) +{ + sampleRate = newSampleRate; + bufferSize = newBufferSize; zeromem (channels, sizeof (channels)); if (source != nullptr) diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h index 7a89527..0a3751c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ -#define __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ +#ifndef JUCE_AUDIOSOURCEPLAYER_H_INCLUDED +#define JUCE_AUDIOSOURCEPLAYER_H_INCLUDED //============================================================================== @@ -66,8 +65,7 @@ public: void setSource (AudioSource* newSource); /** Returns the source that's playing. - - May return 0 if there's no source. + May return nullptr if there's no source. */ AudioSource* getCurrentSource() const noexcept { return source; } @@ -87,13 +85,16 @@ public: int totalNumInputChannels, float** outputChannelData, int totalNumOutputChannels, - int numSamples); + int numSamples) override; /** Implementation of the AudioIODeviceCallback method. */ - void audioDeviceAboutToStart (AudioIODevice* device); + void audioDeviceAboutToStart (AudioIODevice* device) override; /** Implementation of the AudioIODeviceCallback method. */ - void audioDeviceStopped(); + void audioDeviceStopped() override; + + /** An alternative method for initialising the source without an AudioIODevice. */ + void prepareToPlay (double sampleRate, int blockSize); private: //============================================================================== @@ -107,8 +108,8 @@ private: AudioSampleBuffer tempBuffer; float lastGain, gain; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourcePlayer); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourcePlayer) }; -#endif // __JUCE_AUDIOSOURCEPLAYER_JUCEHEADER__ +#endif // JUCE_AUDIOSOURCEPLAYER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp index a2c854f..e923b46 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -45,15 +44,12 @@ AudioTransportSource::AudioTransportSource() AudioTransportSource::~AudioTransportSource() { setSource (nullptr); - releaseMasterResources(); } void AudioTransportSource::setSource (PositionableAudioSource* const newSource, - int readAheadBufferSize_, - TimeSliceThread* readAheadThread, - double sourceSampleRateToCorrectFor, - int maxNumChannels) + int readAheadSize, TimeSliceThread* readAheadThread, + double sourceSampleRateToCorrectFor, int maxNumChannels) { if (source == newSource) { @@ -63,7 +59,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly } - readAheadBufferSize = readAheadBufferSize_; + readAheadBufferSize = readAheadSize; sourceSampleRate = sourceSampleRateToCorrectFor; ResamplingAudioSource* newResamplerSource = nullptr; @@ -71,15 +67,15 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, PositionableAudioSource* newPositionableSource = nullptr; AudioSource* newMasterSource = nullptr; - ScopedPointer oldResamplerSource (resamplerSource); - ScopedPointer oldBufferingSource (bufferingSource); + ScopedPointer oldResamplerSource (resamplerSource); + ScopedPointer oldBufferingSource (bufferingSource); AudioSource* oldMasterSource = masterSource; if (newSource != nullptr) { newPositionableSource = newSource; - if (readAheadBufferSize_ > 0) + if (readAheadSize > 0) { // If you want to use a read-ahead buffer, you must also provide a TimeSliceThread // for it to use! @@ -87,7 +83,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, newPositionableSource = newBufferingSource = new BufferingAudioSource (newPositionableSource, *readAheadThread, - false, readAheadBufferSize_, maxNumChannels); + false, readAheadSize, maxNumChannels); } newPositionableSource->setNextReadPosition (0); @@ -116,6 +112,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, masterSource = newMasterSource; positionableSource = newPositionableSource; + inputStreamEOF = false; playing = false; } @@ -165,13 +162,16 @@ double AudioTransportSource::getCurrentPosition() const { if (sampleRate > 0.0) return getNextReadPosition() / sampleRate; - else - return 0.0; + + return 0.0; } double AudioTransportSource::getLengthInSeconds() const { - return getTotalLength() / sampleRate; + if (sampleRate > 0.0) + return getTotalLength() / sampleRate; + + return 0.0; } void AudioTransportSource::setNextReadPosition (int64 newPosition) @@ -182,6 +182,7 @@ void AudioTransportSource::setNextReadPosition (int64 newPosition) newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); positionableSource->setNextReadPosition (newPosition); + inputStreamEOF = false; } } @@ -190,7 +191,6 @@ int64 AudioTransportSource::getNextReadPosition() const if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return (int64) (positionableSource->getNextReadPosition() * ratio); } @@ -204,7 +204,6 @@ int64 AudioTransportSource::getTotalLength() const if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return (int64) (positionableSource->getTotalLength() * ratio); } @@ -214,9 +213,7 @@ int64 AudioTransportSource::getTotalLength() const bool AudioTransportSource::isLooping() const { const ScopedLock sl (callbackLock); - - return positionableSource != nullptr - && positionableSource->isLooping(); + return positionableSource != nullptr && positionableSource->isLooping(); } void AudioTransportSource::setGain (const float newGain) noexcept @@ -224,12 +221,11 @@ void AudioTransportSource::setGain (const float newGain) noexcept gain = newGain; } -void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, - double sampleRate_) +void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate) { const ScopedLock sl (callbackLock); - sampleRate = sampleRate_; + sampleRate = newSampleRate; blockSize = samplesPerBlockExpected; if (masterSource != nullptr) @@ -238,6 +234,7 @@ void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, if (resamplerSource != nullptr && sourceSampleRate > 0) resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); + inputStreamEOF = false; isPrepared = true; } @@ -260,8 +257,6 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info { const ScopedLock sl (callbackLock); - inputStreamEOF = false; - if (masterSource != nullptr && ! stopped) { masterSource->getNextAudioBlock (info); @@ -277,7 +272,7 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info } if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 - && ! positionableSource->isLooping()) + && ! positionableSource->isLooping()) { playing = false; inputStreamEOF = true; @@ -287,10 +282,7 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info stopped = ! playing; for (int i = info.buffer->getNumChannels(); --i >= 0;) - { - info.buffer->applyGainRamp (i, info.startSample, info.numSamples, - lastGain, gain); - } + info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain); } else { diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h index 5fcf513..e60dee0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ -#define __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ +#ifndef JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED +#define JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED //============================================================================== @@ -46,7 +45,6 @@ class JUCE_API AudioTransportSource : public PositionableAudioSource, public: //============================================================================== /** Creates an AudioTransportSource. - After creating one of these, use the setSource() method to select an input source. */ AudioTransportSource(); @@ -95,7 +93,6 @@ public: 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; @@ -103,8 +100,7 @@ public: /** Returns the stream's length in seconds. */ double getLengthInSeconds() const; - /** Returns true if the player has stopped because its input stream ran out of data. - */ + /** Returns true if the player has stopped because its input stream ran out of data. */ bool hasStreamFinished() const noexcept { return inputStreamEOF; } //============================================================================== @@ -127,41 +123,38 @@ public: //============================================================================== /** 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 (float newGain) noexcept; /** Returns the current gain setting. - @see setGain */ float getGain() const noexcept { return gain; } - //============================================================================== /** Implementation of the AudioSource method. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ - void releaseResources(); + void releaseResources() override; /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void getNextAudioBlock (const AudioSourceChannelInfo&) override; //============================================================================== /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int64 newPosition); + void setNextReadPosition (int64 newPosition) override; /** Implements the PositionableAudioSource method. */ - int64 getNextReadPosition() const; + int64 getNextReadPosition() const override; /** Implements the PositionableAudioSource method. */ - int64 getTotalLength() const; + int64 getTotalLength() const override; /** Implements the PositionableAudioSource method. */ - bool isLooping() const; + bool isLooping() const override; private: //============================================================================== @@ -176,12 +169,12 @@ private: bool volatile playing, stopped; double sampleRate, sourceSampleRate; int blockSize, readAheadBufferSize; - bool isPrepared, inputStreamEOF; + bool volatile isPrepared, inputStreamEOF; void releaseMasterResources(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioTransportSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioTransportSource) }; -#endif // __JUCE_AUDIOTRANSPORTSOURCE_JUCEHEADER__ +#endif // JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h index 98026b2..cc01e05 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/all.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -42,38 +43,6 @@ #include "stream_decoder.h" #include "stream_encoder.h" - -#ifdef _MSC_VER -/* OPT: an MSVC built-in would be better */ -static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) -{ - x = ((x<<8)&0xFF00FF00) | ((x>>8)&0x00FF00FF); - return (x>>16) | (x<<16); -} -#endif - -#if defined(_MSC_VER) && defined(_X86_) -/* OPT: an MSVC built-in would be better */ -static void local_swap32_block_(FLAC__uint32 *start, FLAC__uint32 len) -{ - __asm { - mov edx, start - mov ecx, len - test ecx, ecx -loop1: - jz done1 - mov eax, [edx] - bswap eax - mov [edx], eax - add edx, 4 - dec ecx - jmp short loop1 -done1: - } -} -#endif - - /** \mainpage * * \section intro Introduction @@ -193,7 +162,7 @@ done1: * 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, + * 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 diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h index 270fde0..83595ac 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/alloc.h @@ -1,19 +1,33 @@ /* alloc - Convenience routines for safely allocating memory - * Copyright (C) 2007 Josh Coalson + * Copyright (C) 2007-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FLAC__SHARE__ALLOC_H @@ -28,15 +42,20 @@ */ #include /* for SIZE_MAX */ -#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__ +#if HAVE_STDINT_H #include /* for SIZE_MAX in case limits.h didn't get it */ #endif #include /* for size_t, malloc(), etc */ +#include "compat.h" #ifndef SIZE_MAX # ifndef SIZE_T_MAX # ifdef _MSC_VER -# define SIZE_T_MAX UINT_MAX +# ifdef _WIN64 +# define SIZE_T_MAX 0xffffffffffffffffui64 +# else +# define SIZE_T_MAX 0xffffffff +# endif # else # error # endif @@ -44,14 +63,10 @@ # define SIZE_MAX SIZE_T_MAX #endif -#ifndef FLaC__INLINE -#define FLaC__INLINE -#endif - /* avoid malloc()ing 0 bytes, see: * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 */ -static FLaC__INLINE void *safe_malloc_(size_t size) +static inline void *safe_malloc_(size_t size) { /* malloc(0) is undefined; FLAC src convention is to always allocate */ if(!size) @@ -59,7 +74,7 @@ static FLaC__INLINE void *safe_malloc_(size_t size) return malloc(size); } -static FLaC__INLINE void *safe_calloc_(size_t nmemb, size_t size) +static inline void *safe_calloc_(size_t nmemb, size_t size) { if(!nmemb || !size) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ @@ -68,7 +83,7 @@ static FLaC__INLINE void *safe_calloc_(size_t nmemb, size_t 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) +static inline void *safe_malloc_add_2op_(size_t size1, size_t size2) { size2 += size1; if(size2 < size1) @@ -76,7 +91,7 @@ static FLaC__INLINE void *safe_malloc_add_2op_(size_t size1, size_t size2) return safe_malloc_(size2); } -static FLaC__INLINE void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) +static inline void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) { size2 += size1; if(size2 < size1) @@ -87,7 +102,7 @@ static FLaC__INLINE void *safe_malloc_add_3op_(size_t size1, size_t size2, size_ return safe_malloc_(size3); } -static FLaC__INLINE void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) +static inline void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) { size2 += size1; if(size2 < size1) @@ -101,29 +116,9 @@ static FLaC__INLINE void *safe_malloc_add_4op_(size_t size1, size_t size2, size_ return safe_malloc_(size4); } -static FLaC__INLINE void *safe_malloc_mul_2op_(size_t size1, size_t size2) -#if 0 -needs support for cases where sizeof(size_t) != 4 -{ - /* could be faster #ifdef'ing off SIZEOF_SIZE_T */ - if(sizeof(size_t) == 4) { - if ((double)size1 * (double)size2 < 4294967296.0) - return malloc(size1*size2); - } - return 0; -} -#else -/* better? */ -{ - if(!size1 || !size2) - return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ - if(size1 > SIZE_MAX / size2) - return 0; - return malloc(size1*size2); -} -#endif +void *safe_malloc_mul_2op_(size_t size1, size_t size2) ; -static FLaC__INLINE void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) +static inline void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) { if(!size1 || !size2 || !size3) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ @@ -136,7 +131,7 @@ static FLaC__INLINE void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_ } /* size1*size2 + size3 */ -static FLaC__INLINE void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) +static inline void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) { if(!size1 || !size2) return safe_malloc_(size3); @@ -146,17 +141,19 @@ static FLaC__INLINE void *safe_malloc_mul2add_(size_t size1, size_t size2, size_ } /* size1 * (size2 + size3) */ -static FLaC__INLINE void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) +static inline void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) { if(!size1 || (!size2 && !size3)) return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ size2 += size3; if(size2 < size3) return 0; - return safe_malloc_mul_2op_(size1, size2); + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); } -static FLaC__INLINE void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) +static inline void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) { size2 += size1; if(size2 < size1) @@ -164,7 +161,7 @@ static FLaC__INLINE void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t return realloc(ptr, size2); } -static FLaC__INLINE void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) +static inline void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) { size2 += size1; if(size2 < size1) @@ -175,7 +172,7 @@ static FLaC__INLINE void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t return realloc(ptr, size3); } -static FLaC__INLINE void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) +static inline void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) { size2 += size1; if(size2 < size1) @@ -189,7 +186,7 @@ static FLaC__INLINE void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t return realloc(ptr, size4); } -static FLaC__INLINE void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) +static inline void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) { if(!size1 || !size2) return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ @@ -199,7 +196,7 @@ static FLaC__INLINE void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t } /* size1 * (size2 + size3) */ -static FLaC__INLINE void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) +static inline void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) { if(!size1 || (!size2 && !size3)) return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h index 5c2d504..fab30f7 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/assert.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h index c951305..5f58552 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/callback.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h new file mode 100644 index 0000000..6c436cc --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/compat.h @@ -0,0 +1,195 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2012 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* This is the prefered location of all CPP hackery to make $random_compiler + * work like something approaching a C99 (or maybe more accurately GNU99) + * compiler. + * + * It is assumed that this header will be included after "config.h". + */ + +#ifndef FLAC__SHARE__COMPAT_H +#define FLAC__SHARE__COMPAT_H + +#if defined _WIN32 && !defined __CYGWIN__ +/* where MSVC puts unlink() */ +# include +#else +# include +#endif + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#include /* for off_t */ +#define FLAC__off_t __int64 /* use this instead of off_t to fix the 2 GB limit */ +#if !defined __MINGW32__ +#define fseeko _fseeki64 +#define ftello _ftelli64 +#else /* MinGW */ +#if !defined(HAVE_FSEEKO) +#define fseeko fseeko64 +#define ftello ftello64 +#endif +#endif +#else +#define FLAC__off_t off_t +#endif + +#if HAVE_INTTYPES_H +#define __STDC_FORMAT_MACROS +#include +#endif + +#if defined(_MSC_VER) +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#endif + +#if defined(_MSC_VER) +#if _MSC_VER < 1500 +/* Visual Studio 2008 has restrict. */ +#define restrict __restrict +#endif +#define inline __inline +#endif + +/* 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 + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#define FLAC__STRNCASECMP strnicmp +#else +#define FLAC__STRNCASECMP strncasecmp +#endif + +#if defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ || defined __EMX__ +#include /* for _setmode(), chmod() */ +#include /* for _O_BINARY */ +#else +#include /* for chown(), unlink() */ +#endif + +#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ +#if defined __BORLANDC__ +#include /* for utime() */ +#else +#include /* for utime() */ +#endif +#else +#include /* some flavors of BSD (like OS X) require this to get time_t */ +#include /* for utime() */ +#endif + +#if defined _MSC_VER +# if _MSC_VER >= 1600 +/* Visual Studio 2010 has decent C99 support */ +# include +# define PRIu64 "llu" +# define PRId64 "lld" +# define PRIx64 "llx" +# else +# include +# ifndef UINT32_MAX +# define UINT32_MAX _UI32_MAX +# endif + typedef unsigned __int64 uint64_t; + typedef unsigned __int32 uint32_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int8 uint8_t; + typedef __int64 int64_t; + typedef __int32 int32_t; + typedef __int16 int16_t; + typedef __int8 int8_t; +# define PRIu64 "I64u" +# define PRId64 "I64d" +# define PRIx64 "I64x" +# endif +#endif /* defined _MSC_VER */ + +#ifdef _WIN32 +/* All char* strings are in UTF-8 format. Added to support Unicode files on Windows */ + +#define flac_printf printf_utf8 +#define flac_fprintf fprintf_utf8 +#define flac_vfprintf vfprintf_utf8 +#define flac_fopen fopen_utf8 +#define flac_chmod chmod_utf8 +#define flac_utime utime_utf8 +#define flac_unlink unlink_utf8 +#define flac_rename rename_utf8 +#define flac_stat _stat64_utf8 + +#else + +#define flac_printf printf +#define flac_fprintf fprintf +#define flac_vfprintf vfprintf +#define flac_fopen fopen +#define flac_chmod chmod +#define flac_utime utime +#define flac_unlink unlink +#define flac_rename rename + +#ifdef _WIN32 +#define flac_stat _stat64 +#else +#define flac_stat stat +#endif + +#endif + +#ifdef _WIN32 +#define flac_stat_s __stat64 /* stat struct */ +#define flac_fstat _fstat64 +#else +#define flac_stat_s stat /* stat struct */ +#define flac_fstat fstat +#endif + + +/* FLAC needs to compile and work correctly on systems with a norrmal ISO C99 + * snprintf as well as Microsoft Visual Studio which has an non-standards + * conformant snprint_s function. + * + * This function wraps the MS version to behave more like the the ISO version. + */ +#ifdef __cplusplus +extern "C" { +#endif +int flac_snprintf(char *str, size_t size, const char *fmt, ...); +#ifdef __cplusplus +}; +#endif + +#endif /* FLAC__SHARE__COMPAT_H */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h new file mode 100644 index 0000000..2e09404 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/endswap.h @@ -0,0 +1,52 @@ +/* libFLAC - Free Lossless Audio Codec library + * Copyright (C) 2012 Xiph.org Foundation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the Xiph.org Foundation nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* It is assumed that this header will be included after "config.h". */ + +#if HAVE_BSWAP32 /* GCC and Clang */ + +#define ENDSWAP_32(x) (__builtin_bswap32 (x)) + +#elif defined _MSC_VER /* Windows. Apparently in . */ + +#define ENDSWAP_32(x) (_byteswap_ulong (x)) + +#elif defined HAVE_BYTESWAP_H /* Linux */ + +#include + +#define ENDSWAP_32(x) (bswap_32 (x)) + +#else + +#define ENDSWAP_32(x) ((((x) >> 24) & 0xFF) + (((x) >> 8) & 0xFF00) + (((x) & 0xFF00) << 8) + (((x) & 0xFF) << 24)) + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h index 5a5654c..d7ca735 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/export.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -55,25 +56,30 @@ * \{ */ -#if defined(FLAC__NO_DLL) || !defined(_MSC_VER) +#if defined(FLAC__NO_DLL) #define FLAC_API -#else - +#elif defined(_MSC_VER) #ifdef FLAC_API_EXPORTS #define FLAC_API _declspec(dllexport) #else #define FLAC_API _declspec(dllimport) - #endif + +#elif defined(FLAC__USE_VISIBILITY_ATTR) +#define FLAC_API __attribute__ ((visibility ("default"))) + +#else +#define FLAC_API + #endif /** These #defines will mirror the libtool-based library version number, see - * http://www.gnu.org/software/libtool/manual.html#Libtool-versioning + * http://www.gnu.org/software/libtool/manual/libtool.html#Libtool-versioning */ -#define FLAC_API_VERSION_CURRENT 10 +#define FLAC_API_VERSION_CURRENT 11 #define FLAC_API_VERSION_REVISION 0 /**< see above */ -#define FLAC_API_VERSION_AGE 2 /**< see above */ +#define FLAC_API_VERSION_AGE 3 /**< see above */ #ifdef __cplusplus extern "C" { diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h index 75c06c1..ecebe80 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/format.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -879,6 +880,18 @@ extern FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN; /**< == 24 (bit */ FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate); +/** Tests that a blocksize at the given sample rate is valid for the FLAC + * subset. + * + * \param blocksize The blocksize to test for compliance. + * \param sample_rate The sample rate is needed, since the valid subset + * blocksize depends on the sample rate. + * \retval FLAC__bool + * \c true if the given blocksize conforms to the specification for the + * subset at the given sample rate, else \c false. + */ +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, 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. diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c index ccc6524..3ea040e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitmath.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,46 +37,6 @@ #include "include/private/bitmath.h" #include "../assert.h" -/* An example of what FLAC__bitmath_ilog2() computes: - * - * ilog2( 0) = assertion failure - * ilog2( 1) = 0 - * ilog2( 2) = 1 - * ilog2( 3) = 1 - * ilog2( 4) = 2 - * ilog2( 5) = 2 - * ilog2( 6) = 2 - * ilog2( 7) = 2 - * ilog2( 8) = 3 - * ilog2( 9) = 3 - * ilog2(10) = 3 - * ilog2(11) = 3 - * ilog2(12) = 3 - * ilog2(13) = 3 - * ilog2(14) = 3 - * ilog2(15) = 3 - * ilog2(16) = 4 - * ilog2(17) = 4 - * ilog2(18) = 4 - */ -unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) -{ - unsigned l = 0; - FLAC__ASSERT(v > 0); - while(v >>= 1) - l++; - return l; -} - -unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) -{ - unsigned l = 0; - FLAC__ASSERT(v > 0); - while(v >>= 1) - l++; - return l; -} - /* An example of what FLAC__bitmath_silog2() computes: * * silog2(-10) = 5 diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c index 1a18287..3fc7e1d 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitreader.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,49 +34,28 @@ # include #endif -#include /* for malloc() */ -#include /* for memcpy(), memset() */ -#ifdef _MSC_VER -#include /* for ntohl() */ -#elif defined FLAC__SYS_DARWIN -#include /* for ntohl() */ -#elif defined __MINGW32__ -#include /* for ntohl() */ -#else -#include /* for ntohl() */ -#endif +#include +#include #include "include/private/bitmath.h" #include "include/private/bitreader.h" #include "include/private/crc.h" #include "../assert.h" +#include "../compat.h" +#include "../endswap.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 */ +/* WATCHOUT: if you change this you must also change the following #defines down to FLAC__clz_uint32 below to match */ +/* WATCHOUT: there are a few places where the code will not work unless uint32_t 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__BYTES_PER_WORD 4 /* sizeof uint32_t */ +#define FLAC__BITS_PER_WORD (8 * FLAC__BYTES_PER_WORD) #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 */ +/* SWAP_BE_WORD_TO_HOST swaps bytes in a uint32_t (which is always big-endian) if necessary to match host byte order */ #if WORDS_BIGENDIAN #define SWAP_BE_WORD_TO_HOST(x) (x) #else -#if defined (_MSC_VER) && defined (_X86_) -#define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) -#else -#define SWAP_BE_WORD_TO_HOST(x) ntohl(x) +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_32(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 @@ -93,50 +73,11 @@ typedef FLAC__uint32 brword; */ static const unsigned FLAC__BITREADER_DEFAULT_CAPACITY = 65536u / FLAC__BITS_PER_WORD; /* in words */ -static const unsigned char byte_to_unary_table[] = { - 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#ifdef min -#undef min -#endif -#define min(x,y) ((x)<(y)?(x):(y)) -#ifdef max -#undef max -#endif -#define max(x,y) ((x)>(y)?(x):(y)) - -/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ -#ifdef _MSC_VER -#define FLAC__U64L(x) x -#else -#define FLAC__U64L(x) x##LLU -#endif - -#ifndef FLaC__INLINE -#define FLaC__INLINE -#endif - /* WATCHOUT: assembly routines rely on the order in which these fields are declared */ struct FLAC__BitReader { /* any partially-consumed word at the head will stay right-justified as bits are consumed from the left */ /* any incomplete word at the tail will be left-justified, and bytes from the read callback are added on the right */ - brword *buffer; + uint32_t *buffer; unsigned capacity; /* in words */ unsigned words; /* # of completed words in buffer */ unsigned bytes; /* # of bytes in incomplete word at buffer[words] */ @@ -149,7 +90,7 @@ struct FLAC__BitReader { FLAC__CPUInfo cpu_info; }; -static FLaC__INLINE void crc16_update_word_(FLAC__BitReader *br, brword word) +static inline void crc16_update_word_(FLAC__BitReader *br, uint32_t word) { register unsigned crc = br->read_crc16; #if FLAC__BYTES_PER_WORD == 4 @@ -203,7 +144,7 @@ FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) 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) + /* before reading, if the existing reader looks like this (say uint32_t 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) @@ -237,13 +178,6 @@ FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) #if WORDS_BIGENDIAN #else end = (br->words*FLAC__BYTES_PER_WORD + br->bytes + bytes + (FLAC__BYTES_PER_WORD-1)) / FLAC__BYTES_PER_WORD; -# if defined(_MSC_VER) && defined (_X86_) && (FLAC__BYTES_PER_WORD == 4) - if(br->cpu_info.type == FLAC__CPUINFO_TYPE_IA32 && br->cpu_info.data.ia32.bswap) { - start = br->words; - local_swap32_block_(br->buffer + start, end - start); - } - else -# endif for(start = br->words; start < end; start++) br->buffer[start] = SWAP_BE_WORD_TO_HOST(br->buffer[start]); #endif @@ -269,7 +203,7 @@ FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br) FLAC__BitReader *FLAC__bitreader_new(void) { - FLAC__BitReader *br = (FLAC__BitReader*)calloc(1, sizeof(FLAC__BitReader)); + FLAC__BitReader *br = (FLAC__BitReader*) calloc(1, sizeof(FLAC__BitReader)); /* calloc() implies: memset(br, 0, sizeof(FLAC__BitReader)); @@ -304,7 +238,7 @@ FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__Bi br->words = br->bytes = 0; br->consumed_words = br->consumed_bits = 0; br->capacity = FLAC__BITREADER_DEFAULT_CAPACITY; - br->buffer = (brword*)malloc(sizeof(brword) * br->capacity); + br->buffer = (uint32_t*) malloc(sizeof(uint32_t) * br->capacity); if(br->buffer == 0) return false; br->read_callback = rcb; @@ -384,29 +318,29 @@ FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br) /* CRC any tail bytes in a partially-consumed word */ if(br->consumed_bits) { - const brword tail = br->buffer[br->consumed_words]; + const uint32_t tail = br->buffer[br->consumed_words]; for( ; br->crc16_align < br->consumed_bits; br->crc16_align += 8) br->read_crc16 = FLAC__CRC16_UPDATE((unsigned)((tail >> (FLAC__BITS_PER_WORD-8-br->crc16_align)) & 0xff), br->read_crc16); } - return (FLAC__uint16) br->read_crc16; + return br->read_crc16; } -FLaC__INLINE FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br) +inline FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br) { return ((br->consumed_bits & 7) == 0); } -FLaC__INLINE unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) +inline unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br) { return 8 - (br->consumed_bits & 7); } -FLaC__INLINE unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) +inline unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br) { return (br->words-br->consumed_words)*FLAC__BITS_PER_WORD + br->bytes*8 - br->consumed_bits; } -FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits) +FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits) { FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -432,7 +366,7 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLA 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]; + const uint32_t word = br->buffer[br->consumed_words]; if(bits < n) { *val = (word & (FLAC__WORD_ALL_ONES >> br->consumed_bits)) >> (n-bits); br->consumed_bits += bits; @@ -451,7 +385,7 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLA return true; } else { - const brword word = br->buffer[br->consumed_words]; + const uint32_t word = br->buffer[br->consumed_words]; if(bits < FLAC__BITS_PER_WORD) { *val = word >> (FLAC__BITS_PER_WORD-bits); br->consumed_bits = bits; @@ -517,7 +451,7 @@ FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *va return true; } -FLaC__INLINE FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val) +inline FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val) { FLAC__uint32 x8, x32 = 0; @@ -557,7 +491,7 @@ FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits) FLAC__uint32 x; if(n != 0) { - m = min(8-n, bits); + m = flac_min(8-n, bits); if(!FLAC__bitreader_read_raw_uint32(br, &x, m)) return false; bits -= m; @@ -632,7 +566,7 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F /* 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++]; + const uint32_t word = br->buffer[br->consumed_words++]; #if FLAC__BYTES_PER_WORD == 4 val[0] = (FLAC__byte)(word >> 24); val[1] = (FLAC__byte)(word >> 16); @@ -668,7 +602,7 @@ FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, F return true; } -FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val) +FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val) #if 0 /* slow but readable version */ { unsigned bit; @@ -697,9 +631,9 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, *val = 0; while(1) { while(br->consumed_words < br->words) { /* if we've not consumed up to a partial tail word... */ - brword b = br->buffer[br->consumed_words] << br->consumed_bits; + uint32_t b = br->buffer[br->consumed_words] << br->consumed_bits; if(b) { - i = COUNT_ZERO_MSBS(b); + i = FLAC__clz_uint32(b); *val += i; i++; br->consumed_bits += i; @@ -725,11 +659,11 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, * us data a byte at a time (unlikely), br->consumed_bits may not * be zero. */ - if(br->bytes) { + if(br->bytes*8 > br->consumed_bits) { 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; + uint32_t b = (br->buffer[br->consumed_words] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << br->consumed_bits; if(b) { - i = COUNT_ZERO_MSBS(b); + i = FLAC__clz_uint32(b); *val += i; i++; br->consumed_bits += i; @@ -738,7 +672,7 @@ FLaC__INLINE FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, } else { *val += end - br->consumed_bits; - br->consumed_bits += end; + br->consumed_bits = end; FLAC__ASSERT(br->consumed_bits < FLAC__BITS_PER_WORD); /* didn't find stop bit yet, have to keep going... */ } @@ -759,7 +693,7 @@ FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsig FLAC__ASSERT(parameter <= 31); /* read the unary MSBs and end bit */ - if(!FLAC__bitreader_read_unary_unsigned(br, (unsigned int*) &msbs)) + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) return false; /* read the binary LSBs */ @@ -777,202 +711,15 @@ FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsig } /* this is by far the most heavily used reader call. it ain't pretty but it's fast */ -/* a lot of the logic is copied, then adapted, from FLAC__bitreader_read_unary_unsigned() and FLAC__bitreader_read_raw_uint32() */ FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter) -/* OPT: possibly faster version for use with MSVC */ -#ifdef _MSC_VER { - unsigned i; - unsigned uval = 0; - unsigned bits; /* the # of binary LSBs left to read to finish a rice codeword */ - /* try and get br->consumed_words and br->consumed_bits into register; * must remember to flush them back to *br before calling other - * bitwriter functions that use them, and before returning */ - register unsigned cwords; - register unsigned cbits; - - FLAC__ASSERT(0 != br); - FLAC__ASSERT(0 != br->buffer); - /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ - FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); - FLAC__ASSERT(parameter < 32); - /* the above two asserts also guarantee that the binary part never straddles more that 2 words, so we don't have to loop to read it */ - - if(nvals == 0) - return true; - - cbits = br->consumed_bits; - cwords = br->consumed_words; - - while(1) { - - /* read unary part */ - while(1) { - while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ - brword b = br->buffer[cwords] << cbits; - if(b) { -#if 0 /* slower, probably due to bad register allocation... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 - __asm { - bsr eax, b - not eax - and eax, 31 - mov i, eax - } -#else - i = COUNT_ZERO_MSBS(b); -#endif - uval += i; - bits = parameter; - i++; - cbits += i; - if(cbits == FLAC__BITS_PER_WORD) { - crc16_update_word_(br, br->buffer[cwords]); - cwords++; - cbits = 0; - } - goto break1; - } - else { - uval += FLAC__BITS_PER_WORD - cbits; - crc16_update_word_(br, br->buffer[cwords]); - cwords++; - cbits = 0; - /* didn't find stop bit yet, have to keep going... */ - } - } - /* at this point we've eaten up all the whole words; have to try - * reading through any tail bytes before calling the read callback. - * this is a repeat of the above logic adjusted for the fact we - * don't have a whole word. note though if the client is feeding - * us data a byte at a time (unlikely), br->consumed_bits may not - * be zero. - */ - if(br->bytes) { - const unsigned end = br->bytes * 8; - brword b = (br->buffer[cwords] & (FLAC__WORD_ALL_ONES << (FLAC__BITS_PER_WORD-end))) << cbits; - if(b) { - i = COUNT_ZERO_MSBS(b); - uval += i; - bits = parameter; - i++; - cbits += i; - FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); - goto break1; - } - else { - uval += end - cbits; - cbits += end; - FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); - /* didn't find stop bit yet, have to keep going... */ - } - } - /* flush registers and read; bitreader_read_from_client_() does - * not touch br->consumed_bits at all but we still need to set - * it in case it fails and we have to return false. - */ - br->consumed_bits = cbits; - br->consumed_words = cwords; - if(!bitreader_read_from_client_(br)) - return false; - cwords = br->consumed_words; - } -break1: - /* read binary part */ - FLAC__ASSERT(cwords <= br->words); - - if(bits) { - while((br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits < bits) { - /* flush registers and read; bitreader_read_from_client_() does - * not touch br->consumed_bits at all but we still need to set - * it in case it fails and we have to return false. - */ - br->consumed_bits = cbits; - br->consumed_words = cwords; - if(!bitreader_read_from_client_(br)) - return false; - cwords = br->consumed_words; - } - if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ - if(cbits) { - /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ - const unsigned n = FLAC__BITS_PER_WORD - cbits; - const brword word = br->buffer[cwords]; - if(bits < n) { - uval <<= bits; - uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-bits); - cbits += bits; - goto break2; - } - uval <<= n; - uval |= word & (FLAC__WORD_ALL_ONES >> cbits); - bits -= n; - crc16_update_word_(br, word); - cwords++; - cbits = 0; - if(bits) { /* if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ - uval <<= bits; - uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits)); - cbits = bits; - } - goto break2; - } - else { - FLAC__ASSERT(bits < FLAC__BITS_PER_WORD); - uval <<= bits; - uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); - cbits = bits; - goto break2; - } - } - else { - /* in this case we're starting our read at a partial tail word; - * the reader has guaranteed that we have at least 'bits' bits - * available to read, which makes this case simpler. - */ - uval <<= bits; - if(cbits) { - /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ - FLAC__ASSERT(cbits + bits <= br->bytes*8); - uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-bits); - cbits += bits; - goto break2; - } - else { - uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-bits); - cbits += bits; - goto break2; - } - } - } -break2: - /* compose the value */ - *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); - - /* are we done? */ - --nvals; - if(nvals == 0) { - br->consumed_bits = cbits; - br->consumed_words = cwords; - return true; - } - - uval = 0; - ++vals; - - } -} -#else -{ - unsigned i; - unsigned uval = 0; - - /* try and get br->consumed_words and br->consumed_bits into register; - * must remember to flush them back to *br before calling other - * bitwriter functions that use them, and before returning */ - register unsigned cwords; - register unsigned cbits; - unsigned ucbits; /* keep track of the number of unconsumed bits in the buffer */ + * bitreader functions that use them, and before returning */ + unsigned cwords, words, lsbs, msbs, x, y; + unsigned ucbits; /* keep track of the number of unconsumed bits in word */ + uint32_t b; + int *val, *end; FLAC__ASSERT(0 != br); FLAC__ASSERT(0 != br->buffer); @@ -981,175 +728,127 @@ break2: 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; + val = vals; + end = vals + nvals; - cbits = br->consumed_bits; - cwords = br->consumed_words; - ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; - - while(1) { - - /* read unary part */ - while(1) { - while(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ - brword b = br->buffer[cwords] << cbits; - if(b) { -#if 0 /* is not discernably faster... */ && defined FLAC__CPU_IA32 && !defined FLAC__NO_ASM && FLAC__BITS_PER_WORD == 32 && defined __GNUC__ - asm volatile ( - "bsrl %1, %0;" - "notl %0;" - "andl $31, %0;" - : "=r"(i) - : "r"(b) - ); -#else - i = COUNT_ZERO_MSBS(b); -#endif - uval += i; - cbits += i; - cbits++; /* skip over stop bit */ - if(cbits >= FLAC__BITS_PER_WORD) { /* faster way of testing if(cbits == FLAC__BITS_PER_WORD) */ - crc16_update_word_(br, br->buffer[cwords]); - cwords++; - cbits = 0; - } - goto break1; - } - else { - uval += FLAC__BITS_PER_WORD - cbits; - crc16_update_word_(br, br->buffer[cwords]); - cwords++; - cbits = 0; - /* didn't find stop bit yet, have to keep going... */ - } - } - /* at this point we've eaten up all the whole words; have to try - * reading through any tail bytes before calling the read callback. - * this is a repeat of the above logic adjusted for the fact we - * don't have a whole word. note though if the client is feeding - * us data a byte at a time (unlikely), br->consumed_bits may not - * be zero. - */ - if(br->bytes) { - const unsigned end = br->bytes * 8; - brword b = (br->buffer[cwords] & ~(FLAC__WORD_ALL_ONES >> end)) << cbits; - if(b) { - i = COUNT_ZERO_MSBS(b); - uval += i; - cbits += i; - cbits++; /* skip over stop bit */ - FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); - goto break1; - } - else { - uval += end - cbits; - cbits += end; - FLAC__ASSERT(cbits < FLAC__BITS_PER_WORD); - /* didn't find stop bit yet, have to keep going... */ - } - } - /* flush registers and read; bitreader_read_from_client_() does - * not touch br->consumed_bits at all but we still need to set - * it in case it fails and we have to return false. - */ - br->consumed_bits = cbits; - br->consumed_words = cwords; - if(!bitreader_read_from_client_(br)) + if(parameter == 0) { + while(val < end) { + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) return false; - cwords = br->consumed_words; - ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits + uval; - /* + uval to offset our count by the # of unary bits already - * consumed before the read, because we will add these back - * in all at once at break1 - */ - } -break1: - ucbits -= uval; - ucbits--; /* account for stop bit */ - /* read binary part */ - FLAC__ASSERT(cwords <= br->words); - - if(parameter) { - while(ucbits < parameter) { - /* flush registers and read; bitreader_read_from_client_() does - * not touch br->consumed_bits at all but we still need to set - * it in case it fails and we have to return false. - */ - br->consumed_bits = cbits; - br->consumed_words = cwords; - if(!bitreader_read_from_client_(br)) - return false; - cwords = br->consumed_words; - ucbits = (br->words-cwords)*FLAC__BITS_PER_WORD + br->bytes*8 - cbits; - } - if(cwords < br->words) { /* if we've not consumed up to a partial tail word... */ - if(cbits) { - /* this also works when consumed_bits==0, it's just slower than necessary for that case */ - const unsigned n = FLAC__BITS_PER_WORD - cbits; - const brword word = br->buffer[cwords]; - if(parameter < n) { - uval <<= parameter; - uval |= (word & (FLAC__WORD_ALL_ONES >> cbits)) >> (n-parameter); - cbits += parameter; - } - else { - uval <<= n; - uval |= word & (FLAC__WORD_ALL_ONES >> cbits); - crc16_update_word_(br, word); - cwords++; - cbits = parameter - n; - if(cbits) { /* parameter > n, i.e. if there are still bits left to read, there have to be less than 32 so they will all be in the next word */ - uval <<= cbits; - uval |= (br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits)); - } - } - } - else { - cbits = parameter; - uval <<= parameter; - uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); - } - } - else { - /* in this case we're starting our read at a partial tail word; - * the reader has guaranteed that we have at least 'parameter' - * bits available to read, which makes this case simpler. - */ - uval <<= parameter; - if(cbits) { - /* this also works when consumed_bits==0, it's just a little slower than necessary for that case */ - FLAC__ASSERT(cbits + parameter <= br->bytes*8); - uval |= (br->buffer[cwords] & (FLAC__WORD_ALL_ONES >> cbits)) >> (FLAC__BITS_PER_WORD-cbits-parameter); - cbits += parameter; - } - else { - cbits = parameter; - uval |= br->buffer[cwords] >> (FLAC__BITS_PER_WORD-cbits); - } - } + *val++ = (int)(msbs >> 1) ^ -(int)(msbs & 1); } - ucbits -= parameter; + return true; + } + + FLAC__ASSERT(parameter > 0); + + cwords = br->consumed_words; + words = br->words; + + /* if we've not consumed up to a partial tail word... */ + if(cwords >= words) { + x = 0; + goto process_tail; + } + + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = br->buffer[cwords] << br->consumed_bits; /* keep unconsumed bits aligned to left */ + + while(val < end) { + /* read the unary MSBs and end bit */ + x = y = FLAC__clz2_uint32(b); + if(x == FLAC__BITS_PER_WORD) { + x = ucbits; + do { + /* didn't find stop bit yet, have to keep going... */ + crc16_update_word_(br, br->buffer[cwords++]); + if (cwords >= words) + goto incomplete_msbs; + b = br->buffer[cwords]; + y = FLAC__clz2_uint32(b); + x += y; + } while(y == FLAC__BITS_PER_WORD); + } + b <<= y; + b <<= 1; /* account for stop bit */ + ucbits = (ucbits - x - 1) % FLAC__BITS_PER_WORD; + msbs = x; + + /* read the binary LSBs */ + x = b >> (FLAC__BITS_PER_WORD - parameter); + if(parameter <= ucbits) { + ucbits -= parameter; + b <<= parameter; + } else { + /* there are still bits left to read, they will all be in the next word */ + crc16_update_word_(br, br->buffer[cwords++]); + if (cwords >= words) + goto incomplete_lsbs; + b = br->buffer[cwords]; + ucbits += FLAC__BITS_PER_WORD - parameter; + x |= b >> ucbits; + b <<= FLAC__BITS_PER_WORD - ucbits; + } + lsbs = x; /* compose the value */ - *vals = (int)(uval >> 1 ^ -(int)(uval & 1)); + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); - /* are we done? */ - --nvals; - if(nvals == 0) { - br->consumed_bits = cbits; - br->consumed_words = cwords; - return true; - } + continue; - uval = 0; - ++vals; + /* at this point we've eaten up all the whole words */ +process_tail: + do { + if(0) { +incomplete_msbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + /* read the unary MSBs and end bit */ + if(!FLAC__bitreader_read_unary_unsigned(br, &msbs)) + return false; + msbs += x; + x = ucbits = 0; + + if(0) { +incomplete_lsbs: + br->consumed_bits = 0; + br->consumed_words = cwords; + } + + /* read the binary LSBs */ + if(!FLAC__bitreader_read_raw_uint32(br, &lsbs, parameter - ucbits)) + return false; + lsbs = x | lsbs; + + /* compose the value */ + x = (msbs << parameter) | lsbs; + *val++ = (int)(x >> 1) ^ -(int)(x & 1); + x = 0; + + cwords = br->consumed_words; + words = br->words; + ucbits = FLAC__BITS_PER_WORD - br->consumed_bits; + b = br->buffer[cwords] << br->consumed_bits; + } while(cwords >= words && val < end); } + + if(ucbits == 0 && cwords < words) { + /* don't leave the head word with no unconsumed bits */ + crc16_update_word_(br, br->buffer[cwords++]); + ucbits = FLAC__BITS_PER_WORD; + } + + br->consumed_bits = FLAC__BITS_PER_WORD - ucbits; + br->consumed_words = cwords; + + return true; } -#endif #if 0 /* UNUSED */ FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter) @@ -1348,3 +1047,16 @@ FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *v *val = v; return true; } + +/* These functions a declared inline in this file but are also callable as + * externs from elsewhere. + * According to the C99 sepc, section 6.7.4, simply providing a function + * prototype in a header file without 'inline' and making the function inline + * in this file should be sufficient. + * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To + * fix that we add extern declarations here. + */ +extern FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); +extern unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); +extern unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); +extern FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c index 759a836..9ac9ee5 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/bitwriter.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,41 +34,27 @@ # include #endif -#include /* for malloc() */ -#include /* for memcpy(), memset() */ -#ifdef _MSC_VER -#include /* for ntohl() */ -#elif defined FLAC__SYS_DARWIN -#include /* for ntohl() */ -#elif defined __MINGW32__ -#include /* for ntohl() */ -#else -#include /* for ntohl() */ -#endif -#if 0 /* UNUSED */ -#include "include/private/bitmath.h" -#endif +#include +#include #include "include/private/bitwriter.h" #include "include/private/crc.h" #include "../assert.h" #include "../alloc.h" +#include "../compat.h" +#include "../endswap.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; +/* WATCHOUT: there are a few places where the code will not work unless uint32_t is >= 32 bits wide */ #define FLAC__BYTES_PER_WORD 4 +#undef FLAC__BITS_PER_WORD #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 */ +/* SWAP_BE_WORD_TO_HOST swaps bytes in a uint32_t (which is always big-endian) if necessary to match host byte order */ #if WORDS_BIGENDIAN #define SWAP_BE_WORD_TO_HOST(x) (x) #else -#ifdef _MSC_VER -#define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) -#else -#define SWAP_BE_WORD_TO_HOST(x) ntohl(x) -#endif +#define SWAP_BE_WORD_TO_HOST(x) ENDSWAP_32(x) #endif /* @@ -76,42 +63,29 @@ typedef FLAC__uint32 bwword; * 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 */ +static const unsigned FLAC__BITWRITER_DEFAULT_CAPACITY = 32768u / sizeof(uint32_t); /* size in words */ /* When growing, increment 4K at a time */ -static const unsigned FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(bwword); /* size in words */ +static const unsigned FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(uint32_t); /* size in words */ #define FLAC__WORDS_TO_BITS(words) ((words) * FLAC__BITS_PER_WORD) #define FLAC__TOTAL_BITS(bw) (FLAC__WORDS_TO_BITS((bw)->words) + (bw)->bits) -#ifdef min -#undef min -#endif -#define min(x,y) ((x)<(y)?(x):(y)) - -/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ -#ifdef _MSC_VER -#define FLAC__U64L(x) x -#else -#define FLAC__U64L(x) x##LLU -#endif - -#ifndef FLaC__INLINE -#define FLaC__INLINE -#endif - struct FLAC__BitWriter { - bwword *buffer; - bwword accum; /* accumulator; bits are right-justified; when full, accum is appended to buffer */ + uint32_t *buffer; + uint32_t accum; /* accumulator; bits are right-justified; when full, accum is appended to buffer */ unsigned capacity; /* capacity of buffer in words */ unsigned words; /* # of complete words in buffer */ unsigned bits; /* # of used bits in accum */ }; /* * WATCHOUT: The current implementation only grows the buffer. */ -static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) +#ifndef __SUNPRO_C +static +#endif +FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) { unsigned new_capacity; - bwword *new_buffer; + uint32_t *new_buffer; FLAC__ASSERT(0 != bw); FLAC__ASSERT(0 != bw->buffer); @@ -133,7 +107,7 @@ static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) FLAC__ASSERT(new_capacity > bw->capacity); FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD)); - new_buffer = (bwword*)safe_realloc_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity); + new_buffer = (uint32_t*) safe_realloc_mul_2op_(bw->buffer, sizeof(uint32_t), /*times*/new_capacity); if(new_buffer == 0) return false; bw->buffer = new_buffer; @@ -150,7 +124,7 @@ static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) FLAC__BitWriter *FLAC__bitwriter_new(void) { - FLAC__BitWriter *bw = (FLAC__BitWriter*)calloc(1, sizeof(FLAC__BitWriter)); + FLAC__BitWriter *bw = (FLAC__BitWriter*) calloc(1, sizeof(FLAC__BitWriter)); /* note that calloc() sets all members to 0 for us */ return bw; } @@ -175,7 +149,7 @@ FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw) bw->words = bw->bits = 0; bw->capacity = FLAC__BITWRITER_DEFAULT_CAPACITY; - bw->buffer = (bwword*)malloc(sizeof(bwword) * bw->capacity); + bw->buffer = (uint32_t*) malloc(sizeof(uint32_t) * bw->capacity); if(bw->buffer == 0) return false; @@ -290,7 +264,7 @@ void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw) (void)bw; } -FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits) +inline FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits) { unsigned n; @@ -304,7 +278,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsign return false; /* first part gets to word alignment */ if(bw->bits) { - n = min(FLAC__BITS_PER_WORD - bw->bits, bits); + n = flac_min(FLAC__BITS_PER_WORD - bw->bits, bits); bw->accum <<= n; bits -= n; bw->bits += n; @@ -328,7 +302,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsign return true; } -FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits) +inline FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits) { register unsigned left; @@ -367,7 +341,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FL return true; } -FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits) +inline FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits) { /* zero-out unused bits */ if(bits < 32) @@ -376,7 +350,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLA return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); } -FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits) +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) { @@ -388,7 +362,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FL return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); } -FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val) +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 */ @@ -404,7 +378,7 @@ FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__Bit return true; } -FLaC__INLINE FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals) +inline FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals) { unsigned i; @@ -540,7 +514,7 @@ 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); + FLAC__ASSERT(parameter < 8*sizeof(uint32_t)-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); @@ -551,8 +525,8 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL 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 */ + if(bw->bits && bw->bits + msbits + lsbits <= FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current uint32_t */ + /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free uint32_t to work in */ bw->bits = bw->bits + msbits + lsbits; uval |= mask1; /* set stop bit */ uval &= mask2; /* mask off unused top bits */ @@ -572,8 +546,8 @@ 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 */ + if(bw->bits && bw->bits + msbits + lsbits < FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current uint32_t */ + /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free uint32_t to work in */ bw->bits = bw->bits + msbits + lsbits; uval |= mask1; /* set stop bit */ uval &= mask2; /* mask off unused top bits */ @@ -584,7 +558,7 @@ FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FL #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)) + if(bw->capacity <= bw->words + bw->bits + msbits + 1/*lsbits always fit in 1 uint32_t*/ && !bitwriter_grow_(bw, msbits+lsbits)) return false; if(msbits) { @@ -878,3 +852,17 @@ FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw) else return true; } + +/* These functions a declared inline in this file but are also callable as + * externs from elsewhere. + * According to the C99 sepc, section 6.7.4, simply providing a function + * prototype in a header file without 'inline' and making the function inline + * in this file should be sufficient. + * Unfortunately, the Microsoft VS compiler doesn't pick them up externally. To + * fix that we add extern declarations here. + */ +extern FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits); +extern FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits); +extern FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits); +extern FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val); +extern FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c index c052e25..e352c95 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/cpu.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -136,7 +137,7 @@ static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; # endif # elif defined(_MSC_VER) # include -# undef USE_TRY_CATCH_FLAVOR /* #define this to use the try/catch method for catching illegal opcode exception */ +# define USE_TRY_CATCH_FLAVOR /* sigill_handler flavor resulted in several crash reports on win32 */ # ifdef USE_TRY_CATCH_FLAVOR # else LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep) diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c index 1325f17..225b26d 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/crc.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -74,7 +75,7 @@ FLAC__byte const FLAC__crc8_table[256] = { /* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ -unsigned FLAC__crc16_table[256] = { +unsigned const FLAC__crc16_table[256] = { 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c index 9234f63..f8ebeda 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/fixed.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -44,11 +45,6 @@ #define M_LN2 0.69314718055994530942 #endif -#ifdef min -#undef min -#endif -#define min(x,y) ((x) < (y)? (x) : (y)) - #ifdef local_abs #undef local_abs #endif @@ -242,11 +238,11 @@ unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned d error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; } - if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) order = 0; - else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) order = 1; - else if(total_error_2 < min(total_error_3, total_error_4)) + else if(total_error_2 < flac_min(total_error_3, total_error_4)) order = 2; else if(total_error_3 < total_error_4) order = 3; @@ -304,11 +300,11 @@ unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsig error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; } - if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) + if(total_error_0 < flac_min(flac_min(flac_min(total_error_1, total_error_2), total_error_3), total_error_4)) order = 0; - else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) + else if(total_error_1 < flac_min(flac_min(total_error_2, total_error_3), total_error_4)) order = 1; - else if(total_error_2 < min(total_error_3, total_error_4)) + else if(total_error_2 < flac_min(total_error_3, total_error_4)) order = 2; else if(total_error_3 < total_error_4) order = 3; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c index bcb3ef5..ae10407 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/float.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,18 +35,11 @@ #endif #include "../assert.h" - +#include "../compat.h" #include "include/private/float.h" #ifdef FLAC__INTEGER_ONLY_LIBRARY -/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ -#ifdef _MSC_VER -#define FLAC__U64L(x) x -#else -#define FLAC__U64L(x) x##LLU -#endif - const FLAC__fixedpoint FLAC__FP_ZERO = 0; const FLAC__fixedpoint FLAC__FP_ONE_HALF = 0x00008000; const FLAC__fixedpoint FLAC__FP_ONE = 0x00010000; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c index 8a0749c..1f37de9 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/format.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -38,37 +39,17 @@ #include /* for memset() */ #include "../assert.h" #include "../format.h" +#include "../compat.h" #include "include/private/format.h" -#ifndef FLaC__INLINE -#define FLaC__INLINE -#endif - -#ifdef min -#undef min -#endif -#define min(a,b) ((a)<(b)?(a):(b)) - -/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ -#ifdef _MSC_VER -#define FLAC__U64L(x) x -#else -#define FLAC__U64L(x) x##LLU -#endif - /* VERSION should come from configure */ -FLAC_API const char *FLAC__VERSION_STRING = VERSION - - - - -; +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"; +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC 1.3.0 20130526"; #else -FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20070917"; +FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20130526"; #endif FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; @@ -228,6 +209,16 @@ FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate) return true; } +FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigned sample_rate) +{ + if(blocksize > 16384) + return false; + else if(sample_rate <= 48000 && blocksize > 4608) + return false; + else + return true; +} + FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) { if( @@ -268,7 +259,7 @@ FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_S } /* used as the sort predicate for qsort() */ -static int JUCE_CDECL seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) +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) @@ -288,7 +279,7 @@ 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 (JUCE_CDECL *)(const void *, const void *))seekpoint_compare_); + qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_); /* uniquify the seekpoints */ first = true; @@ -318,7 +309,7 @@ FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *se * 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) +static unsigned utf8len_(const FLAC__byte *utf8) { FLAC__ASSERT(0 != utf8); if ((utf8[0] & 0x80) == 0) { @@ -541,7 +532,7 @@ unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned block max_rice_partition_order++; blocksize >>= 1; } - return min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); + return flac_min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); } unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order) @@ -586,9 +577,9 @@ FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_s FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); if(object->capacity_by_order < max_partition_order) { - if(0 == (object->parameters = (unsigned*)realloc(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) + if(0 == (object->parameters = (unsigned int*) realloc(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) return false; - if(0 == (object->raw_bits = (unsigned*)realloc(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) + if(0 == (object->raw_bits = (unsigned int*) realloc(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) return false; memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order)); object->capacity_by_order = max_partition_order; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h index 389cdb2..2925309 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/all.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h index 1277102..371bd48 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitmath.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,8 +35,136 @@ #include "../../../ordinals.h" -unsigned FLAC__bitmath_ilog2(FLAC__uint32 v); -unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v); +/* for CHAR_BIT */ +#include +#include "../../../compat.h" + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#include /* for _BitScanReverse* */ +#endif + +/* Will never be emitted for MSVC, GCC, Intel compilers */ +static inline unsigned int FLAC__clz_soft_uint32(unsigned int word) +{ + static const unsigned char byte_to_unary_table[] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + return (word) > 0xffffff ? byte_to_unary_table[(word) >> 24] : + (word) > 0xffff ? byte_to_unary_table[(word) >> 16] + 8 : + (word) > 0xff ? byte_to_unary_table[(word) >> 8] + 16 : + byte_to_unary_table[(word)] + 24; +} + +static inline unsigned int FLAC__clz_uint32(FLAC__uint32 v) +{ +/* Never used with input 0 */ +#if defined(__INTEL_COMPILER) + return _bit_scan_reverse(v) ^ 31U; +#elif defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +/* This will translate either to (bsr ^ 31U), clz , ctlz, cntlz, lzcnt depending on + * -march= setting or to a software rutine in exotic machines. */ + return __builtin_clz(v); +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + FLAC__uint32 idx; + _BitScanReverse((DWORD*) &idx, v); + return idx ^ 31U; +#else + return FLAC__clz_soft_uint32(v); +#endif +} + +/* This one works with input 0 */ +static inline unsigned int FLAC__clz2_uint32(FLAC__uint32 v) +{ + if (!v) + return 32; + return FLAC__clz_uint32(v); +} + +/* An example of what FLAC__bitmath_ilog2() computes: + * + * ilog2( 0) = undefined + * 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 + */ + +static inline unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) +{ + return sizeof(FLAC__uint32) * CHAR_BIT - 1 - FLAC__clz_uint32(v); +} + + +#ifdef FLAC__INTEGER_ONLY_LIBRARY /*Unused otherwise */ + +static inline unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) +{ + if (v == 0) + return 0; +#if defined(__GNUC__) && (__GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) + return sizeof(FLAC__uint64) * CHAR_BIT - 1 - __builtin_clzll(v); +/* Sorry, only supported in win64/Itanium.. */ +#elif (defined(_MSC_VER) && (_MSC_VER >= 1400)) && (defined(_M_IA64) || defined(_WIN64)) + FLAC__uint64 idx; + _BitScanReverse64(&idx, v); + return idx ^ 63U; +#else +/* Brain-damaged compilers will use the fastest possible way that is, + de Bruijn sequences (http://supertech.csail.mit.edu/papers/debruijn.pdf) + (C) Timothy B. Terriberry (tterribe@xiph.org) 2001-2009 LGPL (v2 or later). +*/ + static const unsigned char DEBRUIJN_IDX64[64]={ + 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, + 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, + 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, + 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 + }; + int ret; + ret= v>0; + v|= v>>1; + v|= v>>2; + v|= v>>4; + v|= v>>8; + v|= v>>16; + v|= v>>32; + v= (v>>1)+1; + ret+=DEBRUIJN_IDX64[v*0x218A392CD3D5DBF>>58&0x3F]; + return ret; +#endif +} +#endif + unsigned FLAC__bitmath_silog2(int v); unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h index 6344532..4d9668f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitreader.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h index 461b8e2..b1ff5e6 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/bitwriter.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h index 0b8a66a..930d678 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/cpu.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h index c9b82d4..6284783 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/crc.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -48,7 +49,7 @@ FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); ** polynomial = x^16 + x^15 + x^2 + x^0 ** init = 0 */ -extern unsigned FLAC__crc16_table[256]; +extern unsigned const 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 */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h index 220273e..0123bed 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/fixed.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,7 +38,7 @@ #endif #include "float.h" -#include "format.h" +#include "../../../format.h" /* * FLAC__fixed_compute_best_predictor() diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h index 13e6d57..74d17ed 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/float.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2004-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h index 6e4b50f..996c7e9 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/format.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h index 431efb3..d639e25 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/lpc.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h index aa3e256..d6651c6 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/memory.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -45,12 +46,13 @@ * Use free() on this address to deallocate. */ void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); -FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); -FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); -FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); -FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); #ifndef FLAC__INTEGER_ONLY_LIBRARY -FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); +FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); #endif +void *safe_malloc_mul_2op_p(size_t size1, size_t size2); #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h index 87f78db..a29050e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/metadata.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2002-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,7 +33,7 @@ #ifndef FLAC__PRIVATE__METADATA_H #define FLAC__PRIVATE__METADATA_H -#include "metadata.h" +#include "FLAC/metadata.h" /* WATCHOUT: all malloc()ed data in the block is free()ed; this may not * be a consistent state (e.g. PICTURE) or equivalent to the initial diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h index a37dbf4..51bf8c3 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/stream_encoder_framing.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h index 9313451..8acec39 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/private/window.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2006,2007 Josh Coalson + * Copyright (C) 2006-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h index 83efdfc..057cd73 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/all.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h index 81c3692..0d5813f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_decoder.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,7 +35,7 @@ #include "../../../stream_decoder.h" #if FLAC__HAS_OGG -#include "include/private/ogg_decoder_aspect.h" +#include "../private/ogg_decoder_aspect.h" #endif typedef struct FLAC__StreamDecoderProtected { diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h index 64dd457..2c632f9 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/include/protected/stream_encoder.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,7 +35,7 @@ #include "../../../stream_encoder.h" #if FLAC__HAS_OGG -#include "private/ogg_encoder_aspect.h" +#include "../private/ogg_encoder_aspect.h" #endif #ifndef FLAC__INTEGER_ONLY_LIBRARY diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c index 48273c0..5741e7c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/lpc_flac.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,14 +35,19 @@ #endif #include + #include "../assert.h" #include "../format.h" +#include "../compat.h" #include "include/private/bitmath.h" #include "include/private/lpc.h" #if defined DEBUG || defined FLAC__OVERFLOW_DETECT || defined FLAC__OVERFLOW_DETECT_VERBOSE #include #endif +/* OPT: #undef'ing this may improve the speed on some architectures */ +#define FLAC__LPC_UNROLLED_FILTER_LOOPS + #ifndef FLAC__INTEGER_ONLY_LIBRARY #ifndef M_LN2 @@ -49,9 +55,18 @@ #define M_LN2 0.69314718055994530942 #endif -/* OPT: #undef'ing this may improve the speed on some architectures */ -#define FLAC__LPC_UNROLLED_FILTER_LOOPS - +#if !defined(HAVE_LROUND) +#if defined(_MSC_VER) +#include +#define copysign _copysign +#elif defined(__GNUC__) +#define copysign __builtin_copysign +#endif +static inline long int lround(double x) { + return (long)(x + copysign (0.5, x)); +} +//If this fails, we are in the precence of a mid 90's compiler..move along... +#endif void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len) { @@ -112,7 +127,7 @@ void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_le void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]) { unsigned i, j; - FLAC__double r, err, ref[FLAC__MAX_LPC_ORDER], lpc[FLAC__MAX_LPC_ORDER]; + FLAC__double r, err, lpc[FLAC__MAX_LPC_ORDER]; FLAC__ASSERT(0 != max_order); FLAC__ASSERT(0 < *max_order); @@ -126,7 +141,7 @@ void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_o r = -autoc[i+1]; for(j = 0; j < i; j++) r -= lpc[j] * autoc[i-j]; - ref[i] = (r/=err); + r /= err; /* Update LPC coefficients and total error. */ lpc[i]=r; @@ -200,14 +215,8 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, FLAC__int32 q; for(i = 0; i < order; i++) { error += lp_coeff[i] * (1 << *shift); -#if 1 /* unfortunately lround() is C99 */ - if(error >= 0.0) - q = (FLAC__int32)(error + 0.5); - else - q = (FLAC__int32)(error - 0.5); -#else q = lround(error); -#endif + #ifdef FLAC__OVERFLOW_DETECT if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); @@ -235,14 +244,7 @@ int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, #endif for(i = 0; i < order; i++) { error += lp_coeff[i] / (1 << nshift); -#if 1 /* unfortunately lround() is C99 */ - if(error >= 0.0) - q = (FLAC__int32)(error + 0.5); - else - q = (FLAC__int32)(error - 0.5); -#else q = lround(error); -#endif #ifdef FLAC__OVERFLOW_DETECT if(q > qmax+1) /* we expect q==qmax+1 occasionally due to rounding */ fprintf(stderr,"FLAC__lpc_quantize_coefficients: quantizer overflow: q>qmax %d>%d shift=%d cmax=%f precision=%u lpc[%u]=%f\n",q,qmax,*shift,cmax,precision+1,i,lp_coeff[i]); @@ -285,13 +287,7 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, u for(j = 0; j < order; j++) { sum += qlp_coeff[j] * (*(--history)); sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); -#if defined _MSC_VER - if(sumo > 2147483647I64 || sumo < -2147483648I64) - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); -#else - if(sumo > 2147483647ll || sumo < -2147483648ll) - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); -#endif + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); } *(residual++) = *(data++) - (sum >> lp_quantization); } @@ -549,19 +545,11 @@ void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *da for(j = 0; j < order; j++) sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { -#if defined _MSC_VER - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); -#else - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); -#endif + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); break; } if(FLAC__bitmath_silog2_wide((FLAC__int64)(*data) - (sum >> lp_quantization)) > 32) { -#if defined _MSC_VER - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%I64d, residual=%I64d\n", i, *data, sum >> lp_quantization, (FLAC__int64)(*data) - (sum >> lp_quantization)); -#else - fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%lld, residual=%lld\n", i, *data, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*data) - (sum >> lp_quantization))); -#endif + fprintf(stderr,"FLAC__lpc_compute_residual_from_qlp_coefficients_wide: OVERFLOW, i=%u, data=%d, sum=%" PRId64 ", residual=%" PRId64 "\n", i, *data, (long long)(sum >> lp_quantization), ((FLAC__int64)(*data) - (sum >> lp_quantization))); break; } *(residual++) = *(data++) - (FLAC__int32)(sum >> lp_quantization); @@ -815,13 +803,8 @@ void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, c for(j = 0; j < order; j++) { sum += qlp_coeff[j] * (*(--history)); sumo += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*history); -#if defined _MSC_VER - if(sumo > 2147483647I64 || sumo < -2147483648I64) - fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%I64d\n",i,j,qlp_coeff[j],*history,sumo); -#else if(sumo > 2147483647ll || sumo < -2147483648ll) - fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%lld\n",i,j,qlp_coeff[j],*history,(long long)sumo); -#endif + fprintf(stderr,"FLAC__lpc_restore_signal: OVERFLOW, i=%u, j=%u, c=%d, d=%d, sumo=%" PRId64 "\n",i,j,qlp_coeff[j],*history,sumo); } *(data++) = *(r++) + (sum >> lp_quantization); } @@ -1079,19 +1062,11 @@ void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_l for(j = 0; j < order; j++) sum += (FLAC__int64)qlp_coeff[j] * (FLAC__int64)(*(--history)); if(FLAC__bitmath_silog2_wide(sum >> lp_quantization) > 32) { -#ifdef _MSC_VER - fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%I64d\n", i, sum >> lp_quantization); -#else - fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%lld\n", i, (long long)(sum >> lp_quantization)); -#endif + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, sum=%" PRId64 "\n", i, (sum >> lp_quantization)); break; } if(FLAC__bitmath_silog2_wide((FLAC__int64)(*r) + (sum >> lp_quantization)) > 32) { -#ifdef _MSC_VER - fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%I64d, data=%I64d\n", i, *r, sum >> lp_quantization, (FLAC__int64)(*r) + (sum >> lp_quantization)); -#else - fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%lld, data=%lld\n", i, *r, (long long)(sum >> lp_quantization), (long long)((FLAC__int64)(*r) + (sum >> lp_quantization))); -#endif + fprintf(stderr,"FLAC__lpc_restore_signal_wide: OVERFLOW, i=%u, residual=%d, sum=%" PRId64 ", data=%" PRId64 "\n", i, *r, (sum >> lp_quantization), ((FLAC__int64)(*r) + (sum >> lp_quantization))); break; } *(data++) = *(r++) + (FLAC__int32)(sum >> lp_quantization); @@ -1352,7 +1327,7 @@ FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scal unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order) { - unsigned order, index, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ + unsigned order, indx, best_index; /* 'index' the index into lpc_error; index==order-1 since lpc_error[0] is for order==1, lpc_error[1] is for order==2, etc */ FLAC__double bits, best_bits, error_scale; FLAC__ASSERT(max_order > 0); @@ -1363,15 +1338,15 @@ unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned m best_index = 0; best_bits = (unsigned)(-1); - for(index = 0, order = 1; index < max_order; index++, order++) { - bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[index], error_scale) * (FLAC__double)(total_samples - order) + (FLAC__double)(order * overhead_bits_per_order); + for(indx = 0, order = 1; indx < max_order; indx++, order++) { + bits = FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(lpc_error[indx], error_scale) * (FLAC__double)(total_samples - order) + (FLAC__double)(order * overhead_bits_per_order); if(bits < best_bits) { - best_index = index; + best_index = indx; best_bits = bits; } } - return best_index+1; /* +1 since index of lpc_error[] is order-1 */ + return best_index+1; /* +1 since indx of lpc_error[] is order-1 */ } #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c index 5cd8e15..c448b87 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/md5.c @@ -1,4 +1,3 @@ - #if HAVE_CONFIG_H # include #endif @@ -9,10 +8,6 @@ #include "include/private/md5.h" #include "../alloc.h" -#ifndef FLaC__INLINE -#define FLaC__INLINE -#endif - /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was @@ -264,12 +259,12 @@ void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); - //memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ if(0 != ctx->internal_buf) { free(ctx->internal_buf); ctx->internal_buf = 0; ctx->capacity = 0; } + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* @@ -407,13 +402,14 @@ FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const return false; if(ctx->capacity < bytes_needed) { - FLAC__byte *tmp = (FLAC__byte*)realloc(ctx->internal_buf, bytes_needed); + FLAC__byte *tmp = (FLAC__byte*) realloc(ctx->internal_buf, bytes_needed); if(0 == tmp) { free(ctx->internal_buf); - if(0 == (ctx->internal_buf = (FLAC__byte*)safe_malloc_(bytes_needed))) + if(0 == (ctx->internal_buf = (FLAC__byte*) safe_malloc_(bytes_needed))) return false; } - ctx->internal_buf = tmp; + else + ctx->internal_buf = tmp; ctx->capacity = bytes_needed; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c index cf6adbd..dd972e0 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/memory.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -71,7 +72,7 @@ void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) return x; } -FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) +FLAC__bool FLAC__memory_alloc_aligned_int32_array(size_t elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) { FLAC__int32 *pu; /* unaligned pointer */ union { /* union needed to comply with C99 pointer aliasing rules */ @@ -84,7 +85,10 @@ FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 FLAC__ASSERT(0 != aligned_pointer); FLAC__ASSERT(unaligned_pointer != aligned_pointer); - pu = (FLAC__int32*)FLAC__memory_alloc_aligned(sizeof(*pu) * (size_t)elements, &u.pv); + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__int32*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); if(0 == pu) { return false; } @@ -97,7 +101,7 @@ FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 } } -FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) +FLAC__bool FLAC__memory_alloc_aligned_uint32_array(size_t elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) { FLAC__uint32 *pu; /* unaligned pointer */ union { /* union needed to comply with C99 pointer aliasing rules */ @@ -110,7 +114,10 @@ FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint FLAC__ASSERT(0 != aligned_pointer); FLAC__ASSERT(unaligned_pointer != aligned_pointer); - pu = (FLAC__uint32*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__uint32*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); if(0 == pu) { return false; } @@ -123,7 +130,7 @@ FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint } } -FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) +FLAC__bool FLAC__memory_alloc_aligned_uint64_array(size_t elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) { FLAC__uint64 *pu; /* unaligned pointer */ union { /* union needed to comply with C99 pointer aliasing rules */ @@ -136,7 +143,10 @@ FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint FLAC__ASSERT(0 != aligned_pointer); FLAC__ASSERT(unaligned_pointer != aligned_pointer); - pu = (FLAC__uint64*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__uint64*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); if(0 == pu) { return false; } @@ -149,7 +159,7 @@ FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint } } -FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) +FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(size_t elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) { unsigned *pu; /* unaligned pointer */ union { /* union needed to comply with C99 pointer aliasing rules */ @@ -162,7 +172,10 @@ FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned FLAC__ASSERT(0 != aligned_pointer); FLAC__ASSERT(unaligned_pointer != aligned_pointer); - pu = (unsigned*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (unsigned int*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); if(0 == pu) { return false; } @@ -177,7 +190,7 @@ FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned #ifndef FLAC__INTEGER_ONLY_LIBRARY -FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) +FLAC__bool FLAC__memory_alloc_aligned_real_array(size_t elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) { FLAC__real *pu; /* unaligned pointer */ union { /* union needed to comply with C99 pointer aliasing rules */ @@ -190,7 +203,10 @@ FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real * FLAC__ASSERT(0 != aligned_pointer); FLAC__ASSERT(unaligned_pointer != aligned_pointer); - pu = (FLAC__real*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); + if(elements > SIZE_MAX / sizeof(*pu)) /* overflow check */ + return false; + + pu = (FLAC__real*) FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); if(0 == pu) { return false; } @@ -204,3 +220,12 @@ FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real * } #endif + +void *safe_malloc_mul_2op_p(size_t size1, size_t size2) +{ + if(!size1 || !size2) + return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ + if(size1 > SIZE_MAX / size2) + return 0; + return malloc(size1*size2); +} diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c index 77f66f3..04e1766 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,25 +34,12 @@ # include #endif -#if defined _MSC_VER || defined __MINGW32__ -#include /* for _setmode() */ -#include /* for _O_BINARY */ -#endif -#if defined __CYGWIN__ || defined __EMX__ -#include /* for setmode(), O_BINARY */ -#include /* for _O_BINARY */ -#endif #include #include /* for malloc() */ #include /* for memset/memcpy() */ #include /* for stat() */ #include /* for off_t */ -#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ -#if _MSC_VER <= 1700 || defined __BORLANDC__ /* @@@ [2G limit] */ -#define fseeko fseek -#define ftello ftell -#endif -#endif +#include "../compat.h" #include "../assert.h" #include "../alloc.h" #include "include/protected/stream_decoder.h" @@ -65,18 +53,6 @@ #include "include/private/md5.h" #include "include/private/memory.h" -#ifdef max -#undef max -#endif -#define max(a,b) ((a)>(b)?(a):(b)) - -/* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ -#ifdef _MSC_VER -#define FLAC__U64L(x) x -#else -#define FLAC__U64L(x) x##LLU -#endif - /* technically this should be in an "export.c" but this is convenient enough */ FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC = @@ -102,8 +78,8 @@ static FLAC__byte ID3V2_TAG_[3] = { 'I', 'D', '3' }; * ***********************************************************************/ -static void set_defaults_dec(FLAC__StreamDecoder *decoder); -static FILE *get_binary_stdin_(void); +static void set_defaults_(FLAC__StreamDecoder *decoder); +//static FILE *get_binary_stdin_(void); static FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels); static FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id); static FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder); @@ -135,11 +111,11 @@ static FLAC__bool seek_to_absolute_sample_(FLAC__StreamDecoder *decoder, FLAC__u #if FLAC__HAS_OGG static FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint64 stream_length, FLAC__uint64 target_sample); #endif -static FLAC__StreamDecoderReadStatus file_read_callback_dec (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); -static FLAC__StreamDecoderSeekStatus file_seek_callback_dec (const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); -static FLAC__StreamDecoderTellStatus file_tell_callback_dec (const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); -static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); -static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); +//static FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +//static FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data); +//static FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +//static FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data); +//static FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_data); /*********************************************************************** * @@ -167,7 +143,7 @@ typedef struct FLAC__StreamDecoderPrivate { 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); + FLAC__bool (*local_bitreader_read_rice_signed_block)(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); void *client_data; FILE *file; /* only used if FLAC__stream_decoder_init_file()/FLAC__stream_decoder_init_file() called, else NULL */ FLAC__BitReader *input; @@ -281,18 +257,18 @@ FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ - decoder = (FLAC__StreamDecoder*)calloc(1, sizeof(FLAC__StreamDecoder)); + decoder = (FLAC__StreamDecoder*) calloc(1, sizeof(FLAC__StreamDecoder)); if(decoder == 0) { return 0; } - decoder->protected_ = (FLAC__StreamDecoderProtected*)calloc(1, sizeof(FLAC__StreamDecoderProtected)); + decoder->protected_ = (FLAC__StreamDecoderProtected*) calloc(1, sizeof(FLAC__StreamDecoderProtected)); if(decoder->protected_ == 0) { free(decoder); return 0; } - decoder->private_ = (FLAC__StreamDecoderPrivate*)calloc(1, sizeof(FLAC__StreamDecoderPrivate)); + decoder->private_ = (FLAC__StreamDecoderPrivate*) calloc(1, sizeof(FLAC__StreamDecoderPrivate)); if(decoder->private_ == 0) { free(decoder->protected_); free(decoder); @@ -308,7 +284,7 @@ FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) } decoder->private_->metadata_filter_ids_capacity = 16; - if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*) malloc((FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8) * decoder->private_->metadata_filter_ids_capacity))) { FLAC__bitreader_delete(decoder->private_->input); free(decoder->private_); free(decoder->protected_); @@ -330,7 +306,7 @@ FLAC_API FLAC__StreamDecoder *FLAC__stream_decoder_new(void) decoder->private_->file = 0; - set_defaults_dec(decoder); + set_defaults_(decoder); decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; @@ -341,7 +317,9 @@ FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) { unsigned i; - FLAC__ASSERT(0 != decoder); + if (decoder == NULL) + return ; + FLAC__ASSERT(0 != decoder->protected_); FLAC__ASSERT(0 != decoder->private_); FLAC__ASSERT(0 != decoder->private_->input); @@ -367,7 +345,7 @@ FLAC_API void FLAC__stream_decoder_delete(FLAC__StreamDecoder *decoder) * ***********************************************************************/ -static FLAC__StreamDecoderInitStatus init_stream_internal_dec( +static FLAC__StreamDecoderInitStatus init_stream_internal_( FLAC__StreamDecoder *decoder, FLAC__StreamDecoderReadCallback read_callback, FLAC__StreamDecoderSeekCallback seek_callback, @@ -492,7 +470,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream( void *client_data ) { - return init_stream_internal_dec( + return init_stream_internal_( decoder, read_callback, seek_callback, @@ -520,7 +498,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( void *client_data ) { - return init_stream_internal_dec( + return init_stream_internal_( decoder, read_callback, seek_callback, @@ -535,6 +513,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_stream( ); } +#if 0 static FLAC__StreamDecoderInitStatus init_FILE_internal_( FLAC__StreamDecoder *decoder, FILE *file, @@ -564,11 +543,11 @@ static FLAC__StreamDecoderInitStatus init_FILE_internal_( decoder->private_->file = file; - return init_stream_internal_dec( + return init_stream_internal_( decoder, - file_read_callback_dec, - decoder->private_->file == stdin? 0: file_seek_callback_dec, - decoder->private_->file == stdin? 0: file_tell_callback_dec, + file_read_callback_, + decoder->private_->file == stdin? 0: file_seek_callback_, + decoder->private_->file == stdin? 0: file_tell_callback_, decoder->private_->file == stdin? 0: file_length_callback_, file_eof_callback_, write_callback, @@ -628,7 +607,7 @@ 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); - file = filename? fopen(filename, "rb") : stdin; + file = filename? flac_fopen(filename, "rb") : stdin; if(0 == file) return FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE; @@ -659,6 +638,7 @@ FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_ogg_file( { return init_file_internal_(decoder, filename, write_callback, metadata_callback, error_callback, client_data, /*is_ogg=*/true); } +#endif FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) { @@ -719,7 +699,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_finish(FLAC__StreamDecoder *decoder) } decoder->private_->is_seeking = false; - set_defaults_dec(decoder); + set_defaults_(decoder); decoder->protected_->state = FLAC__STREAM_DECODER_UNINITIALIZED; @@ -785,7 +765,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_respond_application(FLAC__ FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { - if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*) safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -844,7 +824,7 @@ FLAC_API FLAC__bool FLAC__stream_decoder_set_metadata_ignore_application(FLAC__S FLAC__ASSERT(0 != decoder->private_->metadata_filter_ids); if(decoder->private_->metadata_filter_ids_count == decoder->private_->metadata_filter_ids_capacity) { - if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*)safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { + if(0 == (decoder->private_->metadata_filter_ids = (FLAC__byte*) safe_realloc_mul_2op_(decoder->private_->metadata_filter_ids, decoder->private_->metadata_filter_ids_capacity, /*times*/2))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1251,7 +1231,7 @@ unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecod * ***********************************************************************/ -void set_defaults_dec(FLAC__StreamDecoder *decoder) +void set_defaults_(FLAC__StreamDecoder *decoder) { #if FLAC__HAS_OGG decoder->private_->is_ogg = false; @@ -1277,6 +1257,7 @@ void set_defaults_dec(FLAC__StreamDecoder *decoder) #endif } +#if 0 /* * This will forcibly set stdin to binary mode (for OSes that require it) */ @@ -1297,6 +1278,7 @@ FILE *get_binary_stdin_(void) return stdin; } +#endif FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigned channels) { @@ -1326,7 +1308,7 @@ FLAC__bool allocate_output_(FLAC__StreamDecoder *decoder, unsigned size, unsigne * (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/*)*/); + tmp = (FLAC__int32*) safe_malloc_muladd2_(sizeof(FLAC__int32), /*times (*/size, /*+*/4/*)*/); if(tmp == 0) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; @@ -1407,7 +1389,7 @@ FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) decoder->private_->lookahead = (FLAC__byte)x; decoder->private_->cached = true; } - else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */ decoder->private_->header_warmup[1] = (FLAC__byte)x; decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; return true; @@ -1497,7 +1479,7 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) 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))) { + if(0 == (block.data.application.data = (FLAC__byte*) malloc(real_length))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1525,7 +1507,7 @@ FLAC__bool read_metadata_(FLAC__StreamDecoder *decoder) break; default: if(real_length > 0) { - if(0 == (block.data.unknown.data = (FLAC__byte*)malloc(real_length))) { + if(0 == (block.data.unknown.data = (FLAC__byte*) malloc(real_length))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1679,7 +1661,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)))) { + 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; } @@ -1718,7 +1700,7 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->vendor_string.length)) return false; /* read_callback_ sets the state for us */ if(obj->vendor_string.length > 0) { - if(0 == (obj->vendor_string.entry = (FLAC__byte*)safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { + if(0 == (obj->vendor_string.entry = (FLAC__byte*) safe_malloc_add_2op_(obj->vendor_string.length, /*+*/1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1736,7 +1718,7 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre /* 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)))) { + if(0 == (obj->comments = (FLAC__StreamMetadata_VorbisComment_Entry*) safe_malloc_mul_2op_p(obj->num_comments, /*times*/sizeof(FLAC__StreamMetadata_VorbisComment_Entry)))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1745,7 +1727,7 @@ FLAC__bool read_metadata_vorbiscomment_(FLAC__StreamDecoder *decoder, FLAC__Stre if(!FLAC__bitreader_read_uint32_little_endian(decoder->private_->input, &obj->comments[i].length)) return false; /* read_callback_ sets the state for us */ if(obj->comments[i].length > 0) { - if(0 == (obj->comments[i].entry = (FLAC__byte*)safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { + if(0 == (obj->comments[i].entry = (FLAC__byte*) safe_malloc_add_2op_(obj->comments[i].length, /*+*/1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1791,7 +1773,7 @@ FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMet obj->num_tracks = x; if(obj->num_tracks > 0) { - if(0 == (obj->tracks = (FLAC__StreamMetadata_CueSheet_Track*)safe_calloc_(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) { + if(0 == (obj->tracks = (FLAC__StreamMetadata_CueSheet_Track*) safe_calloc_(obj->num_tracks, sizeof(FLAC__StreamMetadata_CueSheet_Track)))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1824,18 +1806,18 @@ FLAC__bool read_metadata_cuesheet_(FLAC__StreamDecoder *decoder, FLAC__StreamMet track->num_indices = (FLAC__byte)x; if(track->num_indices > 0) { - if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*)safe_calloc_(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) { + if(0 == (track->indices = (FLAC__StreamMetadata_CueSheet_Index*) safe_calloc_(track->num_indices, sizeof(FLAC__StreamMetadata_CueSheet_Index)))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } for(j = 0; j < track->num_indices; j++) { - FLAC__StreamMetadata_CueSheet_Index *index = &track->indices[j]; - if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + FLAC__StreamMetadata_CueSheet_Index *indx = &track->indices[j]; + if(!FLAC__bitreader_read_raw_uint64(decoder->private_->input, &indx->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) return false; /* read_callback_ sets the state for us */ if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) return false; /* read_callback_ sets the state for us */ - index->number = (FLAC__byte)x; + indx->number = (FLAC__byte)x; if(!FLAC__bitreader_skip_bits_no_crc(decoder->private_->input, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) return false; /* read_callback_ sets the state for us */ @@ -1861,7 +1843,7 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta /* 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))) { + if(0 == (obj->mime_type = (char*) safe_malloc_add_2op_(x, /*+*/1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1874,7 +1856,7 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta /* 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))) { + if(0 == (obj->description = (FLAC__byte*) safe_malloc_add_2op_(x, /*+*/1))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1903,7 +1885,7 @@ FLAC__bool read_metadata_picture_(FLAC__StreamDecoder *decoder, FLAC__StreamMeta /* 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))) { + if(0 == (obj->data = (FLAC__byte*) safe_malloc_(obj->data_length))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -1977,7 +1959,7 @@ FLAC__bool frame_sync_(FLAC__StreamDecoder *decoder) decoder->private_->lookahead = (FLAC__byte)x; decoder->private_->cached = true; } - else if(x >> 2 == 0x3e) { /* MAGIC NUMBER for the last 6 sync bits */ + else if(x >> 1 == 0x7c) { /* MAGIC NUMBER for the last 6 sync bits and reserved 7th bit */ decoder->private_->header_warmup[1] = (FLAC__byte)x; decoder->protected_->state = FLAC__STREAM_DECODER_READ_FRAME; return true; @@ -2737,7 +2719,7 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigne } } - if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, max(6, partition_order))) { + if(!FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, flac_max(6u, partition_order))) { decoder->protected_->state = FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR; return false; } @@ -2750,7 +2732,7 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigne if(rice_parameter < pesc) { partitioned_rice_contents->raw_bits[partition] = 0; u = (partition_order == 0 || partition > 0)? partition_samples : partition_samples - predictor_order; - if(!decoder->private_->local_bitreader_read_rice_signed_block(decoder->private_->input, (int*) residual + sample, u, rice_parameter)) + if(!decoder->private_->local_bitreader_read_rice_signed_block(decoder->private_->input, residual + sample, u, rice_parameter)) return false; /* read_callback_ sets the state for us */ sample += u; } @@ -2759,7 +2741,7 @@ FLAC__bool read_residual_partitioned_rice_(FLAC__StreamDecoder *decoder, unsigne return false; /* read_callback_ sets the state for us */ partitioned_rice_contents->raw_bits[partition] = rice_parameter; for(u = (partition_order == 0 || partition > 0)? 0 : predictor_order; u < partition_samples; u++, sample++) { - if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, (FLAC__int32*) &i, rice_parameter)) + if(!FLAC__bitreader_read_raw_int32(decoder->private_->input, &i, rice_parameter)) return false; /* read_callback_ sets the state for us */ residual[sample] = i; } @@ -2942,22 +2924,23 @@ FLAC__StreamDecoderWriteStatus write_audio_frame_to_client_(FLAC__StreamDecoder return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); } } - - return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + else { + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; + } + } + else { + /* + * If we never got STREAMINFO, turn off MD5 checking to save + * cycles since we don't have a sum to compare to anyway + */ + if(!decoder->private_->has_stream_info) + decoder->private_->do_md5_checking = false; + if(decoder->private_->do_md5_checking) { + if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + } + return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); } - - /* - * If we never got STREAMINFO, turn off MD5 checking to save - * cycles since we don't have a sum to compare to anyway - */ - if(!decoder->private_->has_stream_info) - decoder->private_->do_md5_checking = false; - if(decoder->private_->do_md5_checking) { - if(!FLAC__MD5Accumulate(&decoder->private_->md5context, buffer, frame->header.channels, frame->header.blocksize, (frame->header.bits_per_sample+7) / 8)) - return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; - } - - return decoder->private_->write_callback(decoder, frame, buffer, decoder->private_->client_data); } void send_error_to_client_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status) @@ -3319,7 +3302,8 @@ FLAC__bool seek_to_absolute_sample_ogg_(FLAC__StreamDecoder *decoder, FLAC__uint } #endif -FLAC__StreamDecoderReadStatus file_read_callback_dec(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +#if 0 +FLAC__StreamDecoderReadStatus file_read_callback_(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { (void)client_data; @@ -3336,21 +3320,21 @@ FLAC__StreamDecoderReadStatus file_read_callback_dec(const FLAC__StreamDecoder * return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */ } -FLAC__StreamDecoderSeekStatus file_seek_callback_dec(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) +FLAC__StreamDecoderSeekStatus file_seek_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) { (void)client_data; if(decoder->private_->file == stdin) return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED; - else if(fseeko(decoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + else if(fseeko(decoder->private_->file, (FLAC__off_t)absolute_byte_offset, SEEK_SET) < 0) return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; else return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } -FLAC__StreamDecoderTellStatus file_tell_callback_dec(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +FLAC__StreamDecoderTellStatus file_tell_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { - off_t pos; + FLAC__off_t pos; (void)client_data; if(decoder->private_->file == stdin) @@ -3365,12 +3349,12 @@ FLAC__StreamDecoderTellStatus file_tell_callback_dec(const FLAC__StreamDecoder * FLAC__StreamDecoderLengthStatus file_length_callback_(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { - struct stat filestats; + struct flac_stat_s filestats; (void)client_data; if(decoder->private_->file == stdin) return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED; - else if(fstat(fileno(decoder->private_->file), &filestats) != 0) + else if(flac_fstat(fileno(decoder->private_->file), &filestats) != 0) return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; else { *stream_length = (FLAC__uint64)filestats.st_size; @@ -3384,3 +3368,5 @@ FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_d return feof(decoder->private_->file)? true : false; } + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c index e592d1e..20e98da 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,27 +34,12 @@ # include #endif -#if defined _MSC_VER || defined __MINGW32__ -#include /* for _setmode() */ -#include /* for _O_BINARY */ -#endif -#if defined __CYGWIN__ || defined __EMX__ -#include /* for setmode(), O_BINARY */ -#include /* for _O_BINARY */ -#endif #include #include #include /* for malloc() */ #include /* for memcpy() */ #include /* for off_t */ -#if defined _MSC_VER || defined __BORLANDC__ || defined __MINGW32__ -#if _MSC_VER <= 1700 || defined __BORLANDC__ /* @@@ [2G limit] */ -#define fseeko fseek -#define ftello ftell -#endif -#endif #include "../assert.h" -#include "../alloc.h" #include "../stream_decoder.h" #include "include/protected/stream_encoder.h" #include "include/private/bitwriter.h" @@ -71,20 +57,9 @@ #endif #include "include/private/stream_encoder_framing.h" #include "include/private/window.h" +#include "../alloc.h" +#include "../compat.h" -#ifndef FLaC__INLINE -#define FLaC__INLINE -#endif - -#ifdef min -#undef min -#endif -#define min(x,y) ((x)<(y)?(x):(y)) - -#ifdef max -#undef max -#endif -#define max(x,y) ((x)>(y)?(x):(y)) /* Exact Rice codeword length calculation is off by default. The simple * (and fast) estimation (of how many bits a residual value will be @@ -147,7 +122,7 @@ static struct CompressionLevels { * ***********************************************************************/ -static void set_defaults_enc(FLAC__StreamEncoder *encoder); +static void set_defaults_(FLAC__StreamEncoder *encoder); static void free_(FLAC__StreamEncoder *encoder); static FLAC__bool resize_buffers_(FLAC__StreamEncoder *encoder, unsigned new_blocksize); static FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC__bool is_last_block); @@ -316,11 +291,11 @@ static FLAC__StreamDecoderWriteStatus verify_write_callback_(const FLAC__StreamD static void verify_metadata_callback_(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data); static void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); -static FLAC__StreamEncoderReadStatus file_read_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); -static FLAC__StreamEncoderSeekStatus file_seek_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); -static FLAC__StreamEncoderTellStatus file_tell_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); -static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); -static FILE *get_binary_stdout_(void); +//static FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data); +//static FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); +//static FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +//static FLAC__StreamEncoderWriteStatus file_write_callback_(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); +//static FILE *get_binary_stdout_(void); /*********************************************************************** @@ -477,7 +452,7 @@ FLAC_API const char * const FLAC__StreamEncoderInitStatusString[] = { "FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED" }; -FLAC_API const char * const FLAC__treamEncoderReadStatusString[] = { +FLAC_API const char * const FLAC__StreamEncoderReadStatusString[] = { "FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE", "FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM", "FLAC__STREAM_ENCODER_READ_STATUS_ABORT", @@ -526,18 +501,18 @@ FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void) FLAC__ASSERT(sizeof(int) >= 4); /* we want to die right away if this is not true */ - encoder = (FLAC__StreamEncoder*)calloc(1, sizeof(FLAC__StreamEncoder)); + encoder = (FLAC__StreamEncoder*) calloc(1, sizeof(FLAC__StreamEncoder)); if(encoder == 0) { return 0; } - encoder->protected_ = (FLAC__StreamEncoderProtected*)calloc(1, sizeof(FLAC__StreamEncoderProtected)); + encoder->protected_ = (FLAC__StreamEncoderProtected*) calloc(1, sizeof(FLAC__StreamEncoderProtected)); if(encoder->protected_ == 0) { free(encoder); return 0; } - encoder->private_ = (FLAC__StreamEncoderPrivate*)calloc(1, sizeof(FLAC__StreamEncoderPrivate)); + encoder->private_ = (FLAC__StreamEncoderPrivate*) calloc(1, sizeof(FLAC__StreamEncoderPrivate)); if(encoder->private_ == 0) { free(encoder->protected_); free(encoder); @@ -554,7 +529,7 @@ FLAC_API FLAC__StreamEncoder *FLAC__stream_encoder_new(void) encoder->private_->file = 0; - set_defaults_enc(encoder); + set_defaults_(encoder); encoder->private_->is_being_deleted = false; @@ -595,7 +570,9 @@ FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder) { unsigned i; - FLAC__ASSERT(0 != encoder); + if (encoder == NULL) + return ; + FLAC__ASSERT(0 != encoder->protected_); FLAC__ASSERT(0 != encoder->private_); FLAC__ASSERT(0 != encoder->private_->frame); @@ -630,7 +607,7 @@ FLAC_API void FLAC__stream_encoder_delete(FLAC__StreamEncoder *encoder) * ***********************************************************************/ -static FLAC__StreamEncoderInitStatus init_stream_internal_enc( +static FLAC__StreamEncoderInitStatus init_stream_internal_( FLAC__StreamEncoder *encoder, FLAC__StreamEncoderReadCallback read_callback, FLAC__StreamEncoderWriteCallback write_callback, @@ -696,7 +673,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( 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); + encoder->protected_->qlp_coeff_precision = flac_max(FLAC__MIN_QLP_COEFF_PRECISION, 2 + encoder->protected_->bits_per_sample / 2); } else if(encoder->protected_->bits_per_sample == 16) { if(encoder->protected_->blocksize <= 192) @@ -728,20 +705,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( return FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION; if(encoder->protected_->streamable_subset) { - if( - encoder->protected_->blocksize != 192 && - encoder->protected_->blocksize != 576 && - encoder->protected_->blocksize != 1152 && - encoder->protected_->blocksize != 2304 && - encoder->protected_->blocksize != 4608 && - encoder->protected_->blocksize != 256 && - encoder->protected_->blocksize != 512 && - encoder->protected_->blocksize != 1024 && - encoder->protected_->blocksize != 2048 && - encoder->protected_->blocksize != 4096 && - encoder->protected_->blocksize != 8192 && - encoder->protected_->blocksize != 16384 - ) + if(!FLAC__format_blocksize_is_subset(encoder->protected_->blocksize, encoder->protected_->sample_rate)) return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; if(!FLAC__format_sample_rate_is_subset(encoder->protected_->sample_rate)) return FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE; @@ -774,12 +738,12 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( #if FLAC__HAS_OGG /* reorder metadata if necessary to ensure that any VORBIS_COMMENT is the first, according to the mapping spec */ if(is_ogg && 0 != encoder->protected_->metadata && encoder->protected_->num_metadata_blocks > 1) { - unsigned i; - for(i = 1; i < encoder->protected_->num_metadata_blocks; i++) { - if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - FLAC__StreamMetadata *vc = encoder->protected_->metadata[i]; - for( ; i > 0; i--) - encoder->protected_->metadata[i] = encoder->protected_->metadata[i-1]; + unsigned i1; + for(i1 = 1; i1 < encoder->protected_->num_metadata_blocks; i1++) { + if(0 != encoder->protected_->metadata[i1] && encoder->protected_->metadata[i1]->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + FLAC__StreamMetadata *vc = encoder->protected_->metadata[i1]; + for( ; i1 > 0; i1--) + encoder->protected_->metadata[i1] = encoder->protected_->metadata[i1-1]; encoder->protected_->metadata[0] = vc; break; } @@ -788,10 +752,10 @@ 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++) { - if(0 != encoder->protected_->metadata[i] && encoder->protected_->metadata[i]->type == FLAC__METADATA_TYPE_SEEKTABLE) { - encoder->private_->seek_table = &encoder->protected_->metadata[i]->data.seek_table; + unsigned i2; + for(i2 = 0; i2 < encoder->protected_->num_metadata_blocks; i2++) { + if(0 != encoder->protected_->metadata[i2] && encoder->protected_->metadata[i2]->type == FLAC__METADATA_TYPE_SEEKTABLE) { + encoder->private_->seek_table = &encoder->protected_->metadata[i2]->data.seek_table; break; /* take only the first one */ } } @@ -898,7 +862,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( encoder->private_->current_frame_number = 0; encoder->private_->use_wide_by_block = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(encoder->protected_->blocksize)+1 > 30); - encoder->private_->use_wide_by_order = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(max(encoder->protected_->max_lpc_order, FLAC__MAX_FIXED_ORDER))+1 > 30); /*@@@ need to use this? */ + encoder->private_->use_wide_by_order = (encoder->protected_->bits_per_sample + FLAC__bitmath_ilog2(flac_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 */ /* @@ -994,7 +958,7 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( */ 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))) { + if(0 == (encoder->private_->verify.input_fifo.data[i] = (FLAC__int32*) safe_malloc_mul_2op_p(sizeof(FLAC__int32), /*times*/encoder->private_->verify.input_fifo.size))) { encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR; return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; } @@ -1004,10 +968,12 @@ static FLAC__StreamEncoderInitStatus init_stream_internal_enc( /* * Now set up a stream decoder for verification */ - encoder->private_->verify.decoder = FLAC__stream_decoder_new(); if(0 == encoder->private_->verify.decoder) { - encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; - return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + encoder->private_->verify.decoder = FLAC__stream_decoder_new(); + if(0 == encoder->private_->verify.decoder) { + encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; + return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; + } } if(FLAC__stream_decoder_init_stream(encoder->private_->verify.decoder, verify_read_callback_, /*seek_callback=*/0, /*tell_callback=*/0, /*length_callback=*/0, /*eof_callback=*/0, verify_write_callback_, verify_metadata_callback_, verify_error_callback_, /*client_data=*/encoder) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { @@ -1147,7 +1113,7 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_stream( void *client_data ) { - return init_stream_internal_enc( + return init_stream_internal_( encoder, /*read_callback=*/0, write_callback, @@ -1169,7 +1135,7 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream( void *client_data ) { - return init_stream_internal_enc( + return init_stream_internal_( encoder, read_callback, write_callback, @@ -1181,11 +1147,12 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_stream( ); } -static FLAC__StreamEncoderInitStatus init_FILE_internal_enc( +#if 0 +static FLAC__StreamEncoderInitStatus init_FILE_internal_( FLAC__StreamEncoder *encoder, FILE *file, FLAC__StreamEncoderProgressCallback progress_callback, - void *client_data, + void * /*client_data*/, FLAC__bool is_ogg ) { @@ -1218,12 +1185,12 @@ static FLAC__StreamEncoderInitStatus init_FILE_internal_enc( encoder->private_->samples_written = 0; encoder->private_->frames_written = 0; - init_status = init_stream_internal_enc( + init_status = init_stream_internal_( encoder, - encoder->private_->file == stdout? 0 : is_ogg? file_read_callback_enc : 0, + encoder->private_->file == stdout? 0 : is_ogg? file_read_callback_ : 0, file_write_callback_, - encoder->private_->file == stdout? 0 : file_seek_callback_enc, - encoder->private_->file == stdout? 0 : file_tell_callback_enc, + encoder->private_->file == stdout? 0 : file_seek_callback_, + encoder->private_->file == stdout? 0 : file_tell_callback_, /*metadata_callback=*/0, client_data, is_ogg @@ -1250,7 +1217,7 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_FILE( void *client_data ) { - return init_FILE_internal_enc(encoder, file, progress_callback, client_data, /*is_ogg=*/false); + return init_FILE_internal_(encoder, file, progress_callback, client_data, /*is_ogg=*/false); } FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_FILE( @@ -1260,10 +1227,10 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_FILE( void *client_data ) { - return init_FILE_internal_enc(encoder, file, progress_callback, client_data, /*is_ogg=*/true); + return init_FILE_internal_(encoder, file, progress_callback, client_data, /*is_ogg=*/true); } -static FLAC__StreamEncoderInitStatus init_file_internal_enc( +static FLAC__StreamEncoderInitStatus init_file_internal_( FLAC__StreamEncoder *encoder, const char *filename, FLAC__StreamEncoderProgressCallback progress_callback, @@ -1283,14 +1250,14 @@ static FLAC__StreamEncoderInitStatus init_file_internal_enc( if(encoder->protected_->state != FLAC__STREAM_ENCODER_UNINITIALIZED) return FLAC__STREAM_ENCODER_INIT_STATUS_ALREADY_INITIALIZED; - file = filename? fopen(filename, "w+b") : stdout; + file = filename? flac_fopen(filename, "w+b") : stdout; if(file == 0) { encoder->protected_->state = FLAC__STREAM_ENCODER_IO_ERROR; return FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR; } - return init_FILE_internal_enc(encoder, file, progress_callback, client_data, is_ogg); + return init_FILE_internal_(encoder, file, progress_callback, client_data, is_ogg); } FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file( @@ -1300,7 +1267,7 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_file( void *client_data ) { - return init_file_internal_enc(encoder, filename, progress_callback, client_data, /*is_ogg=*/false); + return init_file_internal_(encoder, filename, progress_callback, client_data, /*is_ogg=*/false); } FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_file( @@ -1310,8 +1277,9 @@ FLAC_API FLAC__StreamEncoderInitStatus FLAC__stream_encoder_init_ogg_file( void *client_data ) { - return init_file_internal_enc(encoder, filename, progress_callback, client_data, /*is_ogg=*/true); + return init_file_internal_(encoder, filename, progress_callback, client_data, /*is_ogg=*/true); } +#endif FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder) { @@ -1373,7 +1341,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_finish(FLAC__StreamEncoder *encoder) #endif free_(encoder); - set_defaults_enc(encoder); + set_defaults_(encoder); if(!error) encoder->protected_->state = FLAC__STREAM_ENCODER_UNINITIALIZED; @@ -1422,7 +1390,6 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_streamable_subset(FLAC__StreamEncod return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_set_do_md5(FLAC__StreamEncoder *encoder, FLAC__bool value); FLAC_API FLAC__bool FLAC__stream_encoder_set_do_md5(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); @@ -1734,7 +1701,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encod } if(num_blocks) { FLAC__StreamMetadata **m; - if(0 == (m = (FLAC__StreamMetadata**)safe_malloc_mul_2op_(sizeof(m[0]), /*times*/num_blocks))) + if(0 == (m = (FLAC__StreamMetadata**) safe_malloc_mul_2op_p(sizeof(m[0]), /*times*/num_blocks))) return false; memcpy(m, metadata, sizeof(m[0]) * num_blocks); encoder->protected_->metadata = m; @@ -1751,7 +1718,6 @@ FLAC_API FLAC__bool FLAC__stream_encoder_set_metadata(FLAC__StreamEncoder *encod * 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_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); @@ -1763,7 +1729,6 @@ FLAC_API FLAC__bool FLAC__stream_encoder_disable_constant_subframes(FLAC__Stream return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value); FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); @@ -1775,7 +1740,6 @@ FLAC_API FLAC__bool FLAC__stream_encoder_disable_fixed_subframes(FLAC__StreamEnc return true; } -FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value); FLAC_API FLAC__bool FLAC__stream_encoder_disable_verbatim_subframes(FLAC__StreamEncoder *encoder, FLAC__bool value) { FLAC__ASSERT(0 != encoder); @@ -1852,7 +1816,6 @@ FLAC_API FLAC__bool FLAC__stream_encoder_get_streamable_subset(const FLAC__Strea return encoder->protected_->streamable_subset; } -FLAC_API FLAC__bool FLAC__stream_encoder_get_do_md5(const FLAC__StreamEncoder *encoder); FLAC_API FLAC__bool FLAC__stream_encoder_get_do_md5(const FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); @@ -1992,7 +1955,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process(FLAC__StreamEncoder *encoder, c FLAC__ASSERT(encoder->protected_->state == FLAC__STREAM_ENCODER_OK); do { - const unsigned n = min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j); + const unsigned n = flac_min(blocksize+OVERREAD_-encoder->private_->current_sample_number, samples-j); if(encoder->protected_->verify) append_to_verify_fifo_(&encoder->private_->verify.input_fifo, buffer, j, channels, n); @@ -2055,7 +2018,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder */ 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)); + append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, flac_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++) { @@ -2090,7 +2053,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder */ 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)); + append_to_verify_fifo_interleaved_(&encoder->private_->verify.input_fifo, buffer, j, channels, flac_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++) { @@ -2121,7 +2084,7 @@ FLAC_API FLAC__bool FLAC__stream_encoder_process_interleaved(FLAC__StreamEncoder * ***********************************************************************/ -void set_defaults_enc(FLAC__StreamEncoder *encoder) +void set_defaults_(FLAC__StreamEncoder *encoder) { FLAC__ASSERT(0 != encoder); @@ -2173,6 +2136,8 @@ void set_defaults_enc(FLAC__StreamEncoder *encoder) #if FLAC__HAS_OGG FLAC__ogg_encoder_aspect_set_defaults(&encoder->protected_->ogg_encoder_aspect); #endif + + FLAC__stream_encoder_set_compression_level(encoder, 5); } void free_(FLAC__StreamEncoder *encoder) @@ -2430,8 +2395,8 @@ FLAC__bool write_bitbuffer_(FLAC__StreamEncoder *encoder, unsigned samples, FLAC FLAC__bitwriter_clear(encoder->private_->frame); if(samples > 0) { - encoder->private_->streaminfo.data.stream_info.min_framesize = min(bytes, encoder->private_->streaminfo.data.stream_info.min_framesize); - encoder->private_->streaminfo.data.stream_info.max_framesize = max(bytes, encoder->private_->streaminfo.data.stream_info.max_framesize); + encoder->private_->streaminfo.data.stream_info.min_framesize = flac_min(bytes, (size_t) encoder->private_->streaminfo.data.stream_info.min_framesize); + encoder->private_->streaminfo.data.stream_info.max_framesize = flac_max(bytes, (size_t) encoder->private_->streaminfo.data.stream_info.max_framesize); } return true; @@ -2442,6 +2407,10 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const FLAC__StreamEncoderWriteStatus status; FLAC__uint64 output_position = 0; +#if FLAC__HAS_OGG == 0 + (void)is_last_block; +#endif + /* 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; @@ -2518,7 +2487,7 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const * 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); + encoder->private_->frames_written = flac_max(encoder->private_->frames_written, encoder->private_->current_frame_number+1); } else encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; @@ -2529,7 +2498,7 @@ FLAC__StreamEncoderWriteStatus write_frame_(FLAC__StreamEncoder *encoder, const /* 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)]; + FLAC__byte b[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH]; const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; const FLAC__uint64 samples = metadata->data.stream_info.total_samples; const unsigned min_framesize = metadata->data.stream_info.min_framesize; @@ -2660,7 +2629,7 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) b[3] = (FLAC__byte)xx; xx >>= 8; b[2] = (FLAC__byte)xx; xx >>= 8; b[1] = (FLAC__byte)xx; xx >>= 8; - b[0] = (FLAC__byte)xx; xx >>= 8; + b[0] = (FLAC__byte)xx; //xx >>= 8; xx = encoder->private_->seek_table->points[i].stream_offset; b[15] = (FLAC__byte)xx; xx >>= 8; b[14] = (FLAC__byte)xx; xx >>= 8; @@ -2669,10 +2638,10 @@ void update_metadata_(const FLAC__StreamEncoder *encoder) b[11] = (FLAC__byte)xx; xx >>= 8; b[10] = (FLAC__byte)xx; xx >>= 8; b[9] = (FLAC__byte)xx; xx >>= 8; - b[8] = (FLAC__byte)xx; xx >>= 8; + b[8] = (FLAC__byte)xx; //xx >>= 8; x = encoder->private_->seek_table->points[i].frame_samples; b[17] = (FLAC__byte)x; x >>= 8; - b[16] = (FLAC__byte)x; x >>= 8; + b[16] = (FLAC__byte)x; //x >>= 8; if(encoder->private_->write_callback(encoder, b, 18, 0, 0, encoder->private_->client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) { encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR; return; @@ -2694,7 +2663,7 @@ void update_ogg_metadata_(FLAC__StreamEncoder *encoder) FLAC__OGG_MAPPING_NUM_HEADERS_LENGTH + FLAC__STREAM_SYNC_LENGTH ; - FLAC__byte b[max(6, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; + FLAC__byte b[flac_max(6u, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)]; const FLAC__StreamMetadata *metadata = &encoder->private_->streaminfo; const FLAC__uint64 samples = metadata->data.stream_info.total_samples; const unsigned min_framesize = metadata->data.stream_info.min_framesize; @@ -2945,9 +2914,9 @@ FLAC__bool process_subframes_(FLAC__StreamEncoder *encoder, FLAC__bool is_fracti } else { max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize(encoder->protected_->blocksize); - max_partition_order = min(max_partition_order, encoder->protected_->max_residual_partition_order); + max_partition_order = flac_min(max_partition_order, encoder->protected_->max_residual_partition_order); } - min_partition_order = min(min_partition_order, max_partition_order); + min_partition_order = flac_min(min_partition_order, max_partition_order); /* * Setup the frame @@ -3351,8 +3320,8 @@ FLAC__bool process_subframe_( 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); + max_qlp_coeff_precision = flac_min(32 - subframe_bps - lpc_order, FLAC__MAX_QLP_COEFF_PRECISION); + max_qlp_coeff_precision = flac_max(max_qlp_coeff_precision, min_qlp_coeff_precision); } else max_qlp_coeff_precision = FLAC__MAX_QLP_COEFF_PRECISION; @@ -3596,7 +3565,7 @@ unsigned evaluate_lpc_subframe_( if(subframe_bps <= 16) { FLAC__ASSERT(order > 0); FLAC__ASSERT(order <= FLAC__MAX_LPC_ORDER); - qlp_coeff_precision = min(qlp_coeff_precision, 32 - subframe_bps - FLAC__bitmath_ilog2(order)); + qlp_coeff_precision = flac_min(qlp_coeff_precision, 32 - subframe_bps - FLAC__bitmath_ilog2(order)); } ret = FLAC__lpc_quantize_coefficients(lp_coeff, order, qlp_coeff_precision, qlp_coeff, &quantization); @@ -3700,7 +3669,7 @@ unsigned find_best_partition_order_( const unsigned blocksize = residual_samples + predictor_order; max_partition_order = FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(max_partition_order, blocksize, predictor_order); - min_partition_order = min(min_partition_order, max_partition_order); + min_partition_order = flac_min(min_partition_order, max_partition_order); precompute_partition_info_sums_(residual, abs_residual_partition_sums, residual_samples, predictor_order, min_partition_order, max_partition_order, bps); @@ -3754,7 +3723,7 @@ unsigned find_best_partition_order_( unsigned partition; /* save best parameters and raw_bits */ - FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(prc, max(6, best_partition_order)); + FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(prc, flac_max(6u, 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))); @@ -3906,7 +3875,7 @@ void precompute_partition_info_escapes_( for(i = 0; i < partitions; i++) { m = raw_bits_per_partition[from_partition]; from_partition++; - raw_bits_per_partition[to_partition] = max(m, raw_bits_per_partition[from_partition]); + raw_bits_per_partition[to_partition] = flac_max(m, raw_bits_per_partition[from_partition]); from_partition++; to_partition++; } @@ -3914,7 +3883,7 @@ void precompute_partition_info_escapes_( } #ifdef EXACT_RICE_BITS_CALCULATION -static FLaC__INLINE unsigned count_rice_bits_in_partition_( +static inline unsigned count_rice_bits_in_partition_( const unsigned rice_parameter, const unsigned partition_samples, const FLAC__int32 *residual @@ -3929,7 +3898,7 @@ static FLaC__INLINE unsigned count_rice_bits_in_partition_( return partition_bits; } #else -static FLaC__INLINE unsigned count_rice_bits_in_partition_( +static inline unsigned count_rice_bits_in_partition_( const unsigned rice_parameter, const unsigned partition_samples, const FLAC__uint64 abs_residual_partition_sum @@ -3984,7 +3953,7 @@ FLAC__bool set_partitioned_rice_( FLAC__ASSERT(suggested_rice_parameter < FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER); FLAC__ASSERT(rice_parameter_limit <= FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER); - FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, max(6, partition_order)); + FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(partitioned_rice_contents, flac_max(6u, partition_order)); parameters = partitioned_rice_contents->parameters; raw_bits = partitioned_rice_contents->raw_bits; @@ -4258,7 +4227,8 @@ void verify_error_callback_(const FLAC__StreamDecoder *decoder, FLAC__StreamDeco encoder->protected_->state = FLAC__STREAM_ENCODER_VERIFY_DECODER_ERROR; } -FLAC__StreamEncoderReadStatus file_read_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) +#if 0 +FLAC__StreamEncoderReadStatus file_read_callback_(const FLAC__StreamEncoder *encoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { (void)client_data; @@ -4272,19 +4242,19 @@ FLAC__StreamEncoderReadStatus file_read_callback_enc(const FLAC__StreamEncoder * return FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE; } -FLAC__StreamEncoderSeekStatus file_seek_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) +FLAC__StreamEncoderSeekStatus file_seek_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) { (void)client_data; - if(fseeko(encoder->private_->file, (off_t)absolute_byte_offset, SEEK_SET) < 0) + if(fseeko(encoder->private_->file, (FLAC__off_t)absolute_byte_offset, SEEK_SET) < 0) return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; else return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; } -FLAC__StreamEncoderTellStatus file_tell_callback_enc(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +FLAC__StreamEncoderTellStatus file_tell_callback_(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { - off_t offset; + FLAC__off_t offset; (void)client_data; @@ -4362,3 +4332,5 @@ FILE *get_binary_stdout_(void) return stdout; } + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c index 82d3b35..41efca5 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder_framing.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -39,11 +40,6 @@ #include "include/private/crc.h" #include "../assert.h" -#ifdef max -#undef max -#endif -#define max(x,y) ((x)>(y)?(x):(y)) - static FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method); static FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order, const FLAC__bool is_extended); @@ -166,11 +162,11 @@ FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__ if(!FLAC__bitwriter_write_raw_uint32(bw, track->num_indices, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) return false; for(j = 0; j < track->num_indices; j++) { - const FLAC__StreamMetadata_CueSheet_Index *index = track->indices + j; + const FLAC__StreamMetadata_CueSheet_Index *indx = track->indices + j; - if(!FLAC__bitwriter_write_raw_uint64(bw, index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) + if(!FLAC__bitwriter_write_raw_uint64(bw, indx->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) return false; - if(!FLAC__bitwriter_write_raw_uint32(bw, index->number, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) + if(!FLAC__bitwriter_write_raw_uint32(bw, indx->number, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) return false; if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) return false; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c index 95db783..c99d5b4 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/window_flac.c @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2006,2007 Josh Coalson + * Copyright (C) 2006-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h index 19c3b0f..18bf6d1 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/metadata.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2001-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -1986,7 +1987,7 @@ FLAC_API FLAC__bool FLAC__metadata_object_cuesheet_resize_tracks(FLAC__StreamMet * \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) + * (track->indices == NULL && track->num_indices == 0) \endcode * \retval FLAC__bool * \c false if \a copy is \c true and malloc() fails, else \c true. */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h index b3ea592..c9466c5 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/ordinals.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,40 +33,45 @@ #ifndef FLAC__ORDINALS_H #define FLAC__ORDINALS_H -#if !(defined(_MSC_VER) || defined(__BORLANDC__) || defined(__EMX__)) -#include -#endif +#if defined(_MSC_VER) && _MSC_VER < 1600 -typedef signed char FLAC__int8; -typedef unsigned char FLAC__uint8; +/* Microsoft Visual Studio earlier than the 2010 version did not provide + * the 1999 ISO C Standard header file . + */ + +typedef __int8 FLAC__int8; +typedef unsigned __int8 FLAC__uint8; -#if defined(_MSC_VER) || defined(__BORLANDC__) typedef __int16 FLAC__int16; typedef __int32 FLAC__int32; typedef __int64 FLAC__int64; typedef unsigned __int16 FLAC__uint16; typedef unsigned __int32 FLAC__uint32; typedef unsigned __int64 FLAC__uint64; -#elif defined(__EMX__) -typedef short FLAC__int16; -typedef long FLAC__int32; -typedef long long FLAC__int64; -typedef unsigned short FLAC__uint16; -typedef unsigned long FLAC__uint32; -typedef unsigned long long FLAC__uint64; + #else + +/* For MSVC 2010 and everything else which provides . */ + +#include + +typedef int8_t FLAC__int8; +typedef uint8_t FLAC__uint8; + typedef int16_t FLAC__int16; typedef int32_t FLAC__int32; typedef int64_t FLAC__int64; typedef uint16_t FLAC__uint16; typedef uint32_t FLAC__uint32; typedef uint64_t FLAC__uint64; + #endif typedef int FLAC__bool; typedef FLAC__uint8 FLAC__byte; + #ifdef true #undef true #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h index 5818fdd..be402c8 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_decoder.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h index 7dcf7ed..f0fcab5 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/stream_encoder.h @@ -1,5 +1,6 @@ /* libFLAC - Free Lossless Audio Codec library - * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + * Copyright (C) 2000-2009 Josh Coalson + * Copyright (C) 2011-2013 Xiph.Org Foundation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -128,7 +129,7 @@ extern "C" { * 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 + * 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_*() diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp index 636bc60..f5b0b9a 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp @@ -1,31 +1,38 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -//============================================================================== static const char* const aiffFormatName = "AIFF file"; -static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 }; + +//============================================================================== +const char* const AiffAudioFormat::appleOneShot = "apple one shot"; +const char* const AiffAudioFormat::appleRootSet = "apple root set"; +const char* const AiffAudioFormat::appleRootNote = "apple root note"; +const char* const AiffAudioFormat::appleBeats = "apple beats"; +const char* const AiffAudioFormat::appleDenominator = "apple denominator"; +const char* const AiffAudioFormat::appleNumerator = "apple numerator"; +const char* const AiffAudioFormat::appleTag = "apple tag"; +const char* const AiffAudioFormat::appleKey = "apple key"; //============================================================================== namespace AiffFileHelpers @@ -34,11 +41,6 @@ namespace AiffFileHelpers #if JUCE_MSVC #pragma pack (push, 1) - #define PACKED - #elif JUCE_GCC - #define PACKED __attribute__((packed)) - #else - #define PACKED #endif //============================================================================== @@ -49,7 +51,7 @@ namespace AiffFileHelpers uint16 type; // these are different in AIFF and WAV uint16 startIdentifier; uint16 endIdentifier; - } PACKED; + } JUCE_PACKED; int8 baseNote; int8 detune; @@ -82,37 +84,157 @@ namespace AiffFileHelpers values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier))); } + static uint16 getValue16 (const StringPairArray& values, const char* name, const char* def) + { + return ByteOrder::swapIfLittleEndian ((uint16) values.getValue (name, def).getIntValue()); + } + + static int8 getValue8 (const StringPairArray& values, const char* name, const char* def) + { + return (int8) values.getValue (name, def).getIntValue(); + } + static void create (MemoryBlock& block, const StringPairArray& values) { if (values.getAllKeys().contains ("MidiUnityNote", true)) { block.setSize ((sizeof (InstChunk) + 3) & ~(size_t) 3, true); - InstChunk* const inst = static_cast (block.getData()); + InstChunk& inst = *static_cast (block.getData()); - inst->baseNote = (int8) values.getValue ("MidiUnityNote", "60").getIntValue(); - inst->detune = (int8) values.getValue ("Detune", "0").getIntValue(); - inst->lowNote = (int8) values.getValue ("LowNote", "0").getIntValue(); - inst->highNote = (int8) values.getValue ("HighNote", "127").getIntValue(); - inst->lowVelocity = (int8) values.getValue ("LowVelocity", "1").getIntValue(); - inst->highVelocity = (int8) values.getValue ("HighVelocity", "127").getIntValue(); - inst->gain = (int16) ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Gain", "0").getIntValue()); + inst.baseNote = getValue8 (values, "MidiUnityNote", "60"); + inst.detune = getValue8 (values, "Detune", "0"); + inst.lowNote = getValue8 (values, "LowNote", "0"); + inst.highNote = getValue8 (values, "HighNote", "127"); + inst.lowVelocity = getValue8 (values, "LowVelocity", "1"); + inst.highVelocity = getValue8 (values, "HighVelocity", "127"); + inst.gain = (int16) getValue16 (values, "Gain", "0"); - inst->sustainLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0Type", "0").getIntValue()); - inst->sustainLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0StartIdentifier", "0").getIntValue()); - inst->sustainLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop0EndIdentifier", "0").getIntValue()); - inst->releaseLoop.type = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1Type", "0").getIntValue()); - inst->releaseLoop.startIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1StartIdentifier", "0").getIntValue()); - inst->releaseLoop.endIdentifier = ByteOrder::swapIfLittleEndian ((uint16) values.getValue ("Loop1EndIdentifier", "0").getIntValue()); + inst.sustainLoop.type = getValue16 (values, "Loop0Type", "0"); + inst.sustainLoop.startIdentifier = getValue16 (values, "Loop0StartIdentifier", "0"); + inst.sustainLoop.endIdentifier = getValue16 (values, "Loop0EndIdentifier", "0"); + inst.releaseLoop.type = getValue16 (values, "Loop1Type", "0"); + inst.releaseLoop.startIdentifier = getValue16 (values, "Loop1StartIdentifier", "0"); + inst.releaseLoop.endIdentifier = getValue16 (values, "Loop1EndIdentifier", "0"); } } - } PACKED; + } JUCE_PACKED; + + //============================================================================== + struct BASCChunk + { + enum Key + { + minor = 1, + major = 2, + neither = 3, + both = 4 + }; + + BASCChunk (InputStream& input) + { + zerostruct (*this); + + flags = (uint32) input.readIntBigEndian(); + numBeats = (uint32) input.readIntBigEndian(); + rootNote = (uint16) input.readShortBigEndian(); + key = (uint16) input.readShortBigEndian(); + timeSigNum = (uint16) input.readShortBigEndian(); + timeSigDen = (uint16) input.readShortBigEndian(); + oneShot = (uint16) input.readShortBigEndian(); + input.read (unknown, sizeof (unknown)); + } + + void addToMetadata (StringPairArray& metadata) const + { + const bool rootNoteSet = rootNote != 0; + + setBoolFlag (metadata, AiffAudioFormat::appleOneShot, oneShot == 2); + setBoolFlag (metadata, AiffAudioFormat::appleRootSet, rootNoteSet); + + if (rootNoteSet) + metadata.set (AiffAudioFormat::appleRootNote, String (rootNote)); + + metadata.set (AiffAudioFormat::appleBeats, String (numBeats)); + metadata.set (AiffAudioFormat::appleDenominator, String (timeSigDen)); + metadata.set (AiffAudioFormat::appleNumerator, String (timeSigNum)); + + const char* keyString = nullptr; + + switch (key) + { + case minor: keyString = "major"; break; + case major: keyString = "major"; break; + case neither: keyString = "neither"; break; + case both: keyString = "both"; break; + } + + if (keyString != nullptr) + metadata.set (AiffAudioFormat::appleKey, keyString); + } + + void setBoolFlag (StringPairArray& values, const char* name, bool shouldBeSet) const + { + values.set (name, shouldBeSet ? "1" : "0"); + } + + uint32 flags; + uint32 numBeats; + uint16 rootNote; + uint16 key; + uint16 timeSigNum; + uint16 timeSigDen; + uint16 oneShot; + uint8 unknown[66]; + } JUCE_PACKED; #if JUCE_MSVC #pragma pack (pop) #endif - #undef PACKED + //============================================================================== + static String readCATEChunk (InputStream& input, const uint32 length) + { + MemoryBlock mb; + input.skipNextBytes (4); + input.readIntoMemoryBlock (mb, (ssize_t) length - 4); + + static const char* appleGenres[] = + { + "Rock/Blues", + "Electronic/Dance", + "Jazz", + "Urban", + "World/Ethnic", + "Cinematic/New Age", + "Orchestral", + "Country/Folk", + "Experimental", + "Other Genre", + nullptr + }; + + const StringArray genres (appleGenres); + StringArray tagsArray; + + int bytesLeft = (int) mb.getSize(); + const char* data = static_cast (mb.getData()); + + while (bytesLeft > 0) + { + const String tag (CharPointer_UTF8 (data), + CharPointer_UTF8 (data + bytesLeft)); + + if (tag.isNotEmpty()) + tagsArray.add (data); + + const int numBytesInTag = genres.contains (tag) ? 118 : 50; + data += numBytesInTag; + bytesLeft -= numBytesInTag; + } + + return tagsArray.joinIntoString (";"); + } //============================================================================== namespace MarkChunk @@ -190,7 +312,7 @@ namespace AiffFileHelpers out.writeShortBigEndian ((short) identifier); out.writeIntBigEndian (offset); - const int labelLength = jmin (254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring + const size_t labelLength = jmin ((size_t) 254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring out.writeByte ((char) labelLength + 1); out.write (label.toUTF8(), labelLength); out.writeByte (0); @@ -221,8 +343,11 @@ namespace AiffFileHelpers out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue()); out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue()); - const String comment (values.getValue (prefix + "Text", String::empty)); - out.write (comment.toUTF8(), jmin (comment.getNumBytesAsUTF8(), 65534)); + const String comment (values.getValue (prefix + "Text", String())); + + const size_t commentLength = jmin (comment.getNumBytesAsUTF8(), (size_t) 65534); + out.writeShortBigEndian ((short) commentLength + 1); + out.write (comment.toUTF8(), commentLength); out.writeByte (0); if ((out.getDataSize() & 1) != 0) @@ -238,7 +363,7 @@ class AiffAudioFormatReader : public AudioFormatReader { public: AiffAudioFormatReader (InputStream* in) - : AudioFormatReader (in, TRANS (aiffFormatName)) + : AudioFormatReader (in, aiffFormatName) { using namespace AiffFileHelpers; @@ -308,6 +433,11 @@ public: { littleEndian = true; } + else if (compType == chunkName ("fl32") || compType == chunkName ("FL32")) + { + littleEndian = false; + usesFloatingPointData = true; + } else { sampleRate = 0; @@ -381,6 +511,15 @@ public: input->read (inst, (int) length); inst->copyTo (metadataValues); } + else if (type == chunkName ("basc")) + { + AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValues); + } + else if (type == chunkName ("cate")) + { + metadataValues.set (AiffAudioFormat::appleTag, + AiffFileHelpers::readCATEChunk (*input, length));; + } else if ((hasGotVer && hasGotData && hasGotType) || chunkEnd < input->getPosition() || input->isExhausted()) @@ -399,18 +538,10 @@ public: //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples) + int64 startSampleInFile, int numSamples) override { - const int64 samplesAvailable = lengthInSamples - startSampleInFile; - - if (samplesAvailable < numSamples) - { - for (int i = numDestChannels; --i >= 0;) - if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); - - numSamples = (int) samplesAvailable; - } + clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, lengthInSamples); if (numSamples <= 0) return true; @@ -431,30 +562,14 @@ public: zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead)); } - jassert (! usesFloatingPointData); // (would need to add support for this if it's possible) - if (littleEndian) - { - switch (bitsPerSample) - { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 32: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - default: jassertfalse; break; - } - } + copySampleData (bitsPerSample, usesFloatingPointData, + destSamples, startOffsetInDestBuffer, numDestChannels, + tempBuffer, (int) numChannels, numThisTime); else - { - switch (bitsPerSample) - { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 32: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - default: jassertfalse; break; - } - } + copySampleData (bitsPerSample, usesFloatingPointData, + destSamples, startOffsetInDestBuffer, numDestChannels, + tempBuffer, (int) numChannels, numThisTime); startOffsetInDestBuffer += numThisTime; numSamples -= numThisTime; @@ -463,22 +578,38 @@ public: return true; } + template + static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData, + int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels, + const void* sourceData, int numChannels, int numSamples) noexcept + { + switch (bitsPerSample) + { + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + case 32: if (usesFloatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); + else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + default: jassertfalse; break; + } + } + int bytesPerFrame; int64 dataChunkStart; bool littleEndian; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader) }; //============================================================================== class AiffAudioFormatWriter : public AudioFormatWriter { public: - AiffAudioFormatWriter (OutputStream* out, double sampleRate_, + AiffAudioFormatWriter (OutputStream* out, double rate, unsigned int numChans, unsigned int bits, const StringPairArray& metadataValues) - : AudioFormatWriter (out, TRANS (aiffFormatName), sampleRate_, numChans, bits), + : AudioFormatWriter (out, aiffFormatName, rate, numChans, bits), lengthInSamples (0), bytesWritten (0), writeFailed (false) @@ -510,7 +641,7 @@ public: } //============================================================================== - bool write (const int** data, int numSamples) + bool write (const int** data, int numSamples) override { jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel! @@ -530,7 +661,7 @@ public: } if (bytesWritten + bytes >= (size_t) 0xfff00000 - || ! output->write (tempBlock.getData(), (int) bytes)) + || ! output->write (tempBlock.getData(), bytes)) { // failed to write to disk, so let's try writing the header. // If it's just run out of disk space, then if it does manage @@ -652,12 +783,101 @@ private: jassert (output->getPosition() == headerLen); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter) }; //============================================================================== -AiffAudioFormat::AiffAudioFormat() - : AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions)) +class MemoryMappedAiffReader : public MemoryMappedAudioFormatReader +{ +public: + MemoryMappedAiffReader (const File& f, const AiffAudioFormatReader& reader) + : MemoryMappedAudioFormatReader (f, reader, reader.dataChunkStart, + reader.bytesPerFrame * reader.lengthInSamples, reader.bytesPerFrame), + littleEndian (reader.littleEndian) + { + } + + bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) override + { + clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, lengthInSamples); + + if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) + { + jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. + return false; + } + + if (littleEndian) + AiffAudioFormatReader::copySampleData + (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, + numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples); + else + AiffAudioFormatReader::copySampleData + (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, + numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples); + + return true; + } + + void readMaxLevels (int64 startSampleInFile, int64 numSamples, + float& min0, float& max0, float& min1, float& max1) + { + if (numSamples <= 0) + { + min0 = max0 = min1 = max1 = 0; + return; + } + + if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) + { + jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. + + min0 = max0 = min1 = max1 = 0; + return; + } + + switch (bitsPerSample) + { + case 8: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 16: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 24: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); + else scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + default: jassertfalse; break; + } + } + +private: + const bool littleEndian; + + template + void scanMinAndMax (int64 startSampleInFile, int64 numSamples, + float& min0, float& max0, float& min1, float& max1) const noexcept + { + scanMinAndMax2 (0, startSampleInFile, numSamples, min0, max0); + + if (numChannels > 1) + scanMinAndMax2 (1, startSampleInFile, numSamples, min1, max1); + else + min1 = max1 = 0; + } + + template + void scanMinAndMax2 (int channel, int64 startSampleInFile, int64 numSamples, float& mn, float& mx) const noexcept + { + if (littleEndian) + scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples, mn, mx); + else + scanMinAndMaxInterleaved (channel, startSampleInFile, numSamples, mn, mx); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAiffReader) +}; + +//============================================================================== +AiffAudioFormat::AiffAudioFormat() : AudioFormat (aiffFormatName, ".aiff .aif") { } @@ -668,13 +888,13 @@ AiffAudioFormat::~AiffAudioFormat() Array AiffAudioFormat::getPossibleSampleRates() { const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; - return Array (rates); + return Array (rates); } Array AiffAudioFormat::getPossibleBitDepths() { const int depths[] = { 8, 16, 24, 0 }; - return Array (depths); + return Array (depths); } bool AiffAudioFormat::canDoStereo() { return true; } @@ -687,8 +907,10 @@ bool AiffAudioFormat::canHandleFile (const File& f) return true; const OSType type = f.getMacOSType(); - return type == 'AIFF' || type == 'AIFC' - || type == 'aiff' || type == 'aifc'; + + // (NB: written as hex to avoid four-char-constant warnings) + return type == 0x41494646 /* AIFF */ || type == 0x41494643 /* AIFC */ + || type == 0x61696666 /* aiff */ || type == 0x61696663 /* aifc */; } #endif @@ -696,7 +918,7 @@ AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, { ScopedPointer w (new AiffAudioFormatReader (sourceStream)); - if (w->sampleRate > 0) + if (w->sampleRate > 0 && w->numChannels > 0) return w.release(); if (! deleteStreamIfOpeningFails) @@ -705,6 +927,19 @@ AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, return nullptr; } +MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (const File& file) +{ + if (FileInputStream* fin = file.createInputStream()) + { + AiffAudioFormatReader reader (fin); + + if (reader.lengthInSamples > 0) + return new MemoryMappedAiffReader (file, reader); + } + + return nullptr; +} + AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, double sampleRate, unsigned int numberOfChannels, diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h index cf317e3..01d1484 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.h @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -40,27 +39,46 @@ public: ~AiffAudioFormat(); //============================================================================== - Array getPossibleSampleRates(); - Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleOneShot; + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleRootSet; + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleRootNote; + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleBeats; + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleDenominator; + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleNumerator; + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleTag; + /** Metadata property name used when reading a aiff file with a basc chunk. */ + static const char* const appleKey; + + //============================================================================== + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; #if JUCE_MAC - bool canHandleFile (const File& fileToTest); + bool canHandleFile (const File& fileToTest) override; #endif //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, - bool deleteStreamIfOpeningFails); + bool deleteStreamIfOpeningFails) override; + + MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, - int qualityOptionIndex); - + int qualityOptionIndex) override; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AiffAudioFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AiffAudioFormat) }; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp index e5e3826..6d3aafa 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -33,8 +32,8 @@ namespace StringArray findFileExtensionsForCoreAudioCodecs() { StringArray extensionsArray; - CFMutableArrayRef extensions = CFArrayCreateMutable (0, 0, 0); - UInt32 sizeOfArray = sizeof (CFMutableArrayRef); + CFArrayRef extensions = nullptr; + UInt32 sizeOfArray = sizeof (extensions); if (AudioFileGetGlobalInfo (kAudioFileGlobalInfo_AllExtensions, 0, 0, &sizeOfArray, &extensions) == noErr) { @@ -42,28 +41,306 @@ namespace for (CFIndex i = 0; i < numValues; ++i) extensionsArray.add ("." + String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (extensions, i))); + + CFRelease (extensions); } - CFRelease (extensions); return extensionsArray; } } +//============================================================================== +const char* const CoreAudioFormat::midiDataBase64 = "midiDataBase64"; +const char* const CoreAudioFormat::tempo = "tempo"; +const char* const CoreAudioFormat::timeSig = "time signature"; +const char* const CoreAudioFormat::keySig = "key signature"; + +//============================================================================== +struct CoreAudioFormatMetatdata +{ + static uint32 chunkName (const char* const name) noexcept { return ByteOrder::bigEndianInt (name); } + + //============================================================================== + struct FileHeader + { + FileHeader (InputStream& input) + { + fileType = (uint32) input.readIntBigEndian(); + fileVersion = (uint16) input.readShortBigEndian(); + fileFlags = (uint16) input.readShortBigEndian(); + } + + uint32 fileType; + uint16 fileVersion; + uint16 fileFlags; + }; + + //============================================================================== + struct ChunkHeader + { + ChunkHeader (InputStream& input) + { + chunkType = (uint32) input.readIntBigEndian(); + chunkSize = (int64) input.readInt64BigEndian(); + } + + uint32 chunkType; + int64 chunkSize; + }; + + //============================================================================== + struct AudioDescriptionChunk + { + AudioDescriptionChunk (InputStream& input) + { + sampleRate = input.readDoubleBigEndian(); + formatID = (uint32) input.readIntBigEndian(); + formatFlags = (uint32) input.readIntBigEndian(); + bytesPerPacket = (uint32) input.readIntBigEndian(); + framesPerPacket = (uint32) input.readIntBigEndian(); + channelsPerFrame = (uint32) input.readIntBigEndian(); + bitsPerChannel = (uint32) input.readIntBigEndian(); + } + + double sampleRate; + uint32 formatID; + uint32 formatFlags; + uint32 bytesPerPacket; + uint32 framesPerPacket; + uint32 channelsPerFrame; + uint32 bitsPerChannel; + }; + + //============================================================================== + struct UserDefinedChunk + { + UserDefinedChunk (InputStream& input, int64 size) + { + // a user defined chunk contains 16 bytes of a UUID first + uuid[1] = input.readInt64BigEndian(); + uuid[0] = input.readInt64BigEndian(); + + input.skipNextBytes (size - 16); + } + + int64 uuid[2]; + }; + + //============================================================================== + static StringPairArray parseMidiChunk (InputStream& input, int64 size) + { + const int64 originalPosition = input.getPosition(); + + MemoryBlock midiBlock; + input.readIntoMemoryBlock (midiBlock, (ssize_t) size); + MemoryInputStream midiInputStream (midiBlock, false); + + StringPairArray midiMetadata; + MidiFile midiFile; + + if (midiFile.readFrom (midiInputStream)) + { + midiMetadata.set (CoreAudioFormat::midiDataBase64, midiBlock.toBase64Encoding()); + + findTempoEvents (midiFile, midiMetadata); + findTimeSigEvents (midiFile, midiMetadata); + findKeySigEvents (midiFile, midiMetadata); + } + + input.setPosition (originalPosition + size); + return midiMetadata; + } + + static void findTempoEvents (MidiFile& midiFile, StringPairArray& midiMetadata) + { + MidiMessageSequence tempoEvents; + midiFile.findAllTempoEvents (tempoEvents); + + const int numTempoEvents = tempoEvents.getNumEvents(); + MemoryOutputStream tempoSequence; + + for (int i = 0; i < numTempoEvents; ++i) + { + const double tempo = getTempoFromTempoMetaEvent (tempoEvents.getEventPointer (i)); + + if (tempo > 0.0) + { + if (i == 0) + midiMetadata.set (CoreAudioFormat::tempo, String (tempo)); + + if (numTempoEvents > 1) + tempoSequence << String (tempo) << ',' << tempoEvents.getEventTime (i) << ';'; + } + } + + if (tempoSequence.getDataSize() > 0) + midiMetadata.set ("tempo sequence", tempoSequence.toUTF8()); + } + + static double getTempoFromTempoMetaEvent (MidiMessageSequence::MidiEventHolder* holder) + { + if (holder != nullptr) + { + const MidiMessage& midiMessage = holder->message; + + if (midiMessage.isTempoMetaEvent()) + { + const double tempoSecondsPerQuarterNote = midiMessage.getTempoSecondsPerQuarterNote(); + + if (tempoSecondsPerQuarterNote > 0.0) + return 60.0 / tempoSecondsPerQuarterNote; + } + } + + return 0.0; + } + + static void findTimeSigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) + { + MidiMessageSequence timeSigEvents; + midiFile.findAllTimeSigEvents (timeSigEvents); + const int numTimeSigEvents = timeSigEvents.getNumEvents(); + + MemoryOutputStream timeSigSequence; + + for (int i = 0; i < numTimeSigEvents; ++i) + { + int numerator, denominator; + timeSigEvents.getEventPointer(i)->message.getTimeSignatureInfo (numerator, denominator); + + String timeSigString; + timeSigString << numerator << '/' << denominator; + + if (i == 0) + midiMetadata.set (CoreAudioFormat::timeSig, timeSigString); + + if (numTimeSigEvents > 1) + timeSigSequence << timeSigString << ',' << timeSigEvents.getEventTime (i) << ';'; + } + + if (timeSigSequence.getDataSize() > 0) + midiMetadata.set ("time signature sequence", timeSigSequence.toUTF8()); + } + + static void findKeySigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) + { + MidiMessageSequence keySigEvents; + midiFile.findAllKeySigEvents (keySigEvents); + const int numKeySigEvents = keySigEvents.getNumEvents(); + + MemoryOutputStream keySigSequence; + + for (int i = 0; i < numKeySigEvents; ++i) + { + const MidiMessage& message (keySigEvents.getEventPointer (i)->message); + const int key = jlimit (0, 14, message.getKeySignatureNumberOfSharpsOrFlats() + 7); + const bool isMajor = message.isKeySignatureMajorKey(); + + static const char* majorKeys[] = { "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#" }; + static const char* minorKeys[] = { "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#" }; + + String keySigString (isMajor ? majorKeys[key] + : minorKeys[key]); + + if (! isMajor) + keySigString << 'm'; + + if (i == 0) + midiMetadata.set (CoreAudioFormat::keySig, keySigString); + + if (numKeySigEvents > 1) + keySigSequence << keySigString << ',' << keySigEvents.getEventTime (i) << ';'; + } + + if (keySigSequence.getDataSize() > 0) + midiMetadata.set ("key signature sequence", keySigSequence.toUTF8()); + } + + //============================================================================== + static StringPairArray parseInformationChunk (InputStream& input) + { + StringPairArray infoStrings; + const uint32 numEntries = (uint32) input.readIntBigEndian(); + + for (uint32 i = 0; i < numEntries; ++i) + infoStrings.set (input.readString(), input.readString()); + + return infoStrings; + } + + //============================================================================== + static bool read (InputStream& input, StringPairArray& metadataValues) + { + const int64 originalPos = input.getPosition(); + + const FileHeader cafFileHeader (input); + const bool isCafFile = cafFileHeader.fileType == chunkName ("caff"); + + if (isCafFile) + { + while (! input.isExhausted()) + { + const ChunkHeader chunkHeader (input); + + if (chunkHeader.chunkType == chunkName ("desc")) + { + AudioDescriptionChunk audioDescriptionChunk (input); + } + else if (chunkHeader.chunkType == chunkName ("uuid")) + { + UserDefinedChunk userDefinedChunk (input, chunkHeader.chunkSize); + } + else if (chunkHeader.chunkType == chunkName ("data")) + { + // -1 signifies an unknown data size so the data has to be at the + // end of the file so we must have finished the header + + if (chunkHeader.chunkSize == -1) + break; + + input.skipNextBytes (chunkHeader.chunkSize); + } + else if (chunkHeader.chunkType == chunkName ("midi")) + { + metadataValues.addArray (parseMidiChunk (input, chunkHeader.chunkSize)); + } + else if (chunkHeader.chunkType == chunkName ("info")) + { + metadataValues.addArray (parseInformationChunk (input)); + } + else + { + // we aren't decoding this chunk yet so just skip over it + input.skipNextBytes (chunkHeader.chunkSize); + } + } + } + + input.setPosition (originalPos); + + return isCafFile; + } +}; + //============================================================================== class CoreAudioReader : public AudioFormatReader { public: CoreAudioReader (InputStream* const inp) - : AudioFormatReader (inp, TRANS (coreAudioFormatName)), + : AudioFormatReader (inp, coreAudioFormatName), ok (false), lastReadPosition (0) { usesFloatingPointData = true; + bitsPerSample = 32; + + if (input != nullptr) + CoreAudioFormatMetatdata::read (*input, metadataValues); OSStatus status = AudioFileOpenWithCallbacks (this, &readCallback, - 0, // write needs to be null to avoid permisisions errors + nullptr, // write needs to be null to avoid permisisions errors &getSizeCallback, - 0, // setSize needs to be null to avoid permisisions errors + nullptr, // setSize needs to be null to avoid permisisions errors 0, // AudioFileTypeID inFileTypeHint &audioFileID); if (status == noErr) @@ -79,9 +356,8 @@ public: &audioStreamBasicDescriptionSize, &sourceAudioFormat); - numChannels = sourceAudioFormat.mChannelsPerFrame; - sampleRate = sourceAudioFormat.mSampleRate; - bitsPerSample = sourceAudioFormat.mBitsPerChannel; + numChannels = sourceAudioFormat.mChannelsPerFrame; + sampleRate = sourceAudioFormat.mSampleRate; UInt32 sizeOfLengthProperty = sizeof (int64); ExtAudioFileGetProperty (audioFileRef, @@ -120,19 +396,10 @@ public: //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples) + int64 startSampleInFile, int numSamples) override { - jassert (destSamples != nullptr); - const int64 samplesAvailable = lengthInSamples - startSampleInFile; - - if (samplesAvailable < numSamples) - { - for (int i = numDestChannels; --i >= 0;) - if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); - - numSamples = (int) samplesAvailable; - } + clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, lengthInSamples); if (numSamples <= 0) return true; @@ -215,12 +482,12 @@ private: return noErr; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioReader) }; //============================================================================== CoreAudioFormat::CoreAudioFormat() - : AudioFormat (TRANS (coreAudioFormatName), findFileExtensionsForCoreAudioCodecs()) + : AudioFormat (coreAudioFormatName, findFileExtensionsForCoreAudioCodecs()) { } @@ -247,12 +514,12 @@ AudioFormatReader* CoreAudioFormat::createReaderFor (InputStream* sourceStream, return nullptr; } -AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream* streamToWriteTo, - double sampleRateToUse, - unsigned int numberOfChannels, - int bitsPerSample, - const StringPairArray& metadataValues, - int qualityOptionIndex) +AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*, + double /*sampleRateToUse*/, + unsigned int /*numberOfChannels*/, + int /*bitsPerSample*/, + const StringPairArray& /*metadataValues*/, + int /*qualityOptionIndex*/) { jassertfalse; // not yet implemented! return nullptr; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h index dc6b736..60bace4 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_CoreAudioFormat.h @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if JUCE_MAC || JUCE_IOS +#if JUCE_MAC || JUCE_IOS || DOXYGEN //============================================================================== /** @@ -45,24 +44,34 @@ public: ~CoreAudioFormat(); //============================================================================== - Array getPossibleSampleRates(); - Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); + /** Metadata property name used when reading a caf file with a MIDI chunk. */ + static const char* const midiDataBase64; + /** Metadata property name used when reading a caf file with tempo information. */ + static const char* const tempo; + /** Metadata property name used when reading a caf file time signature information. */ + static const char* const timeSig; + /** Metadata property name used when reading a caf file time signature information. */ + static const char* const keySig; //============================================================================== - AudioFormatReader* createReaderFor (InputStream* sourceStream, - bool deleteStreamIfOpeningFails); + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; - AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, + //============================================================================== + AudioFormatReader* createReaderFor (InputStream*, + bool deleteStreamIfOpeningFails) override; + + AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, - int qualityOptionIndex); + int qualityOptionIndex) override; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioFormat) }; #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp index dbf0e50..58fdd8f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -46,7 +45,27 @@ namespace FlacNamespace #define SIZE_MAX 0xffffffff #endif + #if JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wconversion" + #pragma clang diagnostic ignored "-Wshadow" + #pragma clang diagnostic ignored "-Wdeprecated-register" + #endif + + #if JUCE_INTEL + #if JUCE_32BIT + #define FLAC__CPU_IA32 1 + #endif + #if JUCE_64BIT + #define FLAC__CPU_X86_64 1 + #endif + #define FLAC__HAS_X86INTRIN 1 + #endif + + #undef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 + #define flac_max jmax + #define flac_min jmin #include "flac/all.h" #include "flac/libFLAC/bitmath.c" #include "flac/libFLAC/bitreader.c" @@ -67,6 +86,10 @@ namespace FlacNamespace #else #include #endif + + #if JUCE_CLANG + #pragma clang diagnostic pop + #endif } #undef max @@ -74,17 +97,14 @@ namespace FlacNamespace //============================================================================== static const char* const flacFormatName = "FLAC file"; -static const char* const flacExtensions[] = { ".flac", 0 }; //============================================================================== class FlacReader : public AudioFormatReader { public: - //============================================================================== FlacReader (InputStream* const in) - : AudioFormatReader (in, TRANS (flacFormatName)), - reservoir (2, 0), + : AudioFormatReader (in, flacFormatName), reservoirStart (0), samplesInReservoir (0), scanningForLength (false) @@ -136,7 +156,7 @@ public: // returns the number of samples read bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples) + int64 startSampleInFile, int numSamples) override { using namespace FlacNamespace; @@ -156,7 +176,7 @@ public: for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, - reservoir.getSampleData (i, (int) (startSampleInFile - reservoirStart)), + reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), sizeof (int) * (size_t) num); startOffsetInDestBuffer += num; @@ -223,7 +243,7 @@ public: if (src != nullptr) { - int* const dest = reinterpret_cast (reservoir.getSampleData(i)); + int* const dest = reinterpret_cast (reservoir.getWritePointer(i)); for (int j = 0; j < numSamples; ++j) dest[j] = src[j] << bitsToShift; @@ -238,34 +258,34 @@ public: static FlacNamespace::FLAC__StreamDecoderReadStatus readCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__byte buffer[], size_t* bytes, void* client_data) { using namespace FlacNamespace; - *bytes = (size_t) static_cast (client_data)->input->read (buffer, (int) *bytes); + *bytes = (size_t) static_cast (client_data)->input->read (buffer, (int) *bytes); return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } static FlacNamespace::FLAC__StreamDecoderSeekStatus seekCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64 absolute_byte_offset, void* client_data) { using namespace FlacNamespace; - static_cast (client_data)->input->setPosition ((int) absolute_byte_offset); + static_cast (client_data)->input->setPosition ((int) absolute_byte_offset); return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } static FlacNamespace::FLAC__StreamDecoderTellStatus tellCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data) { using namespace FlacNamespace; - *absolute_byte_offset = (uint64) static_cast (client_data)->input->getPosition(); + *absolute_byte_offset = (uint64) static_cast (client_data)->input->getPosition(); return FLAC__STREAM_DECODER_TELL_STATUS_OK; } static FlacNamespace::FLAC__StreamDecoderLengthStatus lengthCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* stream_length, void* client_data) { using namespace FlacNamespace; - *stream_length = (uint64) static_cast (client_data)->input->getTotalLength(); + *stream_length = (uint64) static_cast (client_data)->input->getTotalLength(); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } static FlacNamespace::FLAC__bool eofCallback_ (const FlacNamespace::FLAC__StreamDecoder*, void* client_data) { - return static_cast (client_data)->input->isExhausted(); + return static_cast (client_data)->input->isExhausted(); } static FlacNamespace::FLAC__StreamDecoderWriteStatus writeCallback_ (const FlacNamespace::FLAC__StreamDecoder*, @@ -274,7 +294,7 @@ public: void* client_data) { using namespace FlacNamespace; - static_cast (client_data)->useSamples (buffer, (int) frame->header.blocksize); + static_cast (client_data)->useSamples (buffer, (int) frame->header.blocksize); return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } @@ -282,7 +302,7 @@ public: const FlacNamespace::FLAC__StreamMetadata* metadata, void* client_data) { - static_cast (client_data)->useMetadata (metadata->data.stream_info); + static_cast (client_data)->useMetadata (metadata->data.stream_info); } static void errorCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__StreamDecoderErrorStatus, void*) @@ -295,7 +315,7 @@ private: int reservoirStart, samplesInReservoir; bool ok, scanningForLength; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader) }; @@ -303,11 +323,8 @@ private: class FlacWriter : public AudioFormatWriter { public: - //============================================================================== - FlacWriter (OutputStream* const out, double sampleRate_, - uint32 numChannels_, uint32 bitsPerSample_, int qualityOptionIndex) - : AudioFormatWriter (out, TRANS (flacFormatName), - sampleRate_, numChannels_, bitsPerSample_) + FlacWriter (OutputStream* const out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex) + : AudioFormatWriter (out, flacFormatName, rate, numChans, bits) { using namespace FlacNamespace; encoder = FLAC__stream_encoder_new(); @@ -346,7 +363,7 @@ public: } //============================================================================== - bool write (const int** samplesToWrite, int numSamples) + bool write (const int** samplesToWrite, int numSamples) override { using namespace FlacNamespace; if (! ok) @@ -373,15 +390,15 @@ public: destData[j] = (samplesToWrite[i][j] >> bitsToShift); } - samplesToWrite = const_cast (channels.getData()); + samplesToWrite = const_cast (channels.getData()); } - return FLAC__stream_encoder_process (encoder, (const FLAC__int32**) samplesToWrite, (size_t) numSamples) != 0; + return FLAC__stream_encoder_process (encoder, (const FLAC__int32**) samplesToWrite, (unsigned) numSamples) != 0; } bool writeData (const void* const data, const int size) const { - return output->write (data, size); + return output->write (data, (size_t) size); } static void packUint32 (FlacNamespace::FLAC__uint32 val, FlacNamespace::FLAC__byte* b, const int bytes) @@ -435,7 +452,7 @@ public: void* client_data) { using namespace FlacNamespace; - return static_cast (client_data)->writeData (buffer, (int) bytes) + return static_cast (client_data)->writeData (buffer, (int) bytes) ? FLAC__STREAM_ENCODER_WRITE_STATUS_OK : FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; } @@ -452,13 +469,13 @@ public: if (client_data == nullptr) return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; - *absolute_byte_offset = (FLAC__uint64) static_cast (client_data)->output->getPosition(); + *absolute_byte_offset = (FLAC__uint64) static_cast (client_data)->output->getPosition(); return FLAC__STREAM_ENCODER_TELL_STATUS_OK; } static void encodeMetadataCallback (const FlacNamespace::FLAC__StreamEncoder*, const FlacNamespace::FLAC__StreamMetadata* metadata, void* client_data) { - static_cast (client_data)->writeMetaData (metadata); + static_cast (client_data)->writeMetaData (metadata); } bool ok; @@ -466,13 +483,13 @@ public: private: FlacNamespace::FLAC__StreamEncoder* encoder; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacWriter); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacWriter) }; //============================================================================== FlacAudioFormat::FlacAudioFormat() - : AudioFormat (TRANS (flacFormatName), StringArray (flacExtensions)) + : AudioFormat (flacFormatName, ".flac") { } @@ -482,14 +499,17 @@ FlacAudioFormat::~FlacAudioFormat() Array FlacAudioFormat::getPossibleSampleRates() { - const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000, 0 }; - return Array (rates); + const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000, + 88200, 96000, 176400, 192000, 352800, 384000 }; + + return Array (rates, numElementsInArray (rates)); } Array FlacAudioFormat::getPossibleBitDepths() { - const int depths[] = { 16, 24, 0 }; - return Array (depths); + const int depths[] = { 16, 24 }; + + return Array (depths, numElementsInArray (depths)); } bool FlacAudioFormat::canDoStereo() { return true; } @@ -529,7 +549,7 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out, StringArray FlacAudioFormat::getQualityOptions() { - const char* options[] = { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)", 0 }; + static const char* options[] = { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)", 0 }; return StringArray (options); } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h index 6535db1..20bf094 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_FlacAudioFormat.h @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -41,25 +40,25 @@ public: ~FlacAudioFormat(); //============================================================================== - Array getPossibleSampleRates(); - Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); - bool isCompressed(); - StringArray getQualityOptions(); + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; + bool isCompressed() override; + StringArray getQualityOptions() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, - bool deleteStreamIfOpeningFails); + bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, - int qualityOptionIndex); + int qualityOptionIndex) override; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacAudioFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacAudioFormat) }; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp new file mode 100644 index 0000000..5d30d44 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp @@ -0,0 +1,222 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_USE_LAME_AUDIO_FORMAT + +class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter +{ +public: + Writer (OutputStream* destStream, const String& formatName, + const File& appFile, int vbr, int cbr, + double sampleRate, unsigned int numberOfChannels, + unsigned int bitsPerSample, const StringPairArray& metadata) + : AudioFormatWriter (destStream, formatName, sampleRate, + numberOfChannels, bitsPerSample), + vbrLevel (vbr), cbrBitrate (cbr), + tempWav (".wav") + { + WavAudioFormat wavFormat; + + if (FileOutputStream* out = tempWav.getFile().createOutputStream()) + { + writer = wavFormat.createWriterFor (out, sampleRate, numChannels, + bitsPerSample, metadata, 0); + + args.add (appFile.getFullPathName()); + + args.add ("--quiet"); + + if (cbrBitrate == 0) + { + args.add ("--vbr-new"); + args.add ("-V"); + args.add (String (vbrLevel)); + } + else + { + args.add ("--cbr"); + args.add ("-b"); + args.add (String (cbrBitrate)); + } + + addMetadataArg (metadata, "id3title", "--tt"); + addMetadataArg (metadata, "id3artist", "--ta"); + addMetadataArg (metadata, "id3album", "--tl"); + addMetadataArg (metadata, "id3comment", "--tc"); + addMetadataArg (metadata, "id3date", "--ty"); + addMetadataArg (metadata, "id3genre", "--tg"); + addMetadataArg (metadata, "id3trackNumber", "--tn"); + } + } + + void addMetadataArg (const StringPairArray& metadata, const char* key, const char* lameFlag) + { + const String value (metadata.getValue (key, String())); + + if (value.isNotEmpty()) + { + args.add (lameFlag); + args.add (value); + } + } + + ~Writer() + { + if (writer != nullptr) + { + writer = nullptr; + + if (! convertToMP3()) + convertToMP3(); // try again + } + } + + bool write (const int** samplesToWrite, int numSamples) + { + return writer != nullptr && writer->write (samplesToWrite, numSamples); + } + +private: + int vbrLevel, cbrBitrate; + TemporaryFile tempWav; + ScopedPointer writer; + StringArray args; + + bool runLameChildProcess (const TemporaryFile& tempMP3, const StringArray& processArgs) const + { + ChildProcess cp; + + if (cp.start (processArgs)) + { + const String childOutput (cp.readAllProcessOutput()); + DBG (childOutput); (void) childOutput; + + cp.waitForProcessToFinish (10000); + return tempMP3.getFile().getSize() > 0; + } + + return false; + } + + bool convertToMP3() const + { + TemporaryFile tempMP3 (".mp3"); + + StringArray args2 (args); + args2.add (tempWav.getFile().getFullPathName()); + args2.add (tempMP3.getFile().getFullPathName()); + + DBG (args2.joinIntoString (" ")); + + if (runLameChildProcess (tempMP3, args2)) + { + FileInputStream fis (tempMP3.getFile()); + + if (fis.openedOk() && output->writeFromInputStream (fis, -1) > 0) + { + output->flush(); + return true; + } + } + + return false; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Writer) +}; + +//============================================================================== +LAMEEncoderAudioFormat::LAMEEncoderAudioFormat (const File& lameApplication) + : AudioFormat ("MP3 file", ".mp3"), + lameApp (lameApplication) +{ +} + +LAMEEncoderAudioFormat::~LAMEEncoderAudioFormat() +{ +} + +bool LAMEEncoderAudioFormat::canHandleFile (const File&) +{ + return false; +} + +Array LAMEEncoderAudioFormat::getPossibleSampleRates() +{ + const int rates[] = { 32000, 44100, 48000, 0 }; + return Array (rates); +} + +Array LAMEEncoderAudioFormat::getPossibleBitDepths() +{ + const int depths[] = { 16, 0 }; + return Array (depths); +} + +bool LAMEEncoderAudioFormat::canDoStereo() { return true; } +bool LAMEEncoderAudioFormat::canDoMono() { return true; } +bool LAMEEncoderAudioFormat::isCompressed() { return true; } + +StringArray LAMEEncoderAudioFormat::getQualityOptions() +{ + static const char* vbrOptions[] = { "VBR quality 0 (best)", "VBR quality 1", "VBR quality 2", "VBR quality 3", + "VBR quality 4 (normal)", "VBR quality 5", "VBR quality 6", "VBR quality 7", + "VBR quality 8", "VBR quality 9 (smallest)", nullptr }; + StringArray opts (vbrOptions); + + const int cbrRates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }; + + for (int i = 0; i < numElementsInArray (cbrRates); ++i) + opts.add (String (cbrRates[i]) + " Kb/s CBR"); + + return opts; +} + +AudioFormatReader* LAMEEncoderAudioFormat::createReaderFor (InputStream*, const bool) +{ + return nullptr; +} + +AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* streamToWriteTo, + double sampleRateToUse, + unsigned int numberOfChannels, + int bitsPerSample, + const StringPairArray& metadataValues, + int qualityOptionIndex) +{ + int vbr = 4; + int cbr = 0; + + const String qual (getQualityOptions() [qualityOptionIndex]); + + if (qual.contains ("VBR")) + vbr = qual.retainCharacters ("0123456789").getIntValue(); + else + cbr = qual.getIntValue(); + + return new Writer (streamToWriteTo, getFormatName(), lameApp, vbr, cbr, + sampleRateToUse, numberOfChannels, bitsPerSample, metadataValues); +} + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h new file mode 100644 index 0000000..92e581b --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.h @@ -0,0 +1,71 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_USE_LAME_AUDIO_FORMAT || defined (DOXYGEN) + +//============================================================================== +/** + An AudioFormat class which can use an installed version of the LAME mp3 + encoder to encode a file. + + This format can't read MP3s, it just writes them. Internally, the + AudioFormatWriter object that is returned writes the incoming audio data + to a temporary WAV file, and then when the writer is deleted, it invokes + the LAME executable to convert the data to an MP3, whose data is then + piped into the original OutputStream that was used when first creating + the writer. + + @see AudioFormat +*/ +class JUCE_API LAMEEncoderAudioFormat : public AudioFormat +{ +public: + /** Creates a LAMEEncoderAudioFormat that expects to find a working LAME + executable at the location given. + */ + LAMEEncoderAudioFormat (const File& lameExecutableToUse); + ~LAMEEncoderAudioFormat(); + + bool canHandleFile (const File&); + Array getPossibleSampleRates(); + Array getPossibleBitDepths(); + bool canDoStereo(); + bool canDoMono(); + bool isCompressed(); + StringArray getQualityOptions(); + + AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails); + + AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, + unsigned int numberOfChannels, int bitsPerSample, + const StringPairArray& metadataValues, int qualityOptionIndex); + +private: + File lameApp; + class Writer; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LAMEEncoderAudioFormat) +}; + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index 810d266..219c93f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -437,17 +436,15 @@ struct VBRTagData vbrScale = -1; if (flags & 8) - { - vbrScale = ByteOrder::bigEndianInt (data); - data += 4; - } + vbrScale = (int) ByteOrder::bigEndianInt (data); headersize = ((type + 1) * 72000 * bitrate) / sampleRate; return true; } uint8 toc[100]; - int sampleRate, flags, frames, bytes, vbrScale, headersize; + int sampleRate, vbrScale, headersize; + unsigned int flags, frames, bytes; private: static bool isVbrTag (const uint8* const d) noexcept @@ -496,7 +493,7 @@ struct MP3Frame mpeg25 = (header & (1 << 20)) == 0; lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); layer = 4 - ((header >> 17) & 3); - sampleRateIndex = mpeg25 ? (6 + ((header >> 10) & 3)) : (((header >> 10) & 3) + (lsf * 3)); + sampleRateIndex = mpeg25 ? (6 + ((header >> 10) & 3)) : ((int) ((header >> 10) & 3) + (lsf * 3)); crc16FollowsHeader = ((header >> 16) & 1) == 0; bitrateIndex = (header >> 12) & 15; padding = (header >> 9) & 1; @@ -519,12 +516,21 @@ struct MP3Frame { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 } } }; - switch (layer) + if (bitrateIndex == 0) { - case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break; - case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break; - case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break; - default: break; + jassertfalse; // This means the file is using "free format". Apparently very few decoders + // support this mode, and this one certainly doesn't handle it correctly! + frameSize = 0; + } + else + { + switch (layer) + { + case 1: frameSize = (((frameSizes[lsf][0][bitrateIndex] * 12000) / getFrequency() + padding) * 4) - 4; break; + case 2: frameSize = (frameSizes[lsf][1][bitrateIndex] * 144000) / getFrequency() + (padding - 4); break; + case 3: frameSize = (bitrateIndex == 0) ? 0 : ((frameSizes[lsf][2][bitrateIndex] * 144000) / (getFrequency() << lsf) + (padding - 4)); break; + default: break; + } } } @@ -549,13 +555,13 @@ struct Constants initLayer3Tables(); } - const uint8* getGroupTable (const int16 d1, const int index) const noexcept + const uint8* getGroupTable (const int16 d1, const uint32 index) const noexcept { switch (d1) { - case 3: return &group3tab [3 * jmin (index, 3 * 3 * 3)]; - case 5: return &group5tab [3 * jmin (index, 5 * 5 * 5)]; - case 9: return &group9tab [3 * jmin (index, 9 * 9 * 9)]; + case 3: return &group3tab [3 * jmin (index, 3u * 3u * 3u)]; + case 5: return &group5tab [3 * jmin (index, 5u * 5u * 5u)]; + case 9: return &group9tab [3 * jmin (index, 9u * 9u * 9u)]; default: break; } @@ -671,38 +677,38 @@ private: { int i, j; for (i = -256; i < 118 + 4; ++i) - powToGains[i + 256] = pow (2.0, -0.25 * (i + 210)); + powToGains[i + 256] = (float) pow (2.0, -0.25 * (i + 210)); for (i = 0; i < 8207; ++i) - nToThe4Over3[i] = pow ((double) i, 4.0 / 3.0); + nToThe4Over3[i] = (float) pow ((double) i, 4.0 / 3.0); for (i = 0; i < 8; ++i) { static double Ci[] = { -0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037 }; const double sq = sqrt (1.0 + Ci[i] * Ci[i]); - antiAliasingCs[i] = 1.0 / sq; - antiAliasingCa[i] = Ci[i] / sq; + antiAliasingCs[i] = (float) (1.0 / sq); + antiAliasingCa[i] = (float) (Ci[i] / sq); } for (i = 0; i < 18; ++i) { - win[0][i] = win[1][i] = 0.5 * sin (double_Pi / 72.0 * (2 * i + 1)) / cos (double_Pi * (2 * i + 19) / 72.0); - win[0][i + 18] = win[3][i + 18] = 0.5 * sin (double_Pi / 72.0 * (2 * (i + 18) + 1)) / cos (double_Pi * (2 * (i + 18) + 19) / 72.0); + win[0][i] = win[1][i] = (float) (0.5 * sin (double_Pi / 72.0 * (2 * i + 1)) / cos (double_Pi * (2 * i + 19) / 72.0)); + win[0][i + 18] = win[3][i + 18] = (float) (0.5 * sin (double_Pi / 72.0 * (2 * (i + 18) + 1)) / cos (double_Pi * (2 * (i + 18) + 19) / 72.0)); } const double piOver72 = double_Pi; for (i = 0; i < 6; ++i) { - win[1][i + 18] = 0.5 / cos (piOver72 * (2 * (i + 18) + 19)); - win[3][i + 12] = 0.5 / cos (piOver72 * (2 * (i + 12) + 19)); - win[1][i + 24] = 0.5 * sin (double_Pi / 24.0 * (2 * i + 13)) / cos (piOver72 * (2 * (i + 24) + 19)); + win[1][i + 18] = (float) (0.5 / cos (piOver72 * (2 * (i + 18) + 19))); + win[3][i + 12] = (float) (0.5 / cos (piOver72 * (2 * (i + 12) + 19))); + win[1][i + 24] = (float) (0.5 * sin (double_Pi / 24.0 * (2 * i + 13)) / cos (piOver72 * (2 * (i + 24) + 19))); win[1][i + 30] = win[3][i] = 0; - win[3][i + 6] = 0.5 * sin (double_Pi / 24.0 * (2 * i + 1)) / cos (piOver72 * (2 * (i + 6) + 19)); + win[3][i + 6] = (float) (0.5 * sin (double_Pi / 24.0 * (2 * i + 1)) / cos (piOver72 * (2 * (i + 6) + 19))); } for (i = 0; i < 12; ++i) - win[2][i] = 0.5 * sin (double_Pi / 24.0 * (2 * i + 1)) / cos (double_Pi * (2 * i + 7) / 24.0); + win[2][i] = (float) (0.5 * sin (double_Pi / 24.0 * (2 * i + 1)) / cos (double_Pi * (2 * i + 7) / 24.0)); for (j = 0; j < 4; ++j) { @@ -716,10 +722,10 @@ private: for (i = 0; i < 16; ++i) { const double t = tan (i * double_Pi / 12.0); - tan1_1[i] = t / (1.0 + t); - tan2_1[i] = 1.0 / (1.0 + t); - tan1_2[i] = sqrt2 * t / (1.0 + t); - tan2_2[i] = sqrt2 / (1.0 + t); + tan1_1[i] = (float) (t / (1.0 + t)); + tan2_1[i] = (float) (1.0 / (1.0 + t)); + tan1_2[i] = (float) (sqrt2 * t / (1.0 + t)); + tan2_2[i] = (float) (sqrt2 / (1.0 + t)); for (j = 0; j < 2; ++j) { @@ -735,10 +741,10 @@ private: p2 = pow (base, i * 0.5); } - pow1_1[j][i] = p1; - pow2_1[j][i] = p2; - pow1_2[j][i] = sqrt2 * p1; - pow2_2[j][i] = sqrt2 * p2; + pow1_1[j][i] = (float) p1; + pow2_1[j][i] = (float) p2; + pow1_2[j][i] = (float) (sqrt2 * p1); + pow2_2[j][i] = (float) (sqrt2 * p2); } } @@ -812,7 +818,7 @@ private: for (int k = 0; k < 6; ++k) { const int n = k + j * 6 + i * 36; - iLength2[n] = i | (j << 3) | (k << 6) | (3 << 12); + iLength2[n] = (unsigned int) (i | (j << 3) | (k << 6) | (3 << 12)); } for (i = 0; i < 4; ++i) @@ -820,15 +826,15 @@ private: for (int k = 0; k < 4; ++k) { const int n = k + j * 4 + i * 16; - iLength2[n + 180] = i | (j << 3) | (k << 6) | (4 << 12); + iLength2[n + 180] = (unsigned int) (i | (j << 3) | (k << 6) | (4 << 12)); } for (i = 0; i < 4; ++i) for (j = 0; j < 3; ++j) { const int n = j + i * 3; - iLength2[n + 244] = i | (j << 3) | (5 << 12); - nLength2[n + 500] = i | (j << 3) | (2 << 12) | (1 << 15); + iLength2[n + 244] = (unsigned int) (i | (j << 3) | (5 << 12)); + nLength2[n + 500] = (unsigned int) (i | (j << 3) | (2 << 12) | (1 << 15)); } for (i = 0; i < 5; ++i) @@ -837,7 +843,7 @@ private: for (int l = 0; l < 4; ++l) { const int n = l + k * 4 + j * 16 + i * 80; - nLength2[n] = i | (j << 3) | (k << 6) | (l << 9) | (0 << 12); + nLength2[n] = (unsigned int) (i | (j << 3) | (k << 6) | (l << 9) | (0 << 12)); } for (i = 0; i < 5; ++i) @@ -845,7 +851,7 @@ private: for (int k = 0; k < 4; ++k) { const int n = k + j * 4 + i * 20; - nLength2[n + 400] = i | (j << 3) | (k << 6) | (1 << 12); + nLength2[n + 400] = (unsigned int) (i | (j << 3) | (k << 6) | (1 << 12)); } } }; @@ -871,7 +877,7 @@ struct Layer3SideInfo sb = 1; } else - sb = maxb - 1; + sb = (int) maxb - 1; for (; sb != 0; --sb, xr1 += 10) { @@ -927,9 +933,9 @@ struct Layer3SideInfo { bool doL = mixedBlockFlag != 0; - for (int lwin = 0; lwin < 3; ++lwin) + for (uint32 lwin = 0; lwin < 3; ++lwin) { - int sfb = maxBand[lwin]; + uint32 sfb = maxBand[lwin]; doL = doL && (sfb <= 3); for (; sfb < 12; ++sfb) @@ -940,7 +946,7 @@ struct Layer3SideInfo const float t1 = tabl1[p]; const float t2 = tabl2[p]; int sb = bi.shortDiff[sfb]; - int index = sb + lwin; + uint32 index = (uint32) sb + lwin; for (; sb > 0; --sb, index += 3) { @@ -958,7 +964,7 @@ struct Layer3SideInfo const float t1 = tabl1[p]; const float t2 = tabl2[p]; int sb = bi.shortDiff[12]; - int index = sb + lwin; + uint32 index = (uint32) sb + lwin; for (; sb > 0; --sb, index += 3) { @@ -973,7 +979,7 @@ struct Layer3SideInfo { int index = bi.longIndex[maxBandl]; - for (int sfb = maxBandl; sfb < 8; ++sfb) + for (uint32 sfb = maxBandl; sfb < 8; ++sfb) { int sb = bi.longDiff[sfb]; const int p = scaleFactors[sfb]; @@ -999,7 +1005,7 @@ struct Layer3SideInfo { int index = bi.longIndex[maxBandl]; - for (int sfb = maxBandl; sfb < 21; ++sfb) + for (uint32 sfb = maxBandl; sfb < 21; ++sfb) { int sb = bi.longDiff[sfb]; const int p = scaleFactors[sfb]; @@ -1085,7 +1091,7 @@ namespace DCT dct36_0 (v, ts, out1, out2, wintab, tmp2a - tmp1a, (tmp2b - tmp1b) * cos36[v]); } - void dct36 (float* const in, float* const out1, float* const out2, const float* const wintab, float* const ts) noexcept + static void dct36 (float* const in, float* const out1, float* const out2, const float* const wintab, float* const ts) noexcept { in[17] += in[16]; in[16] += in[15]; in[15] += in[14]; in[14] += in[13]; in[13] += in[12]; in[12] += in[11]; in[11] += in[10]; in[10] += in[9]; in[9] += in[8]; in[8] += in[7]; @@ -1167,7 +1173,7 @@ namespace DCT } }; - void dct12 (const float* in, float* const out1, float* const out2, const float* wi, float* ts) noexcept + static void dct12 (const float* in, float* const out1, float* const out2, const float* wi, float* ts) noexcept { { ts[0] = out1[0]; @@ -1254,7 +1260,7 @@ namespace DCT } } - void dct64 (float* const out0, float* const out1, float* const b1, float* const b2, const float* const samples) noexcept + static void dct64 (float* const out0, float* const out1, float* const b1, float* const b2, const float* const samples) noexcept { { const float* const costab = constants.cosTables[0]; @@ -1380,7 +1386,7 @@ namespace DCT out1[0x10 * 13] = b1[0x17] + b1[0x1F]; out1[0x10 * 15] = b1[0x1F]; } - void dct64 (float* const a, float* const b, const float* const c) noexcept + static void dct64 (float* const a, float* const b, const float* const c) noexcept { float temp[64]; dct64 (a, b, temp, temp + 32, c); @@ -1440,7 +1446,7 @@ struct MP3Stream lastFrameSize += nextFrameOffset; } - frame.decodeHeader (stream.readIntBigEndian()); + frame.decodeHeader ((uint32) stream.readIntBigEndian()); headerParsed = true; frameSize = frame.frameSize; isFreeFormat = (frameSize == 0); @@ -1454,7 +1460,7 @@ struct MP3Stream bufferPointer = bufferSpace[bufferSpaceIndex] + 512; bitIndex = 0; - if (lastFrameSize == -1) + if (lastFrameSize < 0) return 1; } @@ -1516,8 +1522,14 @@ struct MP3Stream else { const int nextFrameOffset = scanForNextFrameHeader (true); + + wasFreeFormat = isFreeFormat; + if (nextFrameOffset < 0) + { + lastFrameSize = frameSize; return result; + } frameSize = nextFrameOffset + sideInfoSize + dataSize; lastFrameSizeNoPadding = frameSize - frame.padding; @@ -1562,7 +1574,8 @@ struct MP3Stream if (result < 0) return false; - else if (result > 0) + + if (result > 0) break; } @@ -1670,7 +1683,7 @@ private: uint32 getOneBit() noexcept { - const uint8 result = *bufferPointer << bitIndex; + const uint8 result = (uint8) (*bufferPointer << bitIndex); ++bitIndex; bufferPointer += (bitIndex >> 3); bitIndex &= 7; @@ -1710,14 +1723,14 @@ private: if (! checkTypeAgainstLastFrame) break; - const bool mpeg25 = (header & (1 << 20)) == 0; - const int lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); - const int sampleRateIndex = mpeg25 ? (6 + ((header >> 10) & 3)) : (((header >> 10) & 3) + (lsf * 3)); - const int mode = (header >> 6) & 3; - const int numChannels = (mode == 3) ? 1 : 2; + const bool mpeg25 = (header & (1 << 20)) == 0; + const uint32 lsf = mpeg25 ? 1 : ((header & (1 << 19)) ? 0 : 1); + const uint32 sampleRateIndex = mpeg25 ? (6 + ((header >> 10) & 3)) : (((header >> 10) & 3) + (lsf * 3)); + const uint32 mode = (header >> 6) & 3; + const uint32 numChannels = (mode == 3) ? 1 : 2; - if (numChannels == frame.numChannels && lsf == frame.lsf - && mpeg25 == frame.mpeg25 && sampleRateIndex == frame.sampleRateIndex) + if (numChannels == (uint32) frame.numChannels && lsf == (uint32) frame.lsf + && mpeg25 == frame.mpeg25 && sampleRateIndex == (uint32) frame.sampleRateIndex) break; } @@ -1746,7 +1759,7 @@ private: if (vbrHeaderFound) { - numFrames = vbrTagData.frames; + numFrames = (int) vbrTagData.frames; oldPos += jmax (vbrTagData.headersize, 1); } @@ -1917,7 +1930,7 @@ private: for (int ch = 0; ch < numChannels; ++ch) databits += sideinfo.ch[ch].gr[gr].part2_3Length; - return databits - 8 * sideinfo.mainDataStart; + return databits - 8 * (int) sideinfo.mainDataStart; } void layer1Step1 (SideInfoLayer1& si) noexcept @@ -1972,7 +1985,7 @@ private: if (n > 0) { - const uint32 w = ((-1 << n) + getBitsUint16 (n + 1) + 1); + const uint32 w = ((uint32) (-1 << n) + getBitsUint16 (n + 1) + 1); fraction[0][i] = (float) (w * constants.muls[n + 1][si.scaleFactor[i][0]]); fraction[1][i] = (float) (w * constants.muls[n + 1][si.scaleFactor[i][1]]); } @@ -2002,11 +2015,10 @@ private: const int jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : frame.layer2SubBandLimit; const AllocationTable* allocTable = frame.allocationTable; uint8 scfsi[32][2]; - int i; if (frame.numChannels == 2) { - for (i = 0; i < jsbound; ++i) + for (int i = 0; i < jsbound; ++i) { const int16 step = allocTable->bits; allocTable += (1 << step); @@ -2014,7 +2026,7 @@ private: si.allocation[i][1] = getBitsUint8 (step); } - for (i = jsbound; i < sblimit; ++i) + for (int i = jsbound; i < sblimit; ++i) { const int16 step = allocTable->bits; const uint8 b0 = getBitsUint8 (step); @@ -2023,7 +2035,7 @@ private: si.allocation[i][1] = b0; } - for (i = 0; i < sblimit; ++i) + for (int i = 0; i < sblimit; ++i) { scfsi[i][0] = si.allocation[i][0] ? getBitsUint8 (2) : 0; scfsi[i][1] = si.allocation[i][1] ? getBitsUint8 (2) : 0; @@ -2031,18 +2043,18 @@ private: } else { - for (i = 0; i < sblimit; ++i) + for (int i = 0; i < sblimit; ++i) { const int16 step = allocTable->bits; allocTable += (1 << step); si.allocation[i][0] = getBitsUint8 (step); } - for (i = 0; i < sblimit; ++i) + for (int i = 0; i < sblimit; ++i) scfsi[i][0] = si.allocation[i][0] ? getBitsUint8 (2) : 0; } - for (i = 0; i < sblimit; ++i) + for (int i = 0; i < sblimit; ++i) { for (int ch = 0; ch < frame.numChannels; ++ch) { @@ -2084,9 +2096,8 @@ private: { const AllocationTable* allocTable = frame.allocationTable; const int jsbound = (frame.mode == 1) ? (frame.modeExt << 2) + 4 : frame.layer2SubBandLimit; - int i; - for (i = 0; i < jsbound; ++i) + for (int i = 0; i < jsbound; ++i) { const int16 step = allocTable->bits; @@ -2103,9 +2114,9 @@ private: if (d1 < 0) { const double cm = constants.muls[k][x1]; - fraction[ch][0][i] = (float) ((getBits (k) + d1) * cm); - fraction[ch][1][i] = (float) ((getBits (k) + d1) * cm); - fraction[ch][2][i] = (float) ((getBits (k) + d1) * cm); + fraction[ch][0][i] = (float) (((int) getBits (k) + d1) * cm); + fraction[ch][1][i] = (float) (((int) getBits (k) + d1) * cm); + fraction[ch][2][i] = (float) (((int) getBits (k) + d1) * cm); } else { @@ -2124,7 +2135,7 @@ private: allocTable += (1 << step); } - for (i = jsbound; i < frame.layer2SubBandLimit; ++i) + for (int i = jsbound; i < frame.layer2SubBandLimit; ++i) { const int16 step = allocTable->bits; const uint8 ba = si.allocation[i][0]; @@ -2138,9 +2149,9 @@ private: if (d1 < 0) { - const int v0 = getBits (k); - const int v1 = getBits (k); - const int v2 = getBits (k); + const int v0 = (int) getBits (k); + const int v1 = (int) getBits (k); + const int v2 = (int) getBits (k); for (int ch = 0; ch < frame.numChannels; ++ch) { @@ -2176,7 +2187,7 @@ private: } for (int ch = 0; ch < frame.numChannels; ++ch) - for (i = frame.layer2SubBandLimit; i < 32; ++i) + for (int i = frame.layer2SubBandLimit; i < 32; ++i) fraction[ch][0][i] = fraction[ch][1][i] = fraction[ch][2][i] = 0; } @@ -2189,7 +2200,7 @@ private: for (int ch = 0; ch < stereo; ++ch) { sideinfo.ch[ch].gr[0].scfsi = -1; - sideinfo.ch[ch].gr[1].scfsi = getBitsUnchecked (4); + sideinfo.ch[ch].gr[1].scfsi = (int) getBitsUnchecked (4); } for (int gr = 0; gr < 2; ++gr) @@ -2199,9 +2210,9 @@ private: Layer3SideInfo::Info& granule = sideinfo.ch[ch].gr[gr]; granule.part2_3Length = getBits (12); - granule.bigValues = jmin (288, (int) getBitsUnchecked (9)); + granule.bigValues = jmin (288u, getBitsUnchecked (9)); - const int qss = getBitsUnchecked (8); + const int qss = (int) getBitsUnchecked (8); granule.pow2gain = constants.powToGains + 256 - qss + powdiff; if (msStereo) @@ -2219,7 +2230,7 @@ private: for (int i = 0; i < 3; ++i) { - const int sbg = (getBitsUnchecked (3) << 3); + const uint32 sbg = (getBitsUnchecked (3) << 3); granule.fullGain[i] = granule.pow2gain + sbg; } @@ -2231,13 +2242,13 @@ private: for (int i = 0; i < 3; ++i) granule.tableSelect[i] = getBitsUnchecked (5); - const int r0c = getBitsUnchecked (4); - const int r1c = getBitsUnchecked (3); + const int r0c = (int) getBitsUnchecked (4); + const int r1c = (int) getBitsUnchecked (3); const int region0index = jmin (22, r0c + 1); const int region1index = jmin (22, r0c + 1 + r1c + 1); - granule.region1Start = bandInfo[sampleRate].longIndex[region0index] >> 1; - granule.region2Start = bandInfo[sampleRate].longIndex[region1index] >> 1; + granule.region1Start = (uint32) (bandInfo[sampleRate].longIndex[region0index] >> 1); + granule.region2Start = (uint32) (bandInfo[sampleRate].longIndex[region1index] >> 1); granule.blockType = 0; granule.mixedBlockFlag = 0; } @@ -2260,7 +2271,7 @@ private: Layer3SideInfo::Info& granule = sideinfo.ch[ch].gr[0]; granule.part2_3Length = getBits (12); - granule.bigValues = jmin (288, (int) getBitsUnchecked (9)); + granule.bigValues = jmin (288u, getBitsUnchecked (9)); const uint32 qss = getBitsUnchecked (8); granule.pow2gain = constants.powToGains + 256 - qss + powdiff; @@ -2299,13 +2310,13 @@ private: for (int i = 0; i < 3; ++i) granule.tableSelect[i] = getBitsUnchecked (5); - const int r0c = getBitsUnchecked (4); - const int r1c = getBitsUnchecked (3); + const int r0c = (int) getBitsUnchecked (4); + const int r1c = (int) getBitsUnchecked (3); const int region0index = jmin (22, r0c + 1); const int region1index = jmin (22, r0c + 1 + r1c + 1); - granule.region1Start = bandInfo[sampleRate].longIndex[region0index] >> 1; - granule.region2Start = bandInfo[sampleRate].longIndex[region1index] >> 1; + granule.region1Start = (uint32) (bandInfo[sampleRate].longIndex[region0index] >> 1); + granule.region2Start = (uint32) (bandInfo[sampleRate].longIndex[region1index] >> 1); granule.blockType = 0; granule.mixedBlockFlag = 0; } @@ -2333,13 +2344,13 @@ private: if (granule.mixedBlockFlag) { - for (int j = 8; --j >= 0;) *scf++ = getBitsUnchecked (num0); + for (int j = 8; --j >= 0;) *scf++ = (int) getBitsUnchecked (num0); numBits -= num0; i = 9; } - for (; --i >= 0;) *scf++ = getBitsUnchecked (num0); - for (i = 18; --i >= 0;) *scf++ = getBitsUnchecked (num1); + for (; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); + for (i = 18; --i >= 0;) *scf++ = (int) getBitsUnchecked (num1); *scf++ = 0; *scf++ = 0; @@ -2351,8 +2362,8 @@ private: if (scfsi < 0) { - for (int i = 11; --i >= 0;) *scf++ = getBitsUnchecked (num0); - for (int j = 10; --j >= 0;) *scf++ = getBitsUnchecked (num1); + for (int i = 11; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); + for (int j = 10; --j >= 0;) *scf++ = (int) getBitsUnchecked (num1); numBits = (num0 + num1) * 10 + num0; } else @@ -2360,7 +2371,7 @@ private: numBits = 0; if ((scfsi & 8) == 0) { - for (int i = 6; --i >= 0;) *scf++ = getBitsUnchecked (num0); + for (int i = 6; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); numBits += num0 * 6; } else @@ -2368,7 +2379,7 @@ private: if ((scfsi & 4) == 0) { - for (int i = 5; --i >= 0;) *scf++ = getBitsUnchecked (num0); + for (int i = 5; --i >= 0;) *scf++ = (int) getBitsUnchecked (num0); numBits += num0 * 5; } else @@ -2376,7 +2387,7 @@ private: if ((scfsi & 2) == 0) { - for (int i = 5; --i >= 0;) *scf++ = getBitsUnchecked (num1); + for (int i = 5; --i >= 0;) *scf++ = (int) getBitsUnchecked (num1); numBits += num1 * 5; } else @@ -2384,7 +2395,7 @@ private: if ((scfsi & 1) == 0) { - for (int i = 5; --i >= 0;) *scf++ = getBitsUnchecked (num1); + for (int i = 5; --i >= 0;) *scf++ = (int) getBitsUnchecked (num1); numBits += num1 * 5; } else @@ -2430,7 +2441,7 @@ private: if (num) { for (int j = 0; j < (int) (data[i]); ++j) - *scf++ = getBitsUnchecked (num); + *scf++ = (int) getBitsUnchecked (num); numBits += data[i] * num; } @@ -2450,15 +2461,15 @@ private: bool layer3DequantizeSample (float xr[32][18], int* scf, Layer3SideInfo::Info& granule, int sampleRate, int part2bits) noexcept { - const int shift = 1 + granule.scaleFactorScale; + const uint32 shift = 1 + granule.scaleFactorScale; float* xrpnt = (float*) xr; - int i, part2remain = granule.part2_3Length - part2bits; + int part2remain = (int) granule.part2_3Length - part2bits; - zeromem (xrpnt, sizeof (float) * (&xr[32][0] - xrpnt)); + zeromem (xrpnt, sizeof (float) * (size_t) (&xr[32][0] - xrpnt)); - const int bv = granule.bigValues; - const int region1 = granule.region1Start; - const int region2 = granule.region2Start; + const int bv = (int) granule.bigValues; + const int region1 = (int) granule.region1Start; + const int region2 = (int) granule.region2Start; int l3 = ((576 >> 1) - bv) >> 1; int l[3]; @@ -2483,7 +2494,7 @@ private: } } - for (i = 0; i < 3; ++i) + for (int i = 0; i < 3; ++i) if (l[i] < 0) l[i] = 0; @@ -2509,7 +2520,7 @@ private: mapEnd = constants.mapEnd [sampleRate][1]; } - for (i = 0; i < 2; ++i) + for (int i = 0; i < 2; ++i) { const BitsToTableMap* h = huffmanTables1 + granule.tableSelect[i]; @@ -2605,7 +2616,7 @@ private: val -= a; } - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { if ((i & 1) == 0) { @@ -2662,14 +2673,14 @@ private: *xrpnt = 0; xrpnt += step; } - granule.maxBand[0] = max[0] + 1; - granule.maxBand[1] = max[1] + 1; - granule.maxBand[2] = max[2] + 1; - granule.maxBandl = max[3] + 1; + granule.maxBand[0] = (uint32) (max[0] + 1); + granule.maxBand[1] = (uint32) (max[1] + 1); + granule.maxBand[2] = (uint32) (max[2] + 1); + granule.maxBandl = (uint32) (max[3] + 1); const int rmax = jmax (max[0], max[1], max[3]) + 1; - granule.maxb = rmax ? constants.shortLimit[sampleRate][rmax] - : constants.longLimit[sampleRate][max[3] + 1]; + granule.maxb = rmax ? (uint32) constants.shortLimit[sampleRate][rmax] + : (uint32) constants.longLimit[sampleRate][max[3] + 1]; } else { @@ -2677,11 +2688,11 @@ private: static const int pretab2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; const int* pretab = (const int*) (granule.preflag ? pretab1 : pretab2); - int i, max = -1, cb = 0, mc = 0; + int max = -1, cb = 0, mc = 0; int* map = constants.map [sampleRate][2]; float v = 0; - for (i = 0; i < 3; ++i) + for (int i = 0; i < 3; ++i) { const BitsToTableMap* h = huffmanTables1 + granule.tableSelect[i]; @@ -2759,7 +2770,7 @@ private: values -= a; } - for (i = 0; i < 4; ++i) + for (int i = 0; i < 4; ++i) { if ((i & 1) == 0) { @@ -2787,10 +2798,10 @@ private: } } - zeromem (xrpnt, sizeof (float) * (&xr[32][0] - xrpnt)); + zeromem (xrpnt, sizeof (float) * (size_t) (&xr[32][0] - xrpnt)); - granule.maxBandl = max + 1; - granule.maxb = constants.longLimit[sampleRate][granule.maxBandl]; + granule.maxBandl = (uint32) (max + 1); + granule.maxb = (uint32) constants.longLimit[sampleRate][granule.maxBandl]; } while (part2remain > 16) @@ -2831,7 +2842,7 @@ private: ts += 2; } - int bt = granule.blockType; + const uint32 bt = granule.blockType; if (bt == 2) { for (; sb < (int) granule.maxb; sb += 2, ts += 2, rawout1 += 36, rawout2 += 36) @@ -2928,19 +2939,18 @@ private: samplesDone += 32; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MP3Stream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MP3Stream) }; //============================================================================== static const char* const mp3FormatName = "MP3 file"; -static const char* const mp3Extensions[] = { ".mp3", nullptr }; //============================================================================== class MP3Reader : public AudioFormatReader { public: MP3Reader (InputStream* const in) - : AudioFormatReader (in, TRANS (mp3FormatName)), + : AudioFormatReader (in, mp3FormatName), stream (*in), currentPosition (0), decodedStart (0), decodedEnd (0) { @@ -2952,20 +2962,19 @@ public: bitsPerSample = 32; usesFloatingPointData = true; sampleRate = stream.frame.getFrequency(); - numChannels = stream.frame.numChannels; + numChannels = (unsigned int) stream.frame.numChannels; lengthInSamples = findLength (streamPos); } } - //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples) + int64 startSampleInFile, int numSamples) override { jassert (destSamples != nullptr); if (currentPosition != startSampleInFile) { - if (! stream.seek (startSampleInFile / 1152 - 1)) + if (! stream.seek ((int) (startSampleInFile / 1152 - 1))) { currentPosition = -1; createEmptyDecodedData(); @@ -2974,7 +2983,7 @@ public: { decodedStart = decodedEnd = 0; const int64 streamPos = stream.currentFrameIndex * 1152; - int toSkip = startSampleInFile - streamPos; + int toSkip = (int) (startSampleInFile - streamPos); jassert (toSkip >= 0); while (toSkip > 0) @@ -3004,19 +3013,19 @@ public: { if (decodedEnd <= decodedStart && ! readNextBlock()) { - for (int i = 2; --i >= 0;) + for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (float) * numSamples); + zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (float) * (size_t) numSamples); return false; } const int numToCopy = jmin (decodedEnd - decodedStart, numSamples); float* const* const dst = reinterpret_cast (destSamples); - memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, sizeof (float) * numToCopy); + memcpy (dst[0] + startOffsetInDestBuffer, decoded0 + decodedStart, sizeof (float) * (size_t) numToCopy); - if (dst[1] != nullptr) - memcpy (dst[1] + startOffsetInDestBuffer, (numChannels < 2 ? decoded0 : decoded1) + decodedStart, sizeof (float) * numToCopy); + if (numDestChannels > 1 && dst[1] != nullptr) + memcpy (dst[1] + startOffsetInDestBuffer, (numChannels < 2 ? decoded0 : decoded1) + decodedStart, sizeof (float) * (size_t) numToCopy); startOffsetInDestBuffer += numToCopy; decodedStart += numToCopy; @@ -3054,7 +3063,8 @@ private: createEmptyDecodedData(); return true; } - else if (result <= 0) + + if (result <= 0) { decodedStart = 0; decodedEnd = samplesDone; @@ -3068,7 +3078,7 @@ private: void skipID3() { const int64 originalPosition = stream.stream.getPosition(); - const uint32 firstWord = stream.stream.readInt(); + const uint32 firstWord = (uint32) stream.stream.readInt(); if ((firstWord & 0xffffff) == 0x334449) { @@ -3078,10 +3088,10 @@ private: && buffer[0] != 0xff && ((buffer[2] | buffer[3] | buffer[4] | buffer[5]) & 0x80) == 0) { - const int length = (((uint32) buffer[2]) << 21) - | (((uint32) buffer[3]) << 14) - | (((uint32) buffer[4]) << 7) - | ((uint32) buffer[5]); + const uint32 length = (((uint32) buffer[2]) << 21) + | (((uint32) buffer[3]) << 14) + | (((uint32) buffer[4]) << 7) + | ((uint32) buffer[5]); stream.stream.skipNextBytes (length); return; @@ -3100,22 +3110,26 @@ private: const int64 streamSize = stream.stream.getTotalLength(); if (streamSize > 0) - numFrames = (streamSize - streamStartPos) / (stream.frame.frameSize); + { + const int bytesPerFrame = stream.frame.frameSize + 4; + + if (bytesPerFrame == 417 || bytesPerFrame == 418) + numFrames = roundToInt ((streamSize - streamStartPos) / 417.95918); // more accurate for 128k + else + numFrames = (streamSize - streamStartPos) / bytesPerFrame; + } } return numFrames * 1152; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MP3Reader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MP3Reader) }; } //============================================================================== -MP3AudioFormat::MP3AudioFormat() - : AudioFormat (MP3Decoder::mp3FormatName, StringArray (MP3Decoder::mp3Extensions)) -{} - +MP3AudioFormat::MP3AudioFormat() : AudioFormat (MP3Decoder::mp3FormatName, ".mp3") {} MP3AudioFormat::~MP3AudioFormat() {} Array MP3AudioFormat::getPossibleSampleRates() { return Array(); } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h index 3723b48..e89c05d 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.h @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if JUCE_USE_MP3AUDIOFORMAT +#if JUCE_USE_MP3AUDIOFORMAT || DOXYGEN //============================================================================== /** @@ -47,19 +46,19 @@ public: ~MP3AudioFormat(); //============================================================================== - Array getPossibleSampleRates(); - Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); - bool isCompressed(); - StringArray getQualityOptions(); + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; + bool isCompressed() override; + StringArray getQualityOptions() override; //============================================================================== - AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails); + AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, - const StringPairArray& metadataValues, int qualityOptionIndex); + const StringPairArray& metadataValues, int qualityOptionIndex) override; }; #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp index 554484e..7cc12a4 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -37,6 +36,13 @@ namespace OggVorbisNamespace #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4305 4189 4706 4995 4365) #endif + #if JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wconversion" + #pragma clang diagnostic ignored "-Wshadow" + #pragma clang diagnostic ignored "-Wdeprecated-register" + #endif + #include "oggvorbis/vorbisenc.h" #include "oggvorbis/codec.h" #include "oggvorbis/vorbisfile.h" @@ -68,6 +74,10 @@ namespace OggVorbisNamespace #if JUCE_MSVC #pragma warning (pop) #endif + + #if JUCE_CLANG + #pragma clang diagnostic pop + #endif #else #include #include @@ -80,15 +90,23 @@ namespace OggVorbisNamespace //============================================================================== static const char* const oggFormatName = "Ogg-Vorbis file"; -static const char* const oggExtensions[] = { ".ogg", 0 }; + +const char* const OggVorbisAudioFormat::encoderName = "encoder"; +const char* const OggVorbisAudioFormat::id3title = "id3title"; +const char* const OggVorbisAudioFormat::id3artist = "id3artist"; +const char* const OggVorbisAudioFormat::id3album = "id3album"; +const char* const OggVorbisAudioFormat::id3comment = "id3comment"; +const char* const OggVorbisAudioFormat::id3date = "id3date"; +const char* const OggVorbisAudioFormat::id3genre = "id3genre"; +const char* const OggVorbisAudioFormat::id3trackNumber = "id3trackNumber"; + //============================================================================== class OggReader : public AudioFormatReader { public: OggReader (InputStream* const inp) - : AudioFormatReader (inp, TRANS (oggFormatName)), - reservoir (2, 4096), + : AudioFormatReader (inp, oggFormatName), reservoirStart (0), samplesInReservoir (0) { @@ -96,23 +114,33 @@ public: sampleRate = 0; usesFloatingPointData = true; - callbacks.read_func = &oggReadCallback; - callbacks.seek_func = &oggSeekCallback; + callbacks.read_func = &oggReadCallback; + callbacks.seek_func = &oggSeekCallback; callbacks.close_func = &oggCloseCallback; - callbacks.tell_func = &oggTellCallback; + callbacks.tell_func = &oggTellCallback; const int err = ov_open_callbacks (input, &ovFile, 0, 0, callbacks); if (err == 0) { vorbis_info* info = ov_info (&ovFile, -1); + + vorbis_comment* const comment = ov_comment (&ovFile, -1); + addMetadataItem (comment, "ENCODER", OggVorbisAudioFormat::encoderName); + addMetadataItem (comment, "TITLE", OggVorbisAudioFormat::id3title); + addMetadataItem (comment, "ARTIST", OggVorbisAudioFormat::id3artist); + addMetadataItem (comment, "ALBUM", OggVorbisAudioFormat::id3album); + addMetadataItem (comment, "COMMENT", OggVorbisAudioFormat::id3comment); + addMetadataItem (comment, "DATE", OggVorbisAudioFormat::id3date); + addMetadataItem (comment, "GENRE", OggVorbisAudioFormat::id3genre); + addMetadataItem (comment, "TRACKNUMBER", OggVorbisAudioFormat::id3trackNumber); + lengthInSamples = (uint32) ov_pcm_total (&ovFile, -1); numChannels = (unsigned int) info->channels; bitsPerSample = 16; sampleRate = info->rate; - reservoir.setSize ((int) numChannels, - (int) jmin (lengthInSamples, (int64) reservoir.getNumSamples())); + reservoir.setSize ((int) numChannels, (int) jmin (lengthInSamples, (int64) 4096)); } } @@ -121,13 +149,19 @@ public: OggVorbisNamespace::ov_clear (&ovFile); } + void addMetadataItem (OggVorbisNamespace::vorbis_comment* comment, const char* name, const char* metadataName) + { + if (const char* value = vorbis_comment_query (comment, name, 0)) + metadataValues.set (metadataName, value); + } + //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples) + int64 startSampleInFile, int numSamples) override { while (numSamples > 0) { - const int numAvailable = reservoirStart + samplesInReservoir - startSampleInFile; + const int numAvailable = (int) (reservoirStart + samplesInReservoir - startSampleInFile); if (startSampleInFile >= reservoirStart && numAvailable > 0) { @@ -138,8 +172,8 @@ public: for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) if (destSamples[i] != nullptr) memcpy (destSamples[i] + startOffsetInDestBuffer, - reservoir.getSampleData (i, (int) (startSampleInFile - reservoirStart)), - sizeof (float) * numToUse); + reservoir.getReadPointer (i, (int) (startSampleInFile - reservoirStart)), + sizeof (float) * (size_t) numToUse); startSampleInFile += numToUse; numSamples -= numToUse; @@ -168,18 +202,14 @@ public: { float** dataIn = nullptr; - const int samps = OggVorbisNamespace::ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); + const long samps = OggVorbisNamespace::ov_read_float (&ovFile, &dataIn, numToRead, &bitStream); if (samps <= 0) break; jassert (samps <= numToRead); for (int i = jmin ((int) numChannels, reservoir.getNumChannels()); --i >= 0;) - { - memcpy (reservoir.getSampleData (i, offset), - dataIn[i], - sizeof (float) * samps); - } + memcpy (reservoir.getWritePointer (i, offset), dataIn[i], sizeof (float) * (size_t) samps); numToRead -= samps; offset += samps; @@ -194,7 +224,7 @@ public: { for (int i = numDestChannels; --i >= 0;) if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * numSamples); + zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); } return true; @@ -203,12 +233,12 @@ public: //============================================================================== static size_t oggReadCallback (void* ptr, size_t size, size_t nmemb, void* datasource) { - return (size_t) (static_cast (datasource)->read (ptr, (int) (size * nmemb)) / size); + return (size_t) (static_cast (datasource)->read (ptr, (int) (size * nmemb))) / size; } static int oggSeekCallback (void* datasource, OggVorbisNamespace::ogg_int64_t offset, int whence) { - InputStream* const in = static_cast (datasource); + InputStream* const in = static_cast (datasource); if (whence == SEEK_CUR) offset += in->getPosition(); @@ -226,7 +256,7 @@ public: static long oggTellCallback (void* datasource) { - return (long) static_cast (datasource)->getPosition(); + return (long) static_cast (datasource)->getPosition(); } private: @@ -235,7 +265,7 @@ private: AudioSampleBuffer reservoir; int reservoirStart, samplesInReservoir; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggReader) }; //============================================================================== @@ -247,8 +277,8 @@ public: const unsigned int numChannels_, const unsigned int bitsPerSample_, const int qualityIndex, - const StringPairArray& metadataValues) - : AudioFormatWriter (out, TRANS (oggFormatName), sampleRate_, numChannels_, bitsPerSample_), + const StringPairArray& metadata) + : AudioFormatWriter (out, oggFormatName, sampleRate_, numChannels_, bitsPerSample_), ok (false) { using namespace OggVorbisNamespace; @@ -260,9 +290,14 @@ public: { vorbis_comment_init (&vc); - const String encoder (metadataValues [OggVorbisAudioFormat::encoderName]); - if (encoder.isNotEmpty()) - vorbis_comment_add_tag (&vc, "ENCODER", const_cast (encoder.toUTF8().getAddress())); + addMetadata (metadata, OggVorbisAudioFormat::encoderName, "ENCODER"); + addMetadata (metadata, OggVorbisAudioFormat::id3title, "TITLE"); + addMetadata (metadata, OggVorbisAudioFormat::id3artist, "ARTIST"); + addMetadata (metadata, OggVorbisAudioFormat::id3album, "ALBUM"); + addMetadata (metadata, OggVorbisAudioFormat::id3comment, "COMMENT"); + addMetadata (metadata, OggVorbisAudioFormat::id3date, "DATE"); + addMetadata (metadata, OggVorbisAudioFormat::id3genre, "GENRE"); + addMetadata (metadata, OggVorbisAudioFormat::id3trackNumber, "TRACKNUMBER"); vorbis_analysis_init (&vd, &vi); vorbis_block_init (&vd, &vb); @@ -284,8 +319,8 @@ public: if (ogg_stream_flush (&os, &og) == 0) break; - output->write (og.header, og.header_len); - output->write (og.body, og.body_len); + output->write (og.header, (size_t) og.header_len); + output->write (og.body, (size_t) og.body_len); } ok = true; @@ -317,7 +352,7 @@ public: } //============================================================================== - bool write (const int** samplesToWrite, int numSamples) + bool write (const int** samplesToWrite, int numSamples) override { if (ok) { @@ -367,8 +402,8 @@ public: if (ogg_stream_pageout (&os, &og) == 0) break; - output->write (og.header, og.header_len); - output->write (og.body, og.body_len); + output->write (og.header, (size_t) og.header_len); + output->write (og.body, (size_t) og.body_len); if (ogg_page_eos (&og)) break; @@ -388,13 +423,20 @@ private: OggVorbisNamespace::vorbis_dsp_state vd; OggVorbisNamespace::vorbis_block vb; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter); + void addMetadata (const StringPairArray& metadata, const char* name, const char* vorbisName) + { + const String s (metadata [name]); + + if (s.isNotEmpty()) + vorbis_comment_add_tag (&vc, vorbisName, const_cast (s.toRawUTF8())); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggWriter) }; //============================================================================== -OggVorbisAudioFormat::OggVorbisAudioFormat() - : AudioFormat (TRANS (oggFormatName), StringArray (oggExtensions)) +OggVorbisAudioFormat::OggVorbisAudioFormat() : AudioFormat (oggFormatName, ".ogg") { } @@ -404,22 +446,23 @@ OggVorbisAudioFormat::~OggVorbisAudioFormat() Array OggVorbisAudioFormat::getPossibleSampleRates() { - const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; - return Array (rates); + const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, + 44100, 48000, 88200, 96000, 176400, 192000 }; + + return Array (rates, numElementsInArray (rates)); } Array OggVorbisAudioFormat::getPossibleBitDepths() { - const int depths[] = { 32, 0 }; - return Array (depths); + const int depths[] = { 32 }; + + return Array (depths, numElementsInArray (depths)); } bool OggVorbisAudioFormat::canDoStereo() { return true; } bool OggVorbisAudioFormat::canDoMono() { return true; } bool OggVorbisAudioFormat::isCompressed() { return true; } -const char* const OggVorbisAudioFormat::encoderName = "encoder"; - AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) { ScopedPointer r (new OggReader (in)); @@ -448,37 +491,42 @@ AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out, StringArray OggVorbisAudioFormat::getQualityOptions() { - const char* options[] = { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps", - "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps", 0 }; + static const char* options[] = { "64 kbps", "80 kbps", "96 kbps", "112 kbps", "128 kbps", "160 kbps", + "192 kbps", "224 kbps", "256 kbps", "320 kbps", "500 kbps", 0 }; return StringArray (options); } int OggVorbisAudioFormat::estimateOggFileQuality (const File& source) { - FileInputStream* const in = source.createInputStream(); - - if (in != nullptr) + if (FileInputStream* const in = source.createInputStream()) { - ScopedPointer r (createReaderFor (in, true)); + ScopedPointer r (createReaderFor (in, true)); if (r != nullptr) { - const int64 numSamps = r->lengthInSamples; - r = nullptr; + const double lengthSecs = r->lengthInSamples / r->sampleRate; + const int approxBitsPerSecond = (int) (source.getSize() * 8 / lengthSecs); - const int64 fileNumSamps = source.getSize() / 4; - const double ratio = numSamps / (double) fileNumSamps; + const StringArray qualities (getQualityOptions()); + int bestIndex = 0; + int bestDiff = 10000; - if (ratio > 12.0) - return 0; - else if (ratio > 6.0) - return 1; - else - return 2; + for (int i = qualities.size(); --i >= 0;) + { + const int diff = std::abs (qualities[i].getIntValue() - approxBitsPerSecond); + + if (diff < bestDiff) + { + bestDiff = diff; + bestIndex = i; + } + } + + return bestIndex; } } - return 1; + return 0; } #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h index 2075687..03bc3fb 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_OggVorbisAudioFormat.h @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -33,7 +32,7 @@ @see AudioFormat, */ -class JUCE_API OggVorbisAudioFormat : public AudioFormat +class JUCE_API OggVorbisAudioFormat : public AudioFormat { public: //============================================================================== @@ -41,12 +40,12 @@ public: ~OggVorbisAudioFormat(); //============================================================================== - Array getPossibleSampleRates(); - Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); - bool isCompressed(); - StringArray getQualityOptions(); + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; + bool isCompressed() override; + StringArray getQualityOptions() override; //============================================================================== /** Tries to estimate the quality level of an ogg file based on its size. @@ -67,19 +66,27 @@ public: */ static const char* const encoderName; + static const char* const id3title; /**< Metadata key for setting an ID3 title. */ + static const char* const id3artist; /**< Metadata key for setting an ID3 artist name. */ + static const char* const id3album; /**< Metadata key for setting an ID3 album. */ + static const char* const id3comment; /**< Metadata key for setting an ID3 comment. */ + static const char* const id3date; /**< Metadata key for setting an ID3 date. */ + static const char* const id3genre; /**< Metadata key for setting an ID3 genre. */ + static const char* const id3trackNumber; /**< Metadata key for setting an ID3 track number. */ + //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, - bool deleteStreamIfOpeningFails); + bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, - int qualityOptionIndex); + int qualityOptionIndex) override; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggVorbisAudioFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OggVorbisAudioFormat) }; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp index 66584e5..17a06da 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -67,14 +66,13 @@ namespace juce bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); static const char* const quickTimeFormatName = "QuickTime file"; -static const char* const quickTimeExtensions[] = { ".mov", ".mp3", ".mp4", ".m4a", 0 }; //============================================================================== class QTAudioReader : public AudioFormatReader { public: QTAudioReader (InputStream* const input_, const int trackNum_) - : AudioFormatReader (input_, TRANS (quickTimeFormatName)), + : AudioFormatReader (input_, quickTimeFormatName), ok (false), movie (0), trackNum (trackNum_), @@ -84,225 +82,231 @@ public: dataHandle (0) { JUCE_AUTORELEASEPOOL - bufferList.calloc (256, 1); - - #if JUCE_WINDOWS - if (InitializeQTML (0) != noErr) - return; - #endif - - if (EnterMovies() != noErr) - return; - - bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle); - - if (! opened) - return; - { - const int numTracks = GetMovieTrackCount (movie); - int trackCount = 0; + bufferList.calloc (256, 1); + + #if JUCE_WINDOWS + if (InitializeQTML (0) != noErr) + return; + #endif + + if (EnterMovies() != noErr) + return; + + bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle); + + if (! opened) + return; - for (int i = 1; i <= numTracks; ++i) { - track = GetMovieIndTrack (movie, i); - media = GetTrackMedia (track); + const int numTracks = GetMovieTrackCount (movie); + int trackCount = 0; - OSType mediaType; - GetMediaHandlerDescription (media, &mediaType, 0, 0); - - if (mediaType == SoundMediaType - && trackCount++ == trackNum_) + for (int i = 1; i <= numTracks; ++i) { - ok = true; - break; + track = GetMovieIndTrack (movie, i); + media = GetTrackMedia (track); + + OSType mediaType; + GetMediaHandlerDescription (media, &mediaType, 0, 0); + + if (mediaType == SoundMediaType + && trackCount++ == trackNum_) + { + ok = true; + break; + } } } - } - if (! ok) - return; + if (! ok) + return; - ok = false; + ok = false; - lengthInSamples = GetMediaDecodeDuration (media); - usesFloatingPointData = false; + lengthInSamples = GetMediaDecodeDuration (media); + usesFloatingPointData = false; - samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media)); + samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media)); - trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame - / GetMediaTimeScale (media); + trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame + / GetMediaTimeScale (media); - OSStatus err = MovieAudioExtractionBegin (movie, 0, &extractor); + MovieAudioExtractionBegin (movie, 0, &extractor); - unsigned long output_layout_size; - err = MovieAudioExtractionGetPropertyInfo (extractor, + unsigned long output_layout_size; + OSStatus err = MovieAudioExtractionGetPropertyInfo (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + 0, &output_layout_size, 0); + if (err != noErr) + return; + + HeapBlock qt_audio_channel_layout; + qt_audio_channel_layout.calloc (output_layout_size, 1); + + MovieAudioExtractionGetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + output_layout_size, qt_audio_channel_layout, 0); + + qt_audio_channel_layout[0].mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; + + MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, + output_layout_size, + qt_audio_channel_layout); + + err = MovieAudioExtractionGetProperty (extractor, kQTPropertyClass_MovieAudioExtraction_Audio, - kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, - 0, &output_layout_size, 0); - if (err != noErr) - return; + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof (inputStreamDesc), + &inputStreamDesc, 0); + if (err != noErr) + return; - HeapBlock qt_audio_channel_layout; - qt_audio_channel_layout.calloc (output_layout_size, 1); + inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger + | kAudioFormatFlagIsPacked + | kAudioFormatFlagsNativeEndian; + inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8; + inputStreamDesc.mChannelsPerFrame = jmin ((UInt32) 2, inputStreamDesc.mChannelsPerFrame); + inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame; + inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame; - err = MovieAudioExtractionGetProperty (extractor, - kQTPropertyClass_MovieAudioExtraction_Audio, - kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, - output_layout_size, qt_audio_channel_layout, 0); + err = MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Audio, + kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, + sizeof (inputStreamDesc), + &inputStreamDesc); + if (err != noErr) + return; - qt_audio_channel_layout[0].mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; + Boolean allChannelsDiscrete = false; + err = MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, + sizeof (allChannelsDiscrete), + &allChannelsDiscrete); - err = MovieAudioExtractionSetProperty (extractor, - kQTPropertyClass_MovieAudioExtraction_Audio, - kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, - output_layout_size, - qt_audio_channel_layout); + if (err != noErr) + return; - err = MovieAudioExtractionGetProperty (extractor, - kQTPropertyClass_MovieAudioExtraction_Audio, - kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, - sizeof (inputStreamDesc), - &inputStreamDesc, 0); - if (err != noErr) - return; + bufferList->mNumberBuffers = 1; + bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; + bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16); - inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger - | kAudioFormatFlagIsPacked - | kAudioFormatFlagsNativeEndian; - inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8; - inputStreamDesc.mChannelsPerFrame = jmin ((UInt32) 2, inputStreamDesc.mChannelsPerFrame); - inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame; - inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame; + dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize); + bufferList->mBuffers[0].mData = dataBuffer; - err = MovieAudioExtractionSetProperty (extractor, - kQTPropertyClass_MovieAudioExtraction_Audio, - kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, - sizeof (inputStreamDesc), - &inputStreamDesc); - if (err != noErr) - return; + sampleRate = inputStreamDesc.mSampleRate; + bitsPerSample = 16; + numChannels = inputStreamDesc.mChannelsPerFrame; - Boolean allChannelsDiscrete = false; - err = MovieAudioExtractionSetProperty (extractor, - kQTPropertyClass_MovieAudioExtraction_Movie, - kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, - sizeof (allChannelsDiscrete), - &allChannelsDiscrete); - - if (err != noErr) - return; - - bufferList->mNumberBuffers = 1; - bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; - bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16); - - dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize); - bufferList->mBuffers[0].mData = dataBuffer; - - sampleRate = inputStreamDesc.mSampleRate; - bitsPerSample = 16; - numChannels = inputStreamDesc.mChannelsPerFrame; - - detachThread(); - ok = true; + detachThread(); + ok = true; + } } ~QTAudioReader() { JUCE_AUTORELEASEPOOL - checkThreadIsAttached(); - - if (dataHandle != nullptr) - DisposeHandle (dataHandle); - - if (extractor != nullptr) { - MovieAudioExtractionEnd (extractor); - extractor = nullptr; + checkThreadIsAttached(); + + if (dataHandle != nullptr) + DisposeHandle (dataHandle); + + if (extractor != nullptr) + { + MovieAudioExtractionEnd (extractor); + extractor = nullptr; + } + + DisposeMovie (movie); + + #if JUCE_MAC + ExitMoviesOnThread (); + #endif } - - DisposeMovie (movie); - - #if JUCE_MAC - ExitMoviesOnThread (); - #endif } bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { JUCE_AUTORELEASEPOOL - checkThreadIsAttached(); - bool ok = true; - - while (numSamples > 0) { - if (lastSampleRead != startSampleInFile) + checkThreadIsAttached(); + bool readOk = true; + + while (numSamples > 0) { - TimeRecord time; - time.scale = (TimeScale) inputStreamDesc.mSampleRate; - time.base = 0; - time.value.hi = 0; - time.value.lo = (UInt32) startSampleInFile; + if (lastSampleRead != startSampleInFile) + { + TimeRecord time; + time.scale = (TimeScale) inputStreamDesc.mSampleRate; + time.base = 0; + time.value.hi = 0; + time.value.lo = (UInt32) startSampleInFile; - OSStatus err = MovieAudioExtractionSetProperty (extractor, - kQTPropertyClass_MovieAudioExtraction_Movie, - kQTMovieAudioExtractionMoviePropertyID_CurrentTime, - sizeof (time), &time); + OSStatus err = MovieAudioExtractionSetProperty (extractor, + kQTPropertyClass_MovieAudioExtraction_Movie, + kQTMovieAudioExtractionMoviePropertyID_CurrentTime, + sizeof (time), &time); + if (err != noErr) + { + readOk = false; + break; + } + } + + int framesToDo = jmin (numSamples, (int) (bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame)); + bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * framesToDo; + + UInt32 outFlags = 0; + UInt32 actualNumFrames = framesToDo; + OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags); if (err != noErr) { - ok = false; + readOk = false; + break; + } + + lastSampleRead = startSampleInFile + actualNumFrames; + const int samplesReceived = actualNumFrames; + + for (int j = numDestChannels; --j >= 0;) + { + if (destSamples[j] != nullptr) + { + const short* src = ((const short*) bufferList->mBuffers[0].mData) + j; + + for (int i = 0; i < samplesReceived; ++i) + { + destSamples[j][startOffsetInDestBuffer + i] = (*src << 16); + src += numChannels; + } + } + } + + startOffsetInDestBuffer += samplesReceived; + startSampleInFile += samplesReceived; + numSamples -= samplesReceived; + + if (((outFlags & kQTMovieAudioExtractionComplete) != 0 || samplesReceived == 0) && numSamples > 0) + { + for (int j = numDestChannels; --j >= 0;) + if (destSamples[j] != nullptr) + zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * numSamples); + break; } } - int framesToDo = jmin (numSamples, (int) (bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame)); - bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * framesToDo; - - UInt32 outFlags = 0; - UInt32 actualNumFrames = framesToDo; - OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags); - if (err != noErr) - { - ok = false; - break; - } - - lastSampleRead = startSampleInFile + actualNumFrames; - const int samplesReceived = actualNumFrames; - - for (int j = numDestChannels; --j >= 0;) - { - if (destSamples[j] != nullptr) - { - const short* src = ((const short*) bufferList->mBuffers[0].mData) + j; - - for (int i = 0; i < samplesReceived; ++i) - { - destSamples[j][startOffsetInDestBuffer + i] = (*src << 16); - src += numChannels; - } - } - } - - startOffsetInDestBuffer += samplesReceived; - startSampleInFile += samplesReceived; - numSamples -= samplesReceived; - - if (((outFlags & kQTMovieAudioExtractionComplete) != 0 || samplesReceived == 0) && numSamples > 0) - { - for (int j = numDestChannels; --j >= 0;) - if (destSamples[j] != nullptr) - zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * numSamples); - - break; - } + detachThread(); + return readOk; } - - detachThread(); - return ok; } bool ok; @@ -339,13 +343,12 @@ private: #endif } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QTAudioReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QTAudioReader) }; //============================================================================== -QuickTimeAudioFormat::QuickTimeAudioFormat() - : AudioFormat (TRANS (quickTimeFormatName), StringArray (quickTimeExtensions)) +QuickTimeAudioFormat::QuickTimeAudioFormat() : AudioFormat (quickTimeFormatName, ".mov .mp3 .mp4 .m4a") { } @@ -363,7 +366,7 @@ bool QuickTimeAudioFormat::canDoMono() { return true; } AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) { - ScopedPointer r (new QTAudioReader (sourceStream, 0)); + ScopedPointer r (new QTAudioReader (sourceStream, 0)); if (r->ok) return r.release(); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.h index bee7467..6ef6a4e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_QuickTimeAudioFormat.h @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -63,7 +62,7 @@ public: private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QuickTimeAudioFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QuickTimeAudioFormat) }; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 029adfe..058ff73 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -1,31 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -//============================================================================== static const char* const wavFormatName = "WAV file"; -static const char* const wavExtensions[] = { ".wav", ".bwf", 0 }; //============================================================================== const char* const WavAudioFormat::bwavDescription = "bwav description"; @@ -39,7 +36,7 @@ const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history"; StringPairArray WavAudioFormat::createBWAVMetadata (const String& description, const String& originator, const String& originatorRef, - const Time& date, + const Time date, const int64 timeReferenceSamples, const String& codingHistory) { @@ -56,19 +53,27 @@ StringPairArray WavAudioFormat::createBWAVMetadata (const String& description, return m; } +const char* const WavAudioFormat::acidOneShot = "acid one shot"; +const char* const WavAudioFormat::acidRootSet = "acid root set"; +const char* const WavAudioFormat::acidStretch = "acid stretch"; +const char* const WavAudioFormat::acidDiskBased = "acid disk based"; +const char* const WavAudioFormat::acidizerFlag = "acidizer flag"; +const char* const WavAudioFormat::acidRootNote = "acid root note"; +const char* const WavAudioFormat::acidBeats = "acid beats"; +const char* const WavAudioFormat::acidDenominator = "acid denominator"; +const char* const WavAudioFormat::acidNumerator = "acid numerator"; +const char* const WavAudioFormat::acidTempo = "acid tempo"; + +const char* const WavAudioFormat::ISRC = "ISRC"; //============================================================================== namespace WavFileHelpers { inline int chunkName (const char* const name) noexcept { return (int) ByteOrder::littleEndianInt (name); } + inline size_t roundUpSize (size_t sz) noexcept { return (sz + 3) & ~3u; } #if JUCE_MSVC #pragma pack (push, 1) - #define PACKED - #elif JUCE_GCC - #define PACKED __attribute__((packed)) - #else - #define PACKED #endif struct BWAVChunk @@ -85,35 +90,35 @@ namespace WavFileHelpers uint8 reserved[190]; char codingHistory[1]; - void copyTo (StringPairArray& values) const + void copyTo (StringPairArray& values, const int totalSize) const { - values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, 256)); - values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, 32)); - values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, 32)); - values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, 10)); - values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, 8)); + values.set (WavAudioFormat::bwavDescription, String::fromUTF8 (description, sizeof (description))); + values.set (WavAudioFormat::bwavOriginator, String::fromUTF8 (originator, sizeof (originator))); + values.set (WavAudioFormat::bwavOriginatorRef, String::fromUTF8 (originatorRef, sizeof (originatorRef))); + values.set (WavAudioFormat::bwavOriginationDate, String::fromUTF8 (originationDate, sizeof (originationDate))); + values.set (WavAudioFormat::bwavOriginationTime, String::fromUTF8 (originationTime, sizeof (originationTime))); - const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow); + const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow); const uint32 timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh); const int64 time = (((int64)timeHigh) << 32) + timeLow; values.set (WavAudioFormat::bwavTimeReference, String (time)); - values.set (WavAudioFormat::bwavCodingHistory, String::fromUTF8 (codingHistory)); + values.set (WavAudioFormat::bwavCodingHistory, + String::fromUTF8 (codingHistory, totalSize - (int) offsetof (BWAVChunk, codingHistory))); } static MemoryBlock createFrom (const StringPairArray& values) { - const size_t sizeNeeded = sizeof (BWAVChunk) + values [WavAudioFormat::bwavCodingHistory].getNumBytesAsUTF8(); - MemoryBlock data ((sizeNeeded + 3) & ~3); + MemoryBlock data (roundUpSize (sizeof (BWAVChunk) + values [WavAudioFormat::bwavCodingHistory].getNumBytesAsUTF8())); data.fillWith (0); BWAVChunk* b = (BWAVChunk*) data.getData(); // Allow these calls to overwrite an extra byte at the end, which is fine as long // as they get called in the right order.. - values [WavAudioFormat::bwavDescription].copyToUTF8 (b->description, 257); - values [WavAudioFormat::bwavOriginator].copyToUTF8 (b->originator, 33); - values [WavAudioFormat::bwavOriginatorRef].copyToUTF8 (b->originatorRef, 33); + values [WavAudioFormat::bwavDescription] .copyToUTF8 (b->description, 257); + values [WavAudioFormat::bwavOriginator] .copyToUTF8 (b->originator, 33); + values [WavAudioFormat::bwavOriginatorRef] .copyToUTF8 (b->originatorRef, 33); values [WavAudioFormat::bwavOriginationDate].copyToUTF8 (b->originationDate, 11); values [WavAudioFormat::bwavOriginationTime].copyToUTF8 (b->originationTime, 9); @@ -136,8 +141,7 @@ namespace WavFileHelpers return MemoryBlock(); } - } PACKED; - + } JUCE_PACKED; //============================================================================== struct SMPLChunk @@ -150,7 +154,7 @@ namespace WavFileHelpers uint32 end; uint32 fraction; uint32 playCount; - } PACKED; + } JUCE_PACKED; uint32 manufacturer; uint32 product; @@ -163,33 +167,54 @@ namespace WavFileHelpers uint32 samplerData; SampleLoop loops[1]; + template + static void setValue (StringPairArray& values, NameType name, uint32 val) + { + values.set (name, String (ByteOrder::swapIfBigEndian (val))); + } + + static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) + { + setValue (values, "Loop" + String (prefix) + name, val); + } + void copyTo (StringPairArray& values, const int totalSize) const { - values.set ("Manufacturer", String (ByteOrder::swapIfBigEndian (manufacturer))); - values.set ("Product", String (ByteOrder::swapIfBigEndian (product))); - values.set ("SamplePeriod", String (ByteOrder::swapIfBigEndian (samplePeriod))); - values.set ("MidiUnityNote", String (ByteOrder::swapIfBigEndian (midiUnityNote))); - values.set ("MidiPitchFraction", String (ByteOrder::swapIfBigEndian (midiPitchFraction))); - values.set ("SmpteFormat", String (ByteOrder::swapIfBigEndian (smpteFormat))); - values.set ("SmpteOffset", String (ByteOrder::swapIfBigEndian (smpteOffset))); - values.set ("NumSampleLoops", String (ByteOrder::swapIfBigEndian (numSampleLoops))); - values.set ("SamplerData", String (ByteOrder::swapIfBigEndian (samplerData))); + setValue (values, "Manufacturer", manufacturer); + setValue (values, "Product", product); + setValue (values, "SamplePeriod", samplePeriod); + setValue (values, "MidiUnityNote", midiUnityNote); + setValue (values, "MidiPitchFraction", midiPitchFraction); + setValue (values, "SmpteFormat", smpteFormat); + setValue (values, "SmpteOffset", smpteOffset); + setValue (values, "NumSampleLoops", numSampleLoops); + setValue (values, "SamplerData", samplerData); - for (uint32 i = 0; i < numSampleLoops; ++i) + for (int i = 0; i < (int) numSampleLoops; ++i) { if ((uint8*) (loops + (i + 1)) > ((uint8*) this) + totalSize) break; - const String prefix ("Loop" + String(i)); - values.set (prefix + "Identifier", String (ByteOrder::swapIfBigEndian (loops[i].identifier))); - values.set (prefix + "Type", String (ByteOrder::swapIfBigEndian (loops[i].type))); - values.set (prefix + "Start", String (ByteOrder::swapIfBigEndian (loops[i].start))); - values.set (prefix + "End", String (ByteOrder::swapIfBigEndian (loops[i].end))); - values.set (prefix + "Fraction", String (ByteOrder::swapIfBigEndian (loops[i].fraction))); - values.set (prefix + "PlayCount", String (ByteOrder::swapIfBigEndian (loops[i].playCount))); + setValue (values, i, "Identifier", loops[i].identifier); + setValue (values, i, "Type", loops[i].type); + setValue (values, i, "Start", loops[i].start); + setValue (values, i, "End", loops[i].end); + setValue (values, i, "Fraction", loops[i].fraction); + setValue (values, i, "PlayCount", loops[i].playCount); } } + template + static uint32 getValue (const StringPairArray& values, NameType name, const char* def) + { + return ByteOrder::swapIfBigEndian ((uint32) values.getValue (name, def).getIntValue()); + } + + static uint32 getValue (const StringPairArray& values, int prefix, const char* name, const char* def) + { + return getValue (values, "Loop" + String (prefix) + name, def); + } + static MemoryBlock createFrom (const StringPairArray& values) { MemoryBlock data; @@ -197,36 +222,35 @@ namespace WavFileHelpers if (numLoops > 0) { - const size_t sizeNeeded = sizeof (SMPLChunk) + (numLoops - 1) * sizeof (SampleLoop); - data.setSize ((sizeNeeded + 3) & ~3, true); + data.setSize (roundUpSize (sizeof (SMPLChunk) + (size_t) (numLoops - 1) * sizeof (SampleLoop)), true); - SMPLChunk* const s = static_cast (data.getData()); + SMPLChunk* const s = static_cast (data.getData()); - s->manufacturer = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("Manufacturer", "0").getIntValue()); - s->product = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("Product", "0").getIntValue()); - s->samplePeriod = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("SamplePeriod", "0").getIntValue()); - s->midiUnityNote = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("MidiUnityNote", "60").getIntValue()); - s->midiPitchFraction = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("MidiPitchFraction", "0").getIntValue()); - s->smpteFormat = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("SmpteFormat", "0").getIntValue()); - s->smpteOffset = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("SmpteOffset", "0").getIntValue()); + s->manufacturer = getValue (values, "Manufacturer", "0"); + s->product = getValue (values, "Product", "0"); + s->samplePeriod = getValue (values, "SamplePeriod", "0"); + s->midiUnityNote = getValue (values, "MidiUnityNote", "60"); + s->midiPitchFraction = getValue (values, "MidiPitchFraction", "0"); + s->smpteFormat = getValue (values, "SmpteFormat", "0"); + s->smpteOffset = getValue (values, "SmpteOffset", "0"); s->numSampleLoops = ByteOrder::swapIfBigEndian ((uint32) numLoops); - s->samplerData = ByteOrder::swapIfBigEndian ((uint32) values.getValue ("SamplerData", "0").getIntValue()); + s->samplerData = getValue (values, "SamplerData", "0"); for (int i = 0; i < numLoops; ++i) { - const String prefix ("Loop" + String(i)); - s->loops[i].identifier = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Identifier", "0").getIntValue()); - s->loops[i].type = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Type", "0").getIntValue()); - s->loops[i].start = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Start", "0").getIntValue()); - s->loops[i].end = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "End", "0").getIntValue()); - s->loops[i].fraction = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Fraction", "0").getIntValue()); - s->loops[i].playCount = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "PlayCount", "0").getIntValue()); + SampleLoop& loop = s->loops[i]; + loop.identifier = getValue (values, i, "Identifier", "0"); + loop.type = getValue (values, i, "Type", "0"); + loop.start = getValue (values, i, "Start", "0"); + loop.end = getValue (values, i, "End", "0"); + loop.fraction = getValue (values, i, "Fraction", "0"); + loop.playCount = getValue (values, i, "PlayCount", "0"); } } return data; } - } PACKED; + } JUCE_PACKED; //============================================================================== struct InstChunk @@ -239,15 +263,25 @@ namespace WavFileHelpers int8 lowVelocity; int8 highVelocity; + static void setValue (StringPairArray& values, const char* name, int val) + { + values.set (name, String (val)); + } + void copyTo (StringPairArray& values) const { - values.set ("MidiUnityNote", String (baseNote)); - values.set ("Detune", String (detune)); - values.set ("Gain", String (gain)); - values.set ("LowNote", String (lowNote)); - values.set ("HighNote", String (highNote)); - values.set ("LowVelocity", String (lowVelocity)); - values.set ("HighVelocity", String (highVelocity)); + setValue (values, "MidiUnityNote", baseNote); + setValue (values, "Detune", detune); + setValue (values, "Gain", gain); + setValue (values, "LowNote", lowNote); + setValue (values, "HighNote", highNote); + setValue (values, "LowVelocity", lowVelocity); + setValue (values, "HighVelocity", highVelocity); + } + + static int8 getValue (const StringPairArray& values, const char* name, const char* def) + { + return (int8) values.getValue (name, def).getIntValue(); } static MemoryBlock createFrom (const StringPairArray& values) @@ -258,20 +292,20 @@ namespace WavFileHelpers if (keys.contains ("LowNote", true) && keys.contains ("HighNote", true)) { data.setSize (8, true); - InstChunk* const inst = static_cast (data.getData()); + InstChunk* const inst = static_cast (data.getData()); - inst->baseNote = (int8) values.getValue ("MidiUnityNote", "60").getIntValue(); - inst->detune = (int8) values.getValue ("Detune", "0").getIntValue(); - inst->gain = (int8) values.getValue ("Gain", "0").getIntValue(); - inst->lowNote = (int8) values.getValue ("LowNote", "0").getIntValue(); - inst->highNote = (int8) values.getValue ("HighNote", "127").getIntValue(); - inst->lowVelocity = (int8) values.getValue ("LowVelocity", "1").getIntValue(); - inst->highVelocity = (int8) values.getValue ("HighVelocity", "127").getIntValue(); + inst->baseNote = getValue (values, "MidiUnityNote", "60"); + inst->detune = getValue (values, "Detune", "0"); + inst->gain = getValue (values, "Gain", "0"); + inst->lowNote = getValue (values, "LowNote", "0"); + inst->highNote = getValue (values, "HighNote", "127"); + inst->lowVelocity = getValue (values, "LowVelocity", "1"); + inst->highVelocity = getValue (values, "HighVelocity", "127"); } return data; } - } PACKED; + } JUCE_PACKED; //============================================================================== struct CueChunk @@ -284,40 +318,44 @@ namespace WavFileHelpers uint32 chunkStart; uint32 blockStart; uint32 offset; - } PACKED; + } JUCE_PACKED; uint32 numCues; Cue cues[1]; + static void setValue (StringPairArray& values, int prefix, const char* name, uint32 val) + { + values.set ("Cue" + String (prefix) + name, String (ByteOrder::swapIfBigEndian (val))); + } + void copyTo (StringPairArray& values, const int totalSize) const { values.set ("NumCuePoints", String (ByteOrder::swapIfBigEndian (numCues))); - for (uint32 i = 0; i < numCues; ++i) + for (int i = 0; i < (int) numCues; ++i) { if ((uint8*) (cues + (i + 1)) > ((uint8*) this) + totalSize) break; - const String prefix ("Cue" + String(i)); - values.set (prefix + "Identifier", String (ByteOrder::swapIfBigEndian (cues[i].identifier))); - values.set (prefix + "Order", String (ByteOrder::swapIfBigEndian (cues[i].order))); - values.set (prefix + "ChunkID", String (ByteOrder::swapIfBigEndian (cues[i].chunkID))); - values.set (prefix + "ChunkStart", String (ByteOrder::swapIfBigEndian (cues[i].chunkStart))); - values.set (prefix + "BlockStart", String (ByteOrder::swapIfBigEndian (cues[i].blockStart))); - values.set (prefix + "Offset", String (ByteOrder::swapIfBigEndian (cues[i].offset))); + setValue (values, i, "Identifier", cues[i].identifier); + setValue (values, i, "Order", cues[i].order); + setValue (values, i, "ChunkID", cues[i].chunkID); + setValue (values, i, "ChunkStart", cues[i].chunkStart); + setValue (values, i, "BlockStart", cues[i].blockStart); + setValue (values, i, "Offset", cues[i].offset); } } - static void create (MemoryBlock& data, const StringPairArray& values) + static MemoryBlock createFrom (const StringPairArray& values) { + MemoryBlock data; const int numCues = values.getValue ("NumCuePoints", "0").getIntValue(); if (numCues > 0) { - const size_t sizeNeeded = sizeof (CueChunk) + (numCues - 1) * sizeof (Cue); - data.setSize ((sizeNeeded + 3) & ~3, true); + data.setSize (roundUpSize (sizeof (CueChunk) + (size_t) (numCues - 1) * sizeof (Cue)), true); - CueChunk* const c = static_cast (data.getData()); + CueChunk* const c = static_cast (data.getData()); c->numCues = ByteOrder::swapIfBigEndian ((uint32) numCues); @@ -331,44 +369,55 @@ namespace WavFileHelpers for (int i = 0; i < numCues; ++i) { const String prefix ("Cue" + String (i)); - - uint32 identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue(); + const uint32 identifier = (uint32) values.getValue (prefix + "Identifier", "0").getIntValue(); #if JUCE_DEBUG jassert (! identifiers.contains (identifier)); identifiers.add (identifier); #endif - c->cues[i].identifier = ByteOrder::swapIfBigEndian ((uint32) identifier); - const int order = values.getValue (prefix + "Order", String (nextOrder)).getIntValue(); nextOrder = jmax (nextOrder, order) + 1; - c->cues[i].order = ByteOrder::swapIfBigEndian ((uint32) order); - c->cues[i].chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue()); - c->cues[i].chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue()); - c->cues[i].blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue()); - c->cues[i].offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue()); + Cue& cue = c->cues[i]; + cue.identifier = ByteOrder::swapIfBigEndian ((uint32) identifier); + cue.order = ByteOrder::swapIfBigEndian ((uint32) order); + cue.chunkID = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkID", dataChunkID).getIntValue()); + cue.chunkStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "ChunkStart", "0").getIntValue()); + cue.blockStart = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "BlockStart", "0").getIntValue()); + cue.offset = ByteOrder::swapIfBigEndian ((uint32) values.getValue (prefix + "Offset", "0").getIntValue()); } } + + return data; } - } PACKED; + } JUCE_PACKED; //============================================================================== namespace ListChunk { + static int getValue (const StringPairArray& values, const String& name) + { + return values.getValue (name, "0").getIntValue(); + } + + static int getValue (const StringPairArray& values, const String& prefix, const char* name) + { + return getValue (values, prefix + name); + } + static void appendLabelOrNoteChunk (const StringPairArray& values, const String& prefix, const int chunkType, MemoryOutputStream& out) { const String label (values.getValue (prefix + "Text", prefix)); - const int labelLength = label.getNumBytesAsUTF8() + 1; + const int labelLength = (int) label.getNumBytesAsUTF8() + 1; const int chunkLength = 4 + labelLength + (labelLength & 1); out.writeInt (chunkType); out.writeInt (chunkLength); - out.writeInt (values.getValue (prefix + "Identifier", "0").getIntValue()); - out.write (label.toUTF8(), labelLength); + out.writeInt (getValue (values, prefix, "Identifier")); + out.write (label.toUTF8(), (size_t) labelLength); if ((out.getDataSize() & 1) != 0) out.writeByte (0); @@ -378,47 +427,145 @@ namespace WavFileHelpers { const String text (values.getValue (prefix + "Text", prefix)); - const int textLength = text.getNumBytesAsUTF8() + 1; // include null terminator + const int textLength = (int) text.getNumBytesAsUTF8() + 1; // include null terminator int chunkLength = textLength + 20 + (textLength & 1); out.writeInt (chunkName ("ltxt")); out.writeInt (chunkLength); - out.writeInt (values.getValue (prefix + "Identifier", "0").getIntValue()); - out.writeInt (values.getValue (prefix + "SampleLength", "0").getIntValue()); - out.writeInt (values.getValue (prefix + "Purpose", "0").getIntValue()); - out.writeShort ((short) values.getValue (prefix + "Country", "0").getIntValue()); - out.writeShort ((short) values.getValue (prefix + "Language", "0").getIntValue()); - out.writeShort ((short) values.getValue (prefix + "Dialect", "0").getIntValue()); - out.writeShort ((short) values.getValue (prefix + "CodePage", "0").getIntValue()); - out.write (text.toUTF8(), textLength); + out.writeInt (getValue (values, prefix, "Identifier")); + out.writeInt (getValue (values, prefix, "SampleLength")); + out.writeInt (getValue (values, prefix, "Purpose")); + out.writeShort ((short) getValue (values, prefix, "Country")); + out.writeShort ((short) getValue (values, prefix, "Language")); + out.writeShort ((short) getValue (values, prefix, "Dialect")); + out.writeShort ((short) getValue (values, prefix, "CodePage")); + out.write (text.toUTF8(), (size_t) textLength); if ((out.getDataSize() & 1) != 0) out.writeByte (0); } - static void create (MemoryBlock& block, const StringPairArray& values) + static MemoryBlock createFrom (const StringPairArray& values) { - const int numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue(); - const int numCueNotes = values.getValue ("NumCueNotes", "0").getIntValue(); - const int numCueRegions = values.getValue ("NumCueRegions", "0").getIntValue(); + const int numCueLabels = getValue (values, "NumCueLabels"); + const int numCueNotes = getValue (values, "NumCueNotes"); + const int numCueRegions = getValue (values, "NumCueRegions"); - if (numCueLabels > 0 || numCueNotes > 0 || numCueRegions > 0) + MemoryOutputStream out; + + if (numCueLabels + numCueNotes + numCueRegions > 0) { - MemoryOutputStream out (block, false); + out.writeInt (chunkName ("adtl")); - int i; - for (i = 0; i < numCueLabels; ++i) + for (int i = 0; i < numCueLabels; ++i) appendLabelOrNoteChunk (values, "CueLabel" + String (i), chunkName ("labl"), out); - for (i = 0; i < numCueNotes; ++i) + for (int i = 0; i < numCueNotes; ++i) appendLabelOrNoteChunk (values, "CueNote" + String (i), chunkName ("note"), out); - for (i = 0; i < numCueRegions; ++i) + for (int i = 0; i < numCueRegions; ++i) appendExtraChunk (values, "CueRegion" + String (i), out); } + + return out.getMemoryBlock(); } } + //============================================================================== + struct AcidChunk + { + /** Reads an acid RIFF chunk from a stream positioned just after the size byte. */ + AcidChunk (InputStream& input, size_t length) + { + zerostruct (*this); + input.read (this, (int) jmin (sizeof (*this), length)); + } + + void addToMetadata (StringPairArray& values) const + { + setBoolFlag (values, WavAudioFormat::acidOneShot, 0x01); + setBoolFlag (values, WavAudioFormat::acidRootSet, 0x02); + setBoolFlag (values, WavAudioFormat::acidStretch, 0x04); + setBoolFlag (values, WavAudioFormat::acidDiskBased, 0x08); + setBoolFlag (values, WavAudioFormat::acidizerFlag, 0x10); + + if (flags & 0x02) // root note set + values.set (WavAudioFormat::acidRootNote, String (rootNote)); + + values.set (WavAudioFormat::acidBeats, String (numBeats)); + values.set (WavAudioFormat::acidDenominator, String (meterDenominator)); + values.set (WavAudioFormat::acidNumerator, String (meterNumerator)); + values.set (WavAudioFormat::acidTempo, String (tempo)); + } + + void setBoolFlag (StringPairArray& values, const char* name, int32 mask) const + { + values.set (name, (flags & mask) ? "1" : "0"); + } + + int32 flags; + int16 rootNote; + int16 reserved1; + float reserved2; + int32 numBeats; + int16 meterDenominator; + int16 meterNumerator; + float tempo; + + } JUCE_PACKED; + + //============================================================================== + namespace AXMLChunk + { + static void addToMetadata (StringPairArray& destValues, const String& source) + { + ScopedPointer xml (XmlDocument::parse (source)); + + if (xml != nullptr && xml->hasTagName ("ebucore:ebuCoreMain")) + { + if (XmlElement* xml2 = xml->getChildByName ("ebucore:coreMetadata")) + { + if (XmlElement* xml3 = xml2->getChildByName ("ebucore:identifier")) + { + if (XmlElement* xml4 = xml3->getChildByName ("dc:identifier")) + { + const String ISRCCode (xml4->getAllSubText().fromFirstOccurrenceOf ("ISRC:", false, true)); + + if (ISRCCode.isNotEmpty()) + destValues.set (WavAudioFormat::ISRC, ISRCCode); + } + } + } + } + } + + static MemoryBlock createFrom (const StringPairArray& values) + { + const String ISRC (values.getValue (WavAudioFormat::ISRC, String::empty)); + MemoryOutputStream xml; + + if (ISRC.isNotEmpty()) + { + xml << "" + "" + "" + "ISRC:" << ISRC << "" + "" + "" + ""; + + xml.writeRepeatedByte (0, xml.getDataSize()); // ensures even size, null termination and room for future growing + } + + return xml.getMemoryBlock(); + } + }; + //============================================================================== struct ExtensibleWavSubFormat { @@ -426,7 +573,15 @@ namespace WavFileHelpers uint16 data2; uint16 data3; uint8 data4[8]; - } PACKED; + + bool operator== (const ExtensibleWavSubFormat& other) const noexcept { return memcmp (this, &other, sizeof (*this)) == 0; } + bool operator!= (const ExtensibleWavSubFormat& other) const noexcept { return ! operator== (other); } + + } JUCE_PACKED; + + static const ExtensibleWavSubFormat pcmFormat = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + static const ExtensibleWavSubFormat IEEEFloatFormat = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; + static const ExtensibleWavSubFormat ambisonicFormat = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } }; struct DataSize64Chunk // chunk ID = 'ds64' if data size > 0xffffffff, 'JUNK' otherwise { @@ -437,14 +592,11 @@ namespace WavFileHelpers uint32 sampleCountLow; // low 4 byte sample count of fact chunk uint32 sampleCountHigh; // high 4 byte sample count of fact chunk uint32 tableLength; // number of valid entries in array 'table' - } PACKED; - + } JUCE_PACKED; #if JUCE_MSVC #pragma pack (pop) #endif - - #undef PACKED } //============================================================================== @@ -452,7 +604,7 @@ class WavAudioFormatReader : public AudioFormatReader { public: WavAudioFormatReader (InputStream* const in) - : AudioFormatReader (in, TRANS (wavFormatName)), + : AudioFormatReader (in, wavFormatName), bwavChunkStart (0), bwavSize (0), dataLength (0), @@ -461,8 +613,6 @@ public: using namespace WavFileHelpers; uint64 len = 0; uint64 end = 0; - bool hasGotType = false; - bool hasGotData = false; int cueNoteIndex = 0; int cueLabelIndex = 0; int cueRegionIndex = 0; @@ -477,7 +627,7 @@ public: else if (firstChunkType == chunkName ("RIFF")) { len = (uint64) (uint32) input->readInt(); - end = input->getPosition() + len; + end = len + (uint64) input->getPosition(); } else { @@ -490,20 +640,16 @@ public: { if (isRF64 && input->readInt() == chunkName ("ds64")) { - uint32 length = (uint32) input->readInt(); + const uint32 length = (uint32) input->readInt(); if (length < 28) - { return; - } - else - { - const int64 chunkEnd = input->getPosition() + length + (length & 1); - len = (uint64) input->readInt64(); - end = startOfRIFFChunk + len; - dataLength = input->readInt64(); - input->setPosition (chunkEnd); - } + + const int64 chunkEnd = input->getPosition() + length + (length & 1); + len = (uint64) input->readInt64(); + end = len + (uint64) startOfRIFFChunk; + dataLength = input->readInt64(); + input->setPosition (chunkEnd); } while ((uint64) input->getPosition() < end && ! input->isExhausted()) @@ -525,7 +671,7 @@ public: if (bitsPerSample > 64) { bytesPerFrame = bytesPerSec / (int) sampleRate; - bitsPerSample = 8 * bytesPerFrame / numChannels; + bitsPerSample = 8 * (unsigned int) bytesPerFrame / numChannels; } else { @@ -544,32 +690,25 @@ public: } else { - input->skipNextBytes (10); // skip over bitsPerSample and speakerPosition mask + input->skipNextBytes (4); // skip over size and bitsPerSample + metadataValues.set ("ChannelMask", String (input->readInt())); + ExtensibleWavSubFormat subFormat; subFormat.data1 = (uint32) input->readInt(); subFormat.data2 = (uint16) input->readShort(); subFormat.data3 = (uint16) input->readShort(); input->read (subFormat.data4, sizeof (subFormat.data4)); - const ExtensibleWavSubFormat pcmFormat - = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - - if (memcmp (&subFormat, &pcmFormat, sizeof (subFormat)) != 0) - { - const ExtensibleWavSubFormat ambisonicFormat - = { 0x00000001, 0x0721, 0x11d3, { 0x86, 0x44, 0xC8, 0xC1, 0xCA, 0x00, 0x00, 0x00 } }; - - if (memcmp (&subFormat, &ambisonicFormat, sizeof (subFormat)) != 0) - bytesPerFrame = 0; - } + if (subFormat == IEEEFloatFormat) + usesFloatingPointData = true; + else if (subFormat != pcmFormat && subFormat != ambisonicFormat) + bytesPerFrame = 0; } } else if (format != 1) { bytesPerFrame = 0; } - - hasGotType = true; } else if (chunkType == chunkName ("data")) { @@ -578,8 +717,6 @@ public: dataChunkStart = input->getPosition(); lengthInSamples = (bytesPerFrame > 0) ? (dataLength / bytesPerFrame) : 0; - - hasGotData = true; } else if (chunkType == chunkName ("bext")) { @@ -589,7 +726,7 @@ public: HeapBlock bwav; bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); input->read (bwav, (int) length); - bwav->copyTo (metadataValues); + bwav->copyTo (metadataValues, (int) length); } else if (chunkType == chunkName ("smpl")) { @@ -612,6 +749,12 @@ public: input->read (cue, (int) length); cue->copyTo (metadataValues, (int) length); } + else if (chunkType == chunkName ("axml")) + { + MemoryBlock axml; + input->readIntoMemoryBlock (axml, (ssize_t) length); + AXMLChunk::addToMetadata (metadataValues, axml.toString()); + } else if (chunkType == chunkName ("LIST")) { if (input->readInt() == chunkName ("adtl")) @@ -669,6 +812,10 @@ public: } } } + else if (chunkType == chunkName ("acid")) + { + AcidChunk (*input, length).addToMetadata (metadataValues); + } else if (chunkEnd <= input->getPosition()) { break; @@ -678,27 +825,18 @@ public: } } - if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex)); - if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex)); - if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex)); - if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV"); + if (cueLabelIndex > 0) metadataValues.set ("NumCueLabels", String (cueLabelIndex)); + if (cueNoteIndex > 0) metadataValues.set ("NumCueNotes", String (cueNoteIndex)); + if (cueRegionIndex > 0) metadataValues.set ("NumCueRegions", String (cueRegionIndex)); + if (metadataValues.size() > 0) metadataValues.set ("MetaDataSource", "WAV"); } //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples) + int64 startSampleInFile, int numSamples) override { - jassert (destSamples != nullptr); - const int64 samplesAvailable = lengthInSamples - startSampleInFile; - - if (samplesAvailable < numSamples) - { - for (int i = numDestChannels; --i >= 0;) - if (destSamples[i] != nullptr) - zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * numSamples); - - numSamples = (int) samplesAvailable; - } + clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, lengthInSamples); if (numSamples <= 0) return true; @@ -719,15 +857,9 @@ public: zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead)); } - switch (bitsPerSample) - { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - case 32: if (usesFloatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); - else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, (int) numChannels, numThisTime); break; - default: jassertfalse; break; - } + copySampleData (bitsPerSample, usesFloatingPointData, + destSamples, startOffsetInDestBuffer, numDestChannels, + tempBuffer, (int) numChannels, numThisTime); startOffsetInDestBuffer += numThisTime; numSamples -= numThisTime; @@ -736,25 +868,38 @@ public: return true; } - int64 bwavChunkStart, bwavSize; + static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData, + int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels, + const void* sourceData, int numChannels, int numSamples) noexcept + { + switch (bitsPerSample) + { + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + case 32: if (usesFloatingPointData) ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); + else ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; + default: jassertfalse; break; + } + } -private: - ScopedPointer converter; - int bytesPerFrame; + int64 bwavChunkStart, bwavSize; int64 dataChunkStart, dataLength; + int bytesPerFrame; bool isRF64; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader); +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatReader) }; //============================================================================== class WavAudioFormatWriter : public AudioFormatWriter { public: - WavAudioFormatWriter (OutputStream* const out, const double sampleRate_, - const unsigned int numChannels_, const unsigned int bits, + WavAudioFormatWriter (OutputStream* const out, const double rate, + const unsigned int numChans, const unsigned int bits, const StringPairArray& metadataValues) - : AudioFormatWriter (out, TRANS (wavFormatName), sampleRate_, numChannels_, bits), + : AudioFormatWriter (out, wavFormatName, rate, numChans, bits), lengthInSamples (0), bytesWritten (0), writeFailed (false) @@ -769,10 +914,11 @@ public: jassert (metadataValues.getValue ("MetaDataSource", "None") != "AIFF"); bwavChunk = BWAVChunk::createFrom (metadataValues); + axmlChunk = AXMLChunk::createFrom (metadataValues); smplChunk = SMPLChunk::createFrom (metadataValues); instChunk = InstChunk::createFrom (metadataValues); - CueChunk ::create (cueChunk, metadataValues); - ListChunk::create (listChunk, metadataValues); + cueChunk = CueChunk ::createFrom (metadataValues); + listChunk = ListChunk::createFrom (metadataValues); } headerPosition = out->getPosition(); @@ -791,14 +937,15 @@ public: } //============================================================================== - bool write (const int** data, int numSamples) + bool write (const int** data, int numSamples) override { + jassert (numSamples >= 0); jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel! if (writeFailed) return false; - const size_t bytes = numChannels * numSamples * bitsPerSample / 8; + const size_t bytes = numChannels * (unsigned int) numSamples * bitsPerSample / 8; tempBlock.ensureSize (bytes, false); switch (bitsPerSample) @@ -810,7 +957,7 @@ public: default: jassertfalse; break; } - if (! output->write (tempBlock.getData(), (int) bytes)) + if (! output->write (tempBlock.getData(), bytes)) { // failed to write to disk, so let's try writing the header. // If it's just run out of disk space, then if it does manage @@ -819,18 +966,14 @@ public: writeFailed = true; return false; } - else - { - bytesWritten += bytes; - lengthInSamples += numSamples; - return true; - } + bytesWritten += bytes; + lengthInSamples += (uint64) numSamples; + return true; } private: - ScopedPointer converter; - MemoryBlock tempBlock, bwavChunk, smplChunk, instChunk, cueChunk, listChunk; + MemoryBlock tempBlock, bwavChunk, axmlChunk, smplChunk, instChunk, cueChunk, listChunk; uint64 lengthInSamples, bytesWritten; int64 headerPosition; bool writeFailed; @@ -840,11 +983,13 @@ private: switch (numChannels) { case 1: return 0; - case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT - case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT - case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT - case 7: return 1 + 2 + 4 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT - case 8: return 1 + 2 + 4 + 8 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT + case 2: return 1 + 2; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT + case 3: return 1 + 2 + 4; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER + case 4: return 1 + 2 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 5: return 1 + 2 + 4 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 6: return 1 + 2 + 4 + 8 + 16 + 32; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT + case 7: return 1 + 2 + 4 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT + case 8: return 1 + 2 + 4 + 8 + 16 + 32 + 512 + 1024; // SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT default: break; } @@ -864,50 +1009,66 @@ private: const size_t bytesPerFrame = numChannels * bitsPerSample / 8; uint64 audioDataSize = bytesPerFrame * lengthInSamples; - const bool isRF64 = (bytesWritten >= literal64bit (0x100000000)); + const bool isRF64 = (bytesWritten >= 0x100000000LL); const bool isWaveFmtEx = isRF64 || (numChannels > 2); - int64 riffChunkSize = 4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */ - + 8 + audioDataSize + (audioDataSize & 1) - + (bwavChunk.getSize() > 0 ? (8 + bwavChunk.getSize()) : 0) - + (smplChunk.getSize() > 0 ? (8 + smplChunk.getSize()) : 0) - + (instChunk.getSize() > 0 ? (8 + instChunk.getSize()) : 0) - + (cueChunk .getSize() > 0 ? (8 + cueChunk .getSize()) : 0) - + (listChunk.getSize() > 0 ? (12 + listChunk.getSize()) : 0) - + (8 + 28); // (ds64 chunk) + int64 riffChunkSize = (int64) (4 /* 'RIFF' */ + 8 + 40 /* WAVEFORMATEX */ + + 8 + audioDataSize + (audioDataSize & 1) + + chunkSize (bwavChunk) + + chunkSize (axmlChunk) + + chunkSize (smplChunk) + + chunkSize (instChunk) + + chunkSize (cueChunk) + + chunkSize (listChunk) + + (8 + 28)); // (ds64 chunk) - riffChunkSize += (riffChunkSize & 0x1); + riffChunkSize += (riffChunkSize & 1); + + if (isRF64) + writeChunkHeader (chunkName ("RF64"), -1); + else + writeChunkHeader (chunkName ("RIFF"), (int) riffChunkSize); - output->writeInt (chunkName (isRF64 ? "RF64" : "RIFF")); - output->writeInt (isRF64 ? -1 : (int) riffChunkSize); output->writeInt (chunkName ("WAVE")); if (! isRF64) { - output->writeInt (chunkName ("JUNK")); - output->writeInt (28 + (isWaveFmtEx? 0 : 24)); + #if ! JUCE_WAV_DO_NOT_PAD_HEADER_SIZE + /* NB: This junk chunk is added for padding, so that the header is a fixed size + regardless of whether it's RF64 or not. That way, we can begin recording a file, + and when it's finished, can go back and write either a RIFF or RF64 header, + depending on whether more than 2^32 samples were written. + + The JUCE_WAV_DO_NOT_PAD_HEADER_SIZE macro allows you to disable this feature in case + you need to create files for crappy WAV players with bugs that stop them skipping chunks + which they don't recognise. But DO NOT USE THIS option unless you really have no choice, + because it means that if you write more than 2^32 samples to the file, you'll corrupt it. + */ + writeChunkHeader (chunkName ("JUNK"), 28 + (isWaveFmtEx? 0 : 24)); output->writeRepeatedByte (0, 28 /* ds64 */ + (isWaveFmtEx? 0 : 24)); + #endif } else { - // write ds64 chunk - output->writeInt (chunkName ("ds64")); - output->writeInt (28); // chunk size for uncompressed data (no table) + #if JUCE_WAV_DO_NOT_PAD_HEADER_SIZE + // If you disable padding, then you MUST NOT write more than 2^32 samples to a file. + jassertfalse; + #endif + + writeChunkHeader (chunkName ("ds64"), 28); // chunk size for uncompressed data (no table) output->writeInt64 (riffChunkSize); - output->writeInt64 (audioDataSize); + output->writeInt64 ((int64) audioDataSize); output->writeRepeatedByte (0, 12); } - output->writeInt (chunkName ("fmt ")); - if (isWaveFmtEx) { - output->writeInt (40); // chunk size + writeChunkHeader (chunkName ("fmt "), 40); output->writeShort ((short) (uint16) 0xfffe); // WAVE_FORMAT_EXTENSIBLE } else { - output->writeInt (16); // chunk size + writeChunkHeader (chunkName ("fmt "), 16); output->writeShort (bitsPerSample < 32 ? (short) 1 /*WAVE_FORMAT_PCM*/ : (short) 3 /*WAVE_FORMAT_IEEE_FLOAT*/); } @@ -920,16 +1081,10 @@ private: if (isWaveFmtEx) { - output->writeShort (22); // cbSize (size of the extension) + output->writeShort (22); // cbSize (size of the extension) output->writeShort ((short) bitsPerSample); // wValidBitsPerSample output->writeInt (getChannelMask ((int) numChannels)); - const ExtensibleWavSubFormat pcmFormat - = { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - - const ExtensibleWavSubFormat IEEEFloatFormat - = { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; - const ExtensibleWavSubFormat& subFormat = bitsPerSample < 32 ? pcmFormat : IEEEFloatFormat; output->writeInt ((int) subFormat.data1); @@ -938,71 +1093,127 @@ private: output->write (subFormat.data4, sizeof (subFormat.data4)); } - if (bwavChunk.getSize() > 0) - { - output->writeInt (chunkName ("bext")); - output->writeInt ((int) bwavChunk.getSize()); - *output << bwavChunk; - } + writeChunk (bwavChunk, chunkName ("bext")); + writeChunk (axmlChunk, chunkName ("axml")); + writeChunk (smplChunk, chunkName ("smpl")); + writeChunk (instChunk, chunkName ("inst"), 7); + writeChunk (cueChunk, chunkName ("cue ")); + writeChunk (listChunk, chunkName ("LIST")); - if (smplChunk.getSize() > 0) - { - output->writeInt (chunkName ("smpl")); - output->writeInt ((int) smplChunk.getSize()); - *output << smplChunk; - } - - if (instChunk.getSize() > 0) - { - output->writeInt (chunkName ("inst")); - output->writeInt (7); - *output << instChunk; - } - - if (cueChunk.getSize() > 0) - { - output->writeInt (chunkName ("cue ")); - output->writeInt ((int) cueChunk.getSize()); - *output << cueChunk; - } - - if (listChunk.getSize() > 0) - { - output->writeInt (chunkName ("LIST")); - output->writeInt ((int) listChunk.getSize() + 4); - output->writeInt (chunkName ("adtl")); - *output << listChunk; - } - - output->writeInt (chunkName ("data")); - output->writeInt (isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); + writeChunkHeader (chunkName ("data"), isRF64 ? -1 : (int) (lengthInSamples * bytesPerFrame)); usesFloatingPointData = (bitsPerSample == 32); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter); + static size_t chunkSize (const MemoryBlock& data) noexcept { return data.getSize() > 0 ? (8 + data.getSize()) : 0; } + + void writeChunkHeader (int chunkType, int size) const + { + output->writeInt (chunkType); + output->writeInt (size); + } + + void writeChunk (const MemoryBlock& data, int chunkType, int size = 0) const + { + if (data.getSize() > 0) + { + writeChunkHeader (chunkType, size != 0 ? size : (int) data.getSize()); + *output << data; + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormatWriter) }; //============================================================================== -WavAudioFormat::WavAudioFormat() - : AudioFormat (TRANS (wavFormatName), StringArray (wavExtensions)) +class MemoryMappedWavReader : public MemoryMappedAudioFormatReader { -} +public: + MemoryMappedWavReader (const File& file, const WavAudioFormatReader& reader) + : MemoryMappedAudioFormatReader (file, reader, reader.dataChunkStart, + reader.dataLength, reader.bytesPerFrame) + { + } -WavAudioFormat::~WavAudioFormat() -{ -} + bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) override + { + clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, lengthInSamples); + + if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) + { + jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. + return false; + } + + WavAudioFormatReader::copySampleData (bitsPerSample, usesFloatingPointData, + destSamples, startOffsetInDestBuffer, numDestChannels, + sampleToPointer (startSampleInFile), (int) numChannels, numSamples); + return true; + } + + void readMaxLevels (int64 startSampleInFile, int64 numSamples, + float& min0, float& max0, float& min1, float& max1) override + { + if (numSamples <= 0) + { + min0 = max0 = min1 = max1 = 0; + return; + } + + if (map == nullptr || ! mappedSection.contains (Range (startSampleInFile, startSampleInFile + numSamples))) + { + jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. + + min0 = max0 = min1 = max1 = 0; + return; + } + + switch (bitsPerSample) + { + case 8: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 16: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 24: scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + case 32: if (usesFloatingPointData) scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); + else scanMinAndMax (startSampleInFile, numSamples, min0, max0, min1, max1); break; + default: jassertfalse; break; + } + } + +private: + template + void scanMinAndMax (int64 startSampleInFile, int64 numSamples, + float& min0, float& max0, float& min1, float& max1) const noexcept + { + scanMinAndMaxInterleaved (0, startSampleInFile, numSamples, min0, max0); + + if (numChannels > 1) + scanMinAndMaxInterleaved (1, startSampleInFile, numSamples, min1, max1); + else + min1 = max1 = 0; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedWavReader) +}; + +//============================================================================== +WavAudioFormat::WavAudioFormat() : AudioFormat (wavFormatName, ".wav .bwf") {} +WavAudioFormat::~WavAudioFormat() {} Array WavAudioFormat::getPossibleSampleRates() { - const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; - return Array (rates); + const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100, + 48000, 88200, 96000, 176400, 192000, 352800, 384000 }; + + return Array (rates, numElementsInArray (rates)); } Array WavAudioFormat::getPossibleBitDepths() { - const int depths[] = { 8, 16, 24, 32, 0 }; - return Array (depths); + const int depths[] = { 8, 16, 24, 32 }; + + return Array (depths, numElementsInArray (depths)); } bool WavAudioFormat::canDoStereo() { return true; } @@ -1011,9 +1222,9 @@ bool WavAudioFormat::canDoMono() { return true; } AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) { - ScopedPointer r (new WavAudioFormatReader (sourceStream)); + ScopedPointer r (new WavAudioFormatReader (sourceStream)); - if (r->sampleRate > 0) + if (r->sampleRate > 0 && r->numChannels > 0 && r->bytesPerFrame > 0) return r.release(); if (! deleteStreamIfOpeningFails) @@ -1022,12 +1233,26 @@ AudioFormatReader* WavAudioFormat::createReaderFor (InputStream* sourceStream, return nullptr; } +MemoryMappedAudioFormatReader* WavAudioFormat::createMemoryMappedReader (const File& file) +{ + if (FileInputStream* fin = file.createInputStream()) + { + WavAudioFormatReader reader (fin); + + if (reader.lengthInSamples > 0) + return new MemoryMappedWavReader (file, reader); + } + + return nullptr; +} + AudioFormatWriter* WavAudioFormat::createWriterFor (OutputStream* out, double sampleRate, unsigned int numChannels, int bitsPerSample, const StringPairArray& metadataValues, int /*qualityOptionIndex*/) { if (getPossibleBitDepths().contains (bitsPerSample)) - return new WavAudioFormatWriter (out, sampleRate, (int) numChannels, bitsPerSample, metadataValues); + return new WavAudioFormatWriter (out, sampleRate, (unsigned int) numChannels, + (unsigned int) bitsPerSample, metadataValues); return nullptr; } @@ -1039,17 +1264,17 @@ namespace WavFileHelpers TemporaryFile tempFile (file); WavAudioFormat wav; - ScopedPointer reader (wav.createReaderFor (file.createInputStream(), true)); + ScopedPointer reader (wav.createReaderFor (file.createInputStream(), true)); if (reader != nullptr) { - ScopedPointer outStream (tempFile.getFile().createOutputStream()); + ScopedPointer outStream (tempFile.getFile().createOutputStream()); if (outStream != nullptr) { - ScopedPointer writer (wav.createWriterFor (outStream, reader->sampleRate, - reader->numChannels, (int) reader->bitsPerSample, - metadata, 0)); + ScopedPointer writer (wav.createWriterFor (outStream, reader->sampleRate, + reader->numChannels, (int) reader->bitsPerSample, + metadata, 0)); if (writer != nullptr) { @@ -1071,7 +1296,7 @@ namespace WavFileHelpers bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPairArray& newMetadata) { using namespace WavFileHelpers; - ScopedPointer reader (static_cast (createReaderFor (wavFile.createInputStream(), true))); + ScopedPointer reader (static_cast (createReaderFor (wavFile.createInputStream(), true))); if (reader != nullptr) { diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h index c5d4b37..f80695c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -94,8 +93,6 @@ public: /** 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; @@ -108,26 +105,54 @@ public: static StringPairArray createBWAVMetadata (const String& description, const String& originator, const String& originatorRef, - const Time& dateAndTime, + const Time dateAndTime, const int64 timeReferenceSamples, const String& codingHistory); //============================================================================== - Array getPossibleSampleRates(); - Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidOneShot; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidRootSet; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidStretch; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidDiskBased; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidizerFlag; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidRootNote; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidBeats; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidDenominator; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidNumerator; + /** Metadata property name used when reading a WAV file with an acid chunk. */ + static const char* const acidTempo; + + //============================================================================== + /** Metadata property name used when reading an ISRC code from an AXML chunk. */ + static const char* const ISRC; + + //============================================================================== + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; //============================================================================== AudioFormatReader* createReaderFor (InputStream* sourceStream, - bool deleteStreamIfOpeningFails); + bool deleteStreamIfOpeningFails) override; + + MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& metadataValues, - int qualityOptionIndex); + int qualityOptionIndex) override; //============================================================================== /** Utility function to replace the metadata in a wav file with a new set of values. @@ -139,5 +164,5 @@ public: private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WavAudioFormat) }; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp index 0733b8c..f5f5664 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_WindowsMediaAudioFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -29,10 +28,9 @@ namespace WindowsMediaCodec class JuceIStream : public ComBaseClassHelper { public: - JuceIStream (InputStream& source_) noexcept - : source (source_) + JuceIStream (InputStream& in) noexcept + : ComBaseClassHelper (0), source (in) { - resetReferenceCount(); } JUCE_COMRESULT Commit (DWORD) { return S_OK; } @@ -113,11 +111,10 @@ public: return S_OK; } - private: InputStream& source; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceIStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceIStream) }; //============================================================================== @@ -130,13 +127,10 @@ class WMAudioReader : public AudioFormatReader public: WMAudioReader (InputStream* const input_) : AudioFormatReader (input_, TRANS (wmFormatName)), - wmvCoreLib ("Wmvcore.dll"), - currentPosition (0), - bufferStart (0), bufferEnd (0) + wmvCoreLib ("Wmvcore.dll") { - typedef HRESULT (*WMCreateSyncReaderType) (IUnknown*, DWORD, IWMSyncReader**); - WMCreateSyncReaderType wmCreateSyncReader = nullptr; - wmCreateSyncReader = (WMCreateSyncReaderType) wmvCoreLib.getFunction ("WMCreateSyncReader"); + JUCE_LOAD_WINAPI_FUNCTION (wmvCoreLib, WMCreateSyncReader, wmCreateSyncReader, + HRESULT, (IUnknown*, DWORD, IWMSyncReader**)) if (wmCreateSyncReader != nullptr) { @@ -161,67 +155,71 @@ public: ~WMAudioReader() { if (wmSyncReader != nullptr) - { wmSyncReader->Close(); - wmSyncReader = nullptr; - } } bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples) + int64 startSampleInFile, int numSamples) override { if (sampleRate <= 0) return false; checkCoInitialiseCalled(); - if (startSampleInFile != currentPosition) - { - currentPosition = startSampleInFile; - wmSyncReader->SetRange (((QWORD) startSampleInFile * 10000000) / (int) sampleRate, 0); - bufferStart = bufferEnd = 0; - } + const int stride = numChannels * sizeof (int16); while (numSamples > 0) { - if (bufferEnd <= bufferStart) + if (! bufferedRange.contains (startSampleInFile)) { - INSSBuffer* sampleBuffer = nullptr; + const bool hasJumped = (startSampleInFile != bufferedRange.getEnd()); + + if (hasJumped) + wmSyncReader->SetRange ((QWORD) (startSampleInFile * 10000000 / (int64) sampleRate), 0); + + ComSmartPtr sampleBuffer; QWORD sampleTime, duration; DWORD flags, outputNum; WORD streamNum; - HRESULT hr = wmSyncReader->GetNextSample (0, &sampleBuffer, &sampleTime, - &duration, &flags, &outputNum, &streamNum); + HRESULT hr = wmSyncReader->GetNextSample (1, sampleBuffer.resetAndGetPointerAddress(), + &sampleTime, &duration, &flags, &outputNum, &streamNum); - if (SUCCEEDED (hr)) + if (sampleBuffer != nullptr) { BYTE* rawData = nullptr; DWORD dataLength = 0; hr = sampleBuffer->GetBufferAndLength (&rawData, &dataLength); - jassert (SUCCEEDED (hr)); - bufferStart = 0; - bufferEnd = (int) dataLength; - - if (bufferEnd <= 0) + if (dataLength == 0) return false; - buffer.ensureSize (bufferEnd); - memcpy (buffer.getData(), rawData, bufferEnd); + if (hasJumped) + bufferedRange.setStart ((int64) ((sampleTime * (int64) sampleRate) / 10000000)); + else + bufferedRange.setStart (bufferedRange.getEnd()); // (because the positions returned often aren't continguous) + + bufferedRange.setLength ((int64) (dataLength / stride)); + + buffer.ensureSize ((int) dataLength); + memcpy (buffer.getData(), rawData, (size_t) dataLength); + } + else if (hr == NS_E_NO_MORE_SAMPLES) + { + bufferedRange.setStart (startSampleInFile); + bufferedRange.setLength (256); + buffer.ensureSize (256 * stride); + buffer.fillWith (0); } else { - bufferStart = 0; - bufferEnd = 512; - buffer.ensureSize (bufferEnd); - buffer.fillWith (0); + return false; } } - const int stride = numChannels * sizeof (int16); - const int16* const rawData = static_cast (addBytesToPointer (buffer.getData(), bufferStart)); - const int numToDo = jmin (numSamples, (bufferEnd - bufferStart) / stride); + const int offsetInBuffer = (int) (startSampleInFile - bufferedRange.getStart()); + const int16* const rawData = static_cast (addBytesToPointer (buffer.getData(), offsetInBuffer * stride)); + const int numToDo = jmin (numSamples, (int) (bufferedRange.getLength() - offsetInBuffer)); for (int i = 0; i < numDestChannels; ++i) { @@ -238,10 +236,9 @@ public: } } - bufferStart += numToDo * stride; + startSampleInFile += numToDo; startOffsetInDestBuffer += numToDo; numSamples -= numToDo; - currentPosition += numToDo; } return true; @@ -250,9 +247,8 @@ public: private: DynamicLibrary wmvCoreLib; ComSmartPtr wmSyncReader; - int64 currentPosition; MemoryBlock buffer; - int bufferStart, bufferEnd; + Range bufferedRange; void checkCoInitialiseCalled() { @@ -310,14 +306,15 @@ private: } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WMAudioReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WMAudioReader) }; } //============================================================================== WindowsMediaAudioFormat::WindowsMediaAudioFormat() - : AudioFormat (TRANS (WindowsMediaCodec::wmFormatName), StringArray (WindowsMediaCodec::extensions)) + : AudioFormat (TRANS (WindowsMediaCodec::wmFormatName), + StringArray (WindowsMediaCodec::extensions)) { } @@ -328,6 +325,7 @@ Array WindowsMediaAudioFormat::getPossibleBitDepths() { return Array getPossibleSampleRates(); - Array getPossibleBitDepths(); - bool canDoStereo(); - bool canDoMono(); + Array getPossibleSampleRates() override; + Array getPossibleBitDepths() override; + bool canDoStereo() override; + bool canDoMono() override; + bool isCompressed() override; //============================================================================== - AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails); + AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails) override; AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, - const StringPairArray& metadataValues, int qualityOptionIndex); + const StringPairArray& metadataValues, int qualityOptionIndex) override; }; #endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.c index 4e95566..6d9a386 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/bitrate.c @@ -39,16 +39,16 @@ void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){ bm->short_per_long=ci->blocksizes[1]/ci->blocksizes[0]; bm->managed=1; - bm->avg_bitsper= rint(1.*bi->avg_rate*halfsamples/ratesamples); - bm->min_bitsper= rint(1.*bi->min_rate*halfsamples/ratesamples); - bm->max_bitsper= rint(1.*bi->max_rate*halfsamples/ratesamples); + bm->avg_bitsper= (int) rint(1.*bi->avg_rate*halfsamples/ratesamples); + bm->min_bitsper= (int) rint(1.*bi->min_rate*halfsamples/ratesamples); + bm->max_bitsper= (int) rint(1.*bi->max_rate*halfsamples/ratesamples); bm->avgfloat=PACKETBLOBS/2; /* not a necessary fix, but one that leads to a more balanced typical initialization */ { - long desired_fill=bi->reservoir_bits*bi->reservoir_bias; + long desired_fill = (long) (bi->reservoir_bits*bi->reservoir_bias); bm->minmax_reservoir=desired_fill; bm->avg_reservoir=desired_fill; } @@ -80,12 +80,12 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; bitrate_manager_info *bi=&ci->bi; - int choice=rint(bm->avgfloat); + int choice = (int) rint(bm->avgfloat); long this_bits=oggpack_bytes(vbi->packetblob[choice])*8; long min_target_bits=(vb->W?bm->min_bitsper*bm->short_per_long:bm->min_bitsper); long max_target_bits=(vb->W?bm->max_bitsper*bm->short_per_long:bm->max_bitsper); int samples=ci->blocksizes[vb->W]>>1; - long desired_fill=bi->reservoir_bits*bi->reservoir_bias; + long desired_fill = (long) (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 */ @@ -132,7 +132,7 @@ int vorbis_bitrate_addblock(vorbis_block *vb){ slew=rint(choice-bm->avgfloat)/samples*vi->rate; if(slew<-slewlimit)slew=-slewlimit; if(slew>slewlimit)slew=slewlimit; - choice=rint(bm->avgfloat+= slew/vi->rate*samples); + choice = (int) rint(bm->avgfloat+= slew/vi->rate*samples); this_bits=oggpack_bytes(vbi->packetblob[choice])*8; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/block.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/block.c index 614302c..ba1acae 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/block.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/block.c @@ -169,7 +169,6 @@ int vorbis_block_clear(vorbis_block *vb){ The init is here because some of it is shared */ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ - int i; codec_setup_info *ci=(codec_setup_info*)vi->codec_setup; private_state *b=NULL; int hs; @@ -206,12 +205,12 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ /* finish the codebooks */ if(!ci->fullbooks){ ci->fullbooks=(codebook*) _ogg_calloc(ci->books,sizeof(*ci->fullbooks)); - for(i=0;ibooks;i++) + for(int i=0;ibooks;i++) vorbis_book_init_encode(ci->fullbooks+i,ci->book_param[i]); } b->psy=(vorbis_look_psy*)_ogg_calloc(ci->psys,sizeof(*b->psy)); - for(i=0;ipsys;i++){ + for(int i=0;ipsys;i++){ _vp_psy_init(b->psy+i, ci->psy_param[i], &ci->psy_g_param, @@ -224,7 +223,7 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ /* finish the codebooks */ if(!ci->fullbooks){ ci->fullbooks=(codebook*) _ogg_calloc(ci->books,sizeof(*ci->fullbooks)); - for(i=0;ibooks;i++){ + for(int i=0;ibooks;i++){ if(ci->book_param[i]==NULL) goto abort_books; if(vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i])) @@ -261,17 +260,17 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ b->flr=(vorbis_look_floor**)_ogg_calloc(ci->floors,sizeof(*b->flr)); b->residue=(vorbis_look_residue**)_ogg_calloc(ci->residues,sizeof(*b->residue)); - for(i=0;ifloors;i++) + for(int i=0;ifloors;i++) b->flr[i]=_floor_P[ci->floor_type[i]]-> look(v,ci->floor_param[i]); - for(i=0;iresidues;i++) + for(int i=0;iresidues;i++) b->residue[i]=_residue_P[ci->residue_type[i]]-> look(v,ci->residue_param[i]); return 0; abort_books: - for(i=0;ibooks;i++){ + for(int i=0;ibooks;i++){ if(ci->book_param[i]!=NULL){ vorbis_staticbook_destroy(ci->book_param[i]); ci->book_param[i]=NULL; @@ -850,7 +849,7 @@ int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ 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 */ - long extra=b->sample_count-vb->granulepos; + long extra = (long) (b->sample_count-vb->granulepos); /* we use ogg_int64_t for granule positions because a uint64 isn't universally available. Unfortunately, @@ -958,7 +957,6 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ int n=ci->blocksizes[v->W]>>(hs+1); int n0=ci->blocksizes[0]>>(hs+1); int n1=ci->blocksizes[1]>>(hs+1); - int i,j; if(v->pcm_returned<0)return 0; @@ -975,9 +973,9 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ if(v->centerW==n1){ /* the data buffer wraps; swap the halves */ /* slow, sure, small */ - for(j=0;jchannels;j++){ + for(int j=0;jchannels;j++){ float *p=v->pcm[j]; - for(i=0;ilW^v->W)==1){ /* long/short or short/long */ - for(j=0;jchannels;j++){ + for(int j=0;jchannels;j++){ float *s=v->pcm[j]; float *d=v->pcm[j]+(n1-n0)/2; - for(i=(n1+n0)/2-1;i>=0;--i) + for(int i=(n1+n0)/2-1;i>=0;--i) d[i]=s[i]; } v->pcm_returned+=(n1-n0)/2; @@ -1003,10 +1001,10 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ }else{ if(v->lW==0){ /* short/short */ - for(j=0;jchannels;j++){ + for(int j=0;jchannels;j++){ float *s=v->pcm[j]; float *d=v->pcm[j]+n1-n0; - for(i=n0-1;i>=0;--i) + for(int i=n0-1;i>=0;--i) d[i]=s[i]; } v->pcm_returned+=n1-n0; @@ -1015,8 +1013,7 @@ int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ } if(pcm){ - int i; - for(i=0;ichannels;i++) + for(int i=0;ichannels;i++) v->pcmret[i]=v->pcm[i]+v->pcm_returned; *pcm=v->pcmret; } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor0.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor0.c index fd9cf65..4eac09c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor0.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor0.c @@ -144,7 +144,7 @@ static void floor0_map_lazy_init(vorbis_block *vb, } } -static vorbis_look_floor *floor0_look(vorbis_dsp_state *vd, +static vorbis_look_floor *floor0_look(vorbis_dsp_state* /* vd */, vorbis_info_floor *i){ vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; vorbis_look_floor0 *look=(vorbis_look_floor0*)_ogg_calloc(1,sizeof(*look)); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c index 52387c3..8ea1978 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/floor1.c @@ -175,7 +175,7 @@ static vorbis_info_floor *floor1_unpack (vorbis_info *vi,oggpack_buffer *opb){ return(NULL); } -static vorbis_look_floor *floor1_look(vorbis_dsp_state *vd, +static vorbis_look_floor *floor1_look(vorbis_dsp_state* /* vd */, vorbis_info_floor *in){ int *sortpointer[VIF_POSIT+2]; @@ -471,7 +471,7 @@ static int fit_line(lsfit_acc *a,int fits,int *y0,int *y1, xb+= x0; yb+= *y0; x2b+= x0 * x0; - y2b+= *y0 * *y0; + //y2b+= *y0 * *y0; xyb+= *y0 * x0; bn++; } @@ -480,7 +480,7 @@ static int fit_line(lsfit_acc *a,int fits,int *y0,int *y1, xb+= x1; yb+= *y1; x2b+= x1 * x1; - y2b+= *y1 * *y1; + //y2b+= *y1 * *y1; xyb+= *y1 * x1; bn++; } @@ -1016,7 +1016,7 @@ static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ } } - fit_value[i]=val+predicted&0x7fff; + fit_value[i] = (val + predicted) & 0x7fff; fit_value[look->loneighbor[i-2]]&=0x7fff; fit_value[look->hineighbor[i-2]]&=0x7fff; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.c index 8e56690..a886241 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/lsp.c @@ -309,7 +309,6 @@ static int JUCE_CDECL comp(const void *a,const void *b){ #define EPSILON 10e-7 static int Laguerre_With_Deflation(float *a,int ord,float *r){ int i,m; - double lastdelta=0.f; double *defl=(double*)alloca(sizeof(*defl)*(ord+1)); for(i=0;i<=ord;i++)defl[i]=a[i]; @@ -346,7 +345,6 @@ static int Laguerre_With_Deflation(float *a,int ord,float *r){ if(delta<0.f)delta*=-1; if(fabs(delta/newx)<10e-12)break; - lastdelta=delta; } r[m-1]=newx; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h index e84035f..6217472 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/os.h @@ -94,7 +94,7 @@ typedef ogg_int16_t vorbis_fpu_control; static inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ ogg_int16_t ret; - ogg_int16_t temp; + ogg_int16_t temp = 0; __asm__ __volatile__("fnstcw %0\n\t" "movw %0,%%dx\n\t" "andw $62463,%%dx\n\t" @@ -155,10 +155,10 @@ static __inline int vorbis_ftoi(double f){ return _mm_cvtsd_si32(_mm_load_sd(&f)); } -static __inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ +static __inline void vorbis_fpu_setround(vorbis_fpu_control*){ } -static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ +static __inline void vorbis_fpu_restore(vorbis_fpu_control){ } #endif /* Special MSVC x64 implementation */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.c index 0dac94c..a60f88f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/psy.c @@ -52,7 +52,7 @@ void _vp_global_free(vorbis_look_psy_global *look){ } } -static void _vi_gpsy_free(vorbis_info_psy_global *i){ +static inline void _vi_gpsy_free(vorbis_info_psy_global *i){ if(i){ memset(i,0,sizeof(*i)); _ogg_free(i); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/res0.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/res0.c index c664079..67cfe13 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/res0.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/res0.c @@ -393,7 +393,7 @@ static int local_book_besterror(codebook *book,int *a){ } static int _encodepart(oggpack_buffer *opb,int *vec, int n, - codebook *book,long *acc){ + codebook *book,long* /* acc */){ int i,bits=0; int dim=book->dim; int step=n/dim; @@ -426,7 +426,7 @@ static long **_01class(vorbis_block *vb,vorbis_look_residue *vl, int partvals=n/samples_per_partition; long **partword=(long**)_vorbis_block_alloc(vb,ch*sizeof(*partword)); - float scale=100./samples_per_partition; + float scale=100.0f/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 @@ -536,12 +536,12 @@ static long **_2class(vorbis_block *vb,vorbis_look_residue *vl,int **in, } static int _01forward(oggpack_buffer *opb, - vorbis_block *vb,vorbis_look_residue *vl, + vorbis_block*, vorbis_look_residue *vl, int **in,int ch, long **partword, int (*encode)(oggpack_buffer *,int *,int, codebook *,long *), - int submap){ + int /* submap */){ long i,j,k,s; vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; vorbis_info_residue0 *info=look->info; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c index 5c344dd..79b6688 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/libvorbis-1.3.2/lib/vorbisfile.c @@ -853,7 +853,7 @@ static int _fetch_and_process_packet(OggVorbis_File *vf, if(ret)return(ret); vf->current_serialno=vf->os.serialno; vf->current_link++; - link=0; + //link=0; } } } @@ -1561,6 +1561,7 @@ int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ break; }else result=ogg_stream_packetout(&vf->os,NULL); + (void) result; } } } @@ -2158,6 +2159,8 @@ static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd, memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); lapcount+=samples; } + + (void) lapcount; } } diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h index 5e7113f..d9bd13e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/oggvorbis/ogg.h @@ -42,7 +42,7 @@ typedef struct { } ogg_page; -static ogg_uint32_t ogg_bitreverse(ogg_uint32_t x){ +static inline ogg_uint32_t ogg_bitreverse(ogg_uint32_t x){ x= ((x>>16)&0x0000ffffUL) | ((x<<16)&0xffff0000UL); x= ((x>> 8)&0x00ff00ffUL) | ((x<< 8)&0xff00ff00UL); x= ((x>> 4)&0x0f0f0f0fUL) | ((x<< 4)&0xf0f0f0f0UL); diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp index b5d2b3c..ad87176 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.cpp @@ -1,31 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioFormat::AudioFormat (const String& name, const StringArray& extensions) - : formatName (name), - fileExtensions (extensions) +AudioFormat::AudioFormat (String name, StringArray extensions) + : formatName (name), fileExtensions (extensions) +{ +} + +AudioFormat::AudioFormat (StringRef name, StringRef extensions) + : formatName (name.text), fileExtensions (StringArray::fromTokens (extensions, false)) { } @@ -33,7 +36,6 @@ AudioFormat::~AudioFormat() { } -//============================================================================== bool AudioFormat::canHandleFile (const File& f) { for (int i = 0; i < fileExtensions.size(); ++i) @@ -47,3 +49,8 @@ const String& AudioFormat::getFormatName() const { return formatN const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; } bool AudioFormat::isCompressed() { return false; } StringArray AudioFormat::getQualityOptions() { return StringArray(); } + +MemoryMappedAudioFormatReader* AudioFormat::createMemoryMappedReader (const File&) +{ + return nullptr; +} diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h index 7c34b7a..196d436 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormat.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOFORMAT_JUCEHEADER__ -#define __JUCE_AUDIOFORMAT_JUCEHEADER__ - -#include "juce_AudioFormatReader.h" -#include "juce_AudioFormatWriter.h" +#ifndef JUCE_AUDIOFORMAT_H_INCLUDED +#define JUCE_AUDIOFORMAT_H_INCLUDED //============================================================================== @@ -46,7 +42,6 @@ public: //============================================================================== /** Returns the name of this format. - e.g. "WAV file" or "AIFF file" */ const String& getFormatName() const; @@ -113,6 +108,11 @@ public: virtual AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) = 0; + /** Attempts to create a MemoryMappedAudioFormatReader, if possible for this format. + If the format does not support this, the method will return nullptr; + */ + virtual MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file); + /** 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 @@ -156,11 +156,17 @@ 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() + @param fileExtensions an array of file extensions - these will be returned by getFileExtensions() */ - AudioFormat (const String& formatName, - const StringArray& fileExtensions); + AudioFormat (String formatName, StringArray fileExtensions); + + /** Creates an AudioFormat object. + + @param formatName this sets the value that will be returned by getFormatName() + @param fileExtensions a whitespace-separated list of file extensions - these will + be returned by getFileExtensions() + */ + AudioFormat (StringRef formatName, StringRef fileExtensions); private: //============================================================================== @@ -169,4 +175,4 @@ private: }; -#endif // __JUCE_AUDIOFORMAT_JUCEHEADER__ +#endif // JUCE_AUDIOFORMAT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp index a4bda4c..f0196c8 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.cpp @@ -1,36 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioFormatManager::AudioFormatManager() - : defaultFormatIndex (0) -{ -} - -AudioFormatManager::~AudioFormatManager() -{ -} +AudioFormatManager::AudioFormatManager() : defaultFormatIndex (0) {} +AudioFormatManager::~AudioFormatManager() {} //============================================================================== void AudioFormatManager::registerFormat (AudioFormat* newFormat, const bool makeThisTheDefaultFormat) @@ -71,13 +64,15 @@ void AudioFormatManager::registerBasicFormats() #if JUCE_MAC || JUCE_IOS registerFormat (new CoreAudioFormat(), false); - #elif JUCE_USE_WINDOWS_MEDIA_FORMAT - registerFormat (new WindowsMediaAudioFormat(), false); - #elif JUCE_USE_MP3AUDIOFORMAT - // The software MP3 decoder is only used as a default format if - // there isn't an OS-provided alternative. + #endif + + #if JUCE_USE_MP3AUDIOFORMAT registerFormat (new MP3AudioFormat(), false); #endif + + #if JUCE_USE_WINDOWS_MEDIA_FORMAT + registerFormat (new WindowsMediaAudioFormat(), false); + #endif } void AudioFormatManager::clearFormats() @@ -117,14 +112,13 @@ String AudioFormatManager::getWildcardForAllFormats() const { StringArray extensions; - int i; - for (i = 0; i < getNumKnownFormats(); ++i) + for (int i = 0; i < getNumKnownFormats(); ++i) extensions.addArray (getKnownFormat(i)->getFileExtensions()); extensions.trim(); extensions.removeEmptyStrings(); - for (i = 0; i < extensions.size(); ++i) + for (int i = 0; i < extensions.size(); ++i) extensions.set (i, (extensions[i].startsWithChar ('.') ? "*" : "*.") + extensions[i]); extensions.removeDuplicates (true); @@ -143,17 +137,9 @@ AudioFormatReader* AudioFormatManager::createReaderFor (const File& file) AudioFormat* const af = getKnownFormat(i); if (af->canHandleFile (file)) - { - InputStream* const in = file.createInputStream(); - - if (in != nullptr) - { - AudioFormatReader* const r = af->createReaderFor (in, true); - - if (r != nullptr) + if (InputStream* const in = file.createInputStream()) + if (AudioFormatReader* const r = af->createReaderFor (in, true)) return r; - } - } } return nullptr; @@ -173,9 +159,7 @@ AudioFormatReader* AudioFormatManager::createReaderFor (InputStream* audioFileSt for (int i = 0; i < getNumKnownFormats(); ++i) { - AudioFormatReader* const r = getKnownFormat(i)->createReaderFor (in, false); - - if (r != nullptr) + if (AudioFormatReader* const r = getKnownFormat(i)->createReaderFor (in, false)) { in.release(); return r; diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h index 3705600..f560347 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatManager.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ -#define __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ - -#include "juce_AudioFormat.h" +#ifndef JUCE_AUDIOFORMATMANAGER_H_INCLUDED +#define JUCE_AUDIOFORMATMANAGER_H_INCLUDED //============================================================================== @@ -80,6 +77,12 @@ public: /** Returns one of the registered file formats. */ AudioFormat* getKnownFormat (int index) const; + /** Iterator access to the list of known formats. */ + AudioFormat** begin() const noexcept { return knownFormats.begin(); } + + /** Iterator access to the list of known formats. */ + AudioFormat** end() const noexcept { return knownFormats.end(); } + /** Looks for which of the known formats is listed as being for a given file extension. @@ -133,8 +136,8 @@ private: OwnedArray knownFormats; int defaultFormatIndex; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatManager); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatManager) }; -#endif // __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ +#endif // JUCE_AUDIOFORMATMANAGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp index 4389a8d..501e339 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.cpp @@ -1,37 +1,35 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioFormatReader::AudioFormatReader (InputStream* const in, - const String& formatName_) +AudioFormatReader::AudioFormatReader (InputStream* const in, const String& name) : sampleRate (0), bitsPerSample (0), lengthInSamples (0), numChannels (0), usesFloatingPointData (false), input (in), - formatName (formatName_) + formatName (name) { } @@ -102,6 +100,18 @@ bool AudioFormatReader::read (int* const* destSamples, return true; } +static void readChannels (AudioFormatReader& reader, + int** const chans, AudioSampleBuffer* const buffer, + const int startSample, const int numSamples, + const int64 readerStartSample, const int numTargetChannels) +{ + for (int j = 0; j < numTargetChannels; ++j) + chans[j] = reinterpret_cast (buffer->getWritePointer (j, startSample)); + + chans[numTargetChannels] = nullptr; + reader.read (chans, numTargetChannels, readerStartSample, numSamples, true); +} + void AudioFormatReader::read (AudioSampleBuffer* buffer, int startSample, int numSamples, @@ -115,56 +125,87 @@ void AudioFormatReader::read (AudioSampleBuffer* buffer, if (numSamples > 0) { const int numTargetChannels = buffer->getNumChannels(); - int* chans[3]; - if (useReaderLeftChan == useReaderRightChan) + if (numTargetChannels <= 2) { - chans[0] = reinterpret_cast (buffer->getSampleData (0, startSample)); - chans[1] = (numChannels > 1 && numTargetChannels > 1) ? reinterpret_cast (buffer->getSampleData (1, startSample)) : nullptr; - } - else if (useReaderLeftChan || (numChannels == 1)) - { - chans[0] = reinterpret_cast (buffer->getSampleData (0, startSample)); - chans[1] = nullptr; - } - else if (useReaderRightChan) - { - chans[0] = nullptr; - chans[1] = reinterpret_cast (buffer->getSampleData (0, startSample)); - } + int* const dest0 = reinterpret_cast (buffer->getWritePointer (0, startSample)); + int* const dest1 = reinterpret_cast (numTargetChannels > 1 ? buffer->getWritePointer (1, startSample) : nullptr); + int* chans[3]; - chans[2] = nullptr; + if (useReaderLeftChan == useReaderRightChan) + { + chans[0] = dest0; + chans[1] = numChannels > 1 ? dest1 : nullptr; + } + else if (useReaderLeftChan || (numChannels == 1)) + { + chans[0] = dest0; + chans[1] = nullptr; + } + else if (useReaderRightChan) + { + chans[0] = nullptr; + chans[1] = dest0; + } - read (chans, 2, readerStartSample, numSamples, true); + chans[2] = nullptr; + read (chans, 2, readerStartSample, numSamples, true); + + // if the target's stereo and the source is mono, dupe the first channel.. + if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr)) + memcpy (dest1, dest0, sizeof (float) * (size_t) numSamples); + } + else if (numTargetChannels <= 64) + { + int* chans[65]; + readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels); + } + else + { + HeapBlock chans (numTargetChannels); + readChannels (*this, chans, buffer, startSample, numSamples, readerStartSample, numTargetChannels); + } if (! usesFloatingPointData) - { - for (int j = 0; j < 2; ++j) - { - float* const d = reinterpret_cast (chans[j]); - - if (d != nullptr) - { - const float multiplier = 1.0f / 0x7fffffff; - - for (int i = 0; i < numSamples; ++i) - d[i] = *reinterpret_cast (d + i) * multiplier; - } - } - } - - if (numTargetChannels > 1 && (chans[0] == nullptr || chans[1] == nullptr)) - { - // if this is a stereo buffer and the source was mono, dupe the first channel.. - memcpy (buffer->getSampleData (1, startSample), - buffer->getSampleData (0, startSample), - sizeof (float) * (size_t) numSamples); - } + for (int j = 0; j < numTargetChannels; ++j) + if (float* const d = buffer->getWritePointer (j, startSample)) + FloatVectorOperations::convertFixedToFloat (d, reinterpret_cast (d), 1.0f / 0x7fffffff, numSamples); } } -void AudioFormatReader::readMaxLevels (int64 startSampleInFile, - int64 numSamples, +template +static Range getChannelMinAndMax (SampleType* channel, int numSamples) noexcept +{ + return Range::findMinAndMax (channel, numSamples); +} + +static Range getChannelMinAndMax (float* channel, int numSamples) noexcept +{ + return FloatVectorOperations::findMinAndMax (channel, numSamples); +} + +template +static void getStereoMinAndMax (SampleType* const* channels, const int numChannels, const int numSamples, + SampleType& lmin, SampleType& lmax, SampleType& rmin, SampleType& rmax) +{ + Range range (getChannelMinAndMax (channels[0], numSamples)); + lmax = jmax (lmax, range.getEnd()); + lmin = jmin (lmin, range.getStart()); + + if (numChannels > 1) + { + range = getChannelMinAndMax (channels[1], numSamples); + rmax = jmax (rmax, range.getEnd()); + rmin = jmin (rmin, range.getStart()); + } + else + { + rmax = lmax; + rmin = lmin; + } +} + +void AudioFormatReader::readMaxLevels (int64 startSampleInFile, int64 numSamples, float& lowestLeft, float& highestLeft, float& lowestRight, float& highestRight) { @@ -178,12 +219,10 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, } const int bufferSize = (int) jmin (numSamples, (int64) 4096); - HeapBlock tempSpace ((size_t) bufferSize * 2 + 64); + AudioSampleBuffer tempSampleBuffer ((int) numChannels, bufferSize); - int* tempBuffer[3]; - tempBuffer[0] = tempSpace.getData(); - tempBuffer[1] = tempSpace.getData() + bufferSize; - tempBuffer[2] = 0; + float* const* const floatBuffer = tempSampleBuffer.getArrayOfWritePointers(); + int* const* intBuffer = reinterpret_cast (floatBuffer); if (usesFloatingPointData) { @@ -195,28 +234,12 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, while (numSamples > 0) { const int numToDo = (int) jmin (numSamples, (int64) bufferSize); - read (tempBuffer, 2, startSampleInFile, numToDo, false); + if (! read (intBuffer, 2, startSampleInFile, numToDo, false)) + break; numSamples -= numToDo; startSampleInFile += numToDo; - - float bufMin, bufMax; - findMinAndMax (reinterpret_cast (tempBuffer[0]), numToDo, bufMin, bufMax); - lmin = jmin (lmin, bufMin); - lmax = jmax (lmax, bufMax); - - if (numChannels > 1) - { - findMinAndMax (reinterpret_cast (tempBuffer[1]), numToDo, bufMin, bufMax); - rmin = jmin (rmin, bufMin); - rmax = jmax (rmax, bufMax); - } - } - - if (numChannels <= 1) - { - rmax = lmax; - rmin = lmin; + getStereoMinAndMax (floatBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax); } lowestLeft = lmin; @@ -234,34 +257,12 @@ void AudioFormatReader::readMaxLevels (int64 startSampleInFile, while (numSamples > 0) { const int numToDo = (int) jmin (numSamples, (int64) bufferSize); - if (! read (tempBuffer, 2, startSampleInFile, numToDo, false)) + if (! read (intBuffer, 2, startSampleInFile, numToDo, false)) break; numSamples -= numToDo; startSampleInFile += numToDo; - - for (int j = (int) numChannels; --j >= 0;) - { - int bufMin, bufMax; - findMinAndMax (tempBuffer[j], numToDo, bufMin, bufMax); - - if (j == 0) - { - lmax = jmax (lmax, bufMax); - lmin = jmin (lmin, bufMin); - } - else - { - rmax = jmax (rmax, bufMax); - rmin = jmin (rmin, bufMin); - } - } - } - - if (numChannels <= 1) - { - rmax = lmax; - rmin = lmin; + getStereoMinAndMax (intBuffer, (int) numChannels, numToDo, lmin, lmax, rmin, rmax); } lowestLeft = lmin / (float) std::numeric_limits::max(); @@ -386,3 +387,53 @@ int64 AudioFormatReader::searchForLevel (int64 startSample, return -1; } + +//============================================================================== +MemoryMappedAudioFormatReader::MemoryMappedAudioFormatReader (const File& f, const AudioFormatReader& reader, + int64 start, int64 length, int frameSize) + : AudioFormatReader (nullptr, reader.getFormatName()), file (f), + dataChunkStart (start), dataLength (length), bytesPerFrame (frameSize) +{ + sampleRate = reader.sampleRate; + bitsPerSample = reader.bitsPerSample; + lengthInSamples = reader.lengthInSamples; + numChannels = reader.numChannels; + metadataValues = reader.metadataValues; + usesFloatingPointData = reader.usesFloatingPointData; +} + +bool MemoryMappedAudioFormatReader::mapEntireFile() +{ + return mapSectionOfFile (Range (0, lengthInSamples)); +} + +bool MemoryMappedAudioFormatReader::mapSectionOfFile (Range samplesToMap) +{ + if (map == nullptr || samplesToMap != mappedSection) + { + map = nullptr; + + const Range fileRange (sampleToFilePos (samplesToMap.getStart()), + sampleToFilePos (samplesToMap.getEnd())); + + map = new MemoryMappedFile (file, fileRange, MemoryMappedFile::readOnly); + + if (map->getData() == nullptr) + map = nullptr; + else + mappedSection = Range (jmax ((int64) 0, filePosToSample (map->getRange().getStart() + (bytesPerFrame - 1))), + jmin (lengthInSamples, filePosToSample (map->getRange().getEnd()))); + } + + return map != nullptr; +} + +static int memoryReadDummyVariable; // used to force the compiler not to optimise-away the read operation + +void MemoryMappedAudioFormatReader::touchSample (int64 sample) const noexcept +{ + if (map != nullptr && mappedSection.contains (sample)) + memoryReadDummyVariable += *(char*) sampleToPointer (sample); + else + jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. +} diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h index 9532a8d..d7cdd9c 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReader.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOFORMATREADER_JUCEHEADER__ -#define __JUCE_AUDIOFORMATREADER_JUCEHEADER__ - -class AudioFormat; +#ifndef JUCE_AUDIOFORMATREADER_H_INCLUDED +#define JUCE_AUDIOFORMATREADER_H_INCLUDED //============================================================================== @@ -237,13 +234,15 @@ protected: typedef AudioData::Pointer DestType; typedef AudioData::Pointer SourceType; - static void read (int** destData, int destOffset, int numDestChannels, const void* sourceData, int numSourceChannels, int numSamples) noexcept + template + static void read (TargetType* const* destData, int destOffset, int numDestChannels, + const void* sourceData, int numSourceChannels, int numSamples) noexcept { for (int i = 0; i < numDestChannels; ++i) { - if (destData[i] != nullptr) + if (void* targetChan = destData[i]) { - DestType dest (destData[i]); + DestType dest (targetChan); dest += destOffset; if (i < numSourceChannels) @@ -255,11 +254,31 @@ protected: } }; + /** Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie + beyond the end of their available length. + */ + static void clearSamplesBeyondAvailableLength (int** destSamples, int numDestChannels, + int startOffsetInDestBuffer, int64 startSampleInFile, + int& numSamples, int64 fileLengthInSamples) + { + jassert (destSamples != nullptr); + const int64 samplesAvailable = fileLengthInSamples - startSampleInFile; + + if (samplesAvailable < numSamples) + { + for (int i = numDestChannels; --i >= 0;) + if (destSamples[i] != nullptr) + zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); + + numSamples = (int) samplesAvailable; + } + } + private: String formatName; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReader) }; -#endif // __JUCE_AUDIOFORMATREADER_JUCEHEADER__ +#endif // JUCE_AUDIOFORMATREADER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp index 574d66c..fb086ef 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.cpp @@ -1,31 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioFormatReaderSource::AudioFormatReaderSource (AudioFormatReader* const reader_, +AudioFormatReaderSource::AudioFormatReaderSource (AudioFormatReader* const r, const bool deleteReaderWhenThisIsDeleted) - : reader (reader_, deleteReaderWhenThisIsDeleted), + : reader (r, deleteReaderWhenThisIsDeleted), nextPlayPos (0), looping (false) { @@ -55,23 +54,23 @@ void AudioFormatReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& i if (looping) { - const int newStart = (int) (start % (int) reader->lengthInSamples); - const int newEnd = (int) ((start + info.numSamples) % (int) reader->lengthInSamples); + const int64 newStart = start % reader->lengthInSamples; + const int64 newEnd = (start + info.numSamples) % reader->lengthInSamples; if (newEnd > newStart) { reader->read (info.buffer, info.startSample, - newEnd - newStart, newStart, true, true); + (int) (newEnd - newStart), newStart, true, true); } else { - const int endSamps = (int) reader->lengthInSamples - newStart; + const int endSamps = (int) (reader->lengthInSamples - newStart); reader->read (info.buffer, info.startSample, endSamps, newStart, true, true); reader->read (info.buffer, info.startSample + endSamps, - newEnd, 0, true, true); + (int) newEnd, 0, true, true); } nextPlayPos = newEnd; diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h index d55b22c..d7ff5f7 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatReaderSource.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ -#define __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ +#ifndef JUCE_AUDIOFORMATREADERSOURCE_H_INCLUDED +#define JUCE_AUDIOFORMATREADERSOURCE_H_INCLUDED //============================================================================== @@ -69,23 +68,23 @@ public: //============================================================================== /** Implementation of the AudioSource method. */ - void prepareToPlay (int samplesPerBlockExpected, double sampleRate); + void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; /** Implementation of the AudioSource method. */ - void releaseResources(); + void releaseResources() override; /** Implementation of the AudioSource method. */ - void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill); + void getNextAudioBlock (const AudioSourceChannelInfo&) override; //============================================================================== /** Implements the PositionableAudioSource method. */ - void setNextReadPosition (int64 newPosition); + void setNextReadPosition (int64 newPosition) override; /** Implements the PositionableAudioSource method. */ - int64 getNextReadPosition() const; + int64 getNextReadPosition() const override; /** Implements the PositionableAudioSource method. */ - int64 getTotalLength() const; + int64 getTotalLength() const override; private: //============================================================================== @@ -94,12 +93,8 @@ private: int64 volatile nextPlayPos; bool volatile looping; - void readBufferSection (int start, int length, AudioSampleBuffer& buffer, int startSample); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReaderSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatReaderSource) }; - - -#endif // __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ +#endif // JUCE_AUDIOFORMATREADERSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp index 8e7531f..cd1ffc9 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -42,6 +41,23 @@ AudioFormatWriter::~AudioFormatWriter() delete output; } +static void convertFloatsToInts (int* dest, const float* src, int numSamples) noexcept +{ + while (--numSamples >= 0) + { + const double samp = *src++; + + if (samp <= -1.0) + *dest = std::numeric_limits::min(); + else if (samp >= 1.0) + *dest = std::numeric_limits::max(); + else + *dest = roundToInt (std::numeric_limits::max() * samp); + + ++dest; + } +} + bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, int64 startSample, int64 numSamplesToRead) @@ -52,7 +68,7 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, int* buffers [128] = { 0 }; for (int i = tempBuffer.getNumChannels(); --i >= 0;) - buffers[i] = reinterpret_cast (tempBuffer.getSampleData (i, 0)); + buffers[i] = reinterpret_cast (tempBuffer.getWritePointer (i, 0)); if (numSamplesToRead < 0) numSamplesToRead = reader.lengthInSamples; @@ -70,31 +86,12 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, while (*bufferChan != nullptr) { - int* b = *bufferChan++; + void* const b = *bufferChan++; if (isFloatingPoint()) - { - // int -> float - const double factor = 1.0 / std::numeric_limits::max(); - - for (int i = 0; i < numToDo; ++i) - reinterpret_cast (b)[i] = (float) (factor * b[i]); - } + FloatVectorOperations::convertFixedToFloat ((float*) b, (int*) b, 1.0f / 0x7fffffff, numToDo); else - { - // float -> int - for (int i = 0; i < numToDo; ++i) - { - const double samp = *(const float*) b; - - if (samp <= -1.0) - *b++ = std::numeric_limits::min(); - else if (samp >= 1.0) - *b++ = std::numeric_limits::max(); - else - *b++ = roundToInt (std::numeric_limits::max() * samp); - } - } + convertFloatsToInts ((int*) b, (float*) b, numToDo); } } @@ -116,10 +113,7 @@ bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSample { const int numToDo = jmin (numSamplesToRead, samplesPerBlock); - AudioSourceChannelInfo info; - info.buffer = &tempBuffer; - info.startSample = 0; - info.numSamples = numToDo; + AudioSourceChannelInfo info (&tempBuffer, 0, numToDo); info.clearActiveBufferRegion(); source.getNextAudioBlock (info); @@ -133,51 +127,71 @@ bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSample return true; } -bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples) +bool AudioFormatWriter::writeFromFloatArrays (const float* const* channels, int numSourceChannels, int numSamples) { - jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && source.getNumChannels() > 0); - if (numSamples <= 0) return true; - HeapBlock tempBuffer; - HeapBlock chans (numChannels + 1); - chans [numChannels] = 0; - if (isFloatingPoint()) - { - for (int i = (int) numChannels; --i >= 0;) - chans[i] = reinterpret_cast (source.getSampleData (i, startSample)); - } - else - { - tempBuffer.malloc (((size_t) numSamples) * (size_t) numChannels); + return write ((const int**) channels, numSamples); - for (unsigned int i = 0; i < numChannels; ++i) - { - typedef AudioData::Pointer DestSampleType; - typedef AudioData::Pointer SourceSampleType; + int* chans [256]; + int scratch [4096]; - chans[i] = tempBuffer + (int) i * numSamples; - DestSampleType destData (chans[i]); - SourceSampleType sourceData (source.getSampleData ((int) i, startSample)); - destData.convertSamples (sourceData, numSamples); - } + jassert (numSourceChannels < numElementsInArray (chans)); + const int maxSamples = (int) (numElementsInArray (scratch) / numSourceChannels); + + for (int i = 0; i < numSourceChannels; ++i) + chans[i] = scratch + (i * maxSamples); + + chans[numSourceChannels] = nullptr; + int startSample = 0; + + while (numSamples > 0) + { + const int numToDo = jmin (numSamples, maxSamples); + + for (int i = 0; i < numSourceChannels; ++i) + convertFloatsToInts (chans[i], channels[i] + startSample, numToDo); + + if (! write ((const int**) chans, numToDo)) + return false; + + startSample += numToDo; + numSamples -= numToDo; } - return write ((const int**) chans.getData(), numSamples); + return true; +} + +bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples) +{ + const int numSourceChannels = source.getNumChannels(); + jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && numSourceChannels > 0); + + if (startSample == 0) + return writeFromFloatArrays (source.getArrayOfReadPointers(), numSourceChannels, numSamples); + + const float* chans [256]; + jassert ((int) numChannels < numElementsInArray (chans)); + + for (int i = 0; i < numSourceChannels; ++i) + chans[i] = source.getReadPointer (i, startSample); + + chans[numSourceChannels] = nullptr; + + return writeFromFloatArrays (chans, numSourceChannels, numSamples); } //============================================================================== -class AudioFormatWriter::ThreadedWriter::Buffer : public TimeSliceClient, - public AbstractFifo +class AudioFormatWriter::ThreadedWriter::Buffer : private TimeSliceClient { public: - Buffer (TimeSliceThread& timeSliceThread_, AudioFormatWriter* writer_, int numChannels, int bufferSize_) - : AbstractFifo (bufferSize_), - buffer (numChannels, bufferSize_), - timeSliceThread (timeSliceThread_), - writer (writer_), + Buffer (TimeSliceThread& tst, AudioFormatWriter* w, int channels, int numSamples) + : fifo (numSamples), + buffer (channels, numSamples), + timeSliceThread (tst), + writer (w), receiver (nullptr), samplesWritten (0), isRunning (true) @@ -194,7 +208,7 @@ public: {} } - bool write (const float** data, int numSamples) + bool write (const float* const* data, int numSamples) { if (numSamples <= 0 || ! isRunning) return true; @@ -202,7 +216,7 @@ public: jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this! int start1, size1, start2, size2; - prepareToWrite (numSamples, start1, size1, start2, size2); + fifo.prepareToWrite (numSamples, start1, size1, start2, size2); if (size1 + size2 < numSamples) return false; @@ -213,22 +227,22 @@ public: buffer.copyFrom (i, start2, data[i] + size1, size2); } - finishedWrite (size1 + size2); + fifo.finishedWrite (size1 + size2); timeSliceThread.notify(); return true; } - int useTimeSlice() + int useTimeSlice() override { return writePendingData(); } int writePendingData() { - const int numToDo = getTotalSize() / 4; + const int numToDo = fifo.getTotalSize() / 4; int start1, size1, start2, size2; - prepareToRead (numToDo, start1, size1, start2, size2); + fifo.prepareToRead (numToDo, start1, size1, start2, size2); if (size1 <= 0) return 10; @@ -251,7 +265,7 @@ public: samplesWritten += size2; } - finishedRead (size1 + size2); + fifo.finishedRead (size1 + size2); return 0; } @@ -266,6 +280,7 @@ public: } private: + AbstractFifo fifo; AudioSampleBuffer buffer; TimeSliceThread& timeSliceThread; ScopedPointer writer; @@ -274,7 +289,7 @@ private: int64 samplesWritten; volatile bool isRunning; - JUCE_DECLARE_NON_COPYABLE (Buffer); + JUCE_DECLARE_NON_COPYABLE (Buffer) }; AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer) @@ -286,7 +301,7 @@ AudioFormatWriter::ThreadedWriter::~ThreadedWriter() { } -bool AudioFormatWriter::ThreadedWriter::write (const float** data, int numSamples) +bool AudioFormatWriter::ThreadedWriter::write (const float* const* data, int numSamples) { return buffer->write (data, numSamples); } diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h index 9f1ce1a..ceb54da 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioFormatWriter.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ -#define __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ - -#include "juce_AudioFormatReader.h" +#ifndef JUCE_AUDIOFORMATWRITER_H_INCLUDED +#define JUCE_AUDIOFORMATWRITER_H_INCLUDED //============================================================================== @@ -130,6 +127,9 @@ public: bool writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples); + /** Writes some samples from a set of float data channels. */ + bool writeFromFloatArrays (const float* const* channels, int numChannels, int numSamples); + //============================================================================== /** Returns the sample rate being used. */ double getSampleRate() const noexcept { return sampleRate; } @@ -177,7 +177,7 @@ public: The data must be an array containing the same number of channels as the AudioFormatWriter object is using. None of these channels can be null. */ - bool write (const float** data, int numSamples); + bool write (const float* const* data, int numSamples); class JUCE_API IncomingDataReceiver { @@ -201,7 +201,7 @@ public: private: class Buffer; - friend class ScopedPointer; + friend struct ContainerDeletePolicy; ScopedPointer buffer; }; @@ -219,7 +219,7 @@ protected: /** True if it's a floating-point format, false if it's fixed-point. */ bool usesFloatingPointData; - /** The output stream for Use by subclasses. */ + /** The output stream for use by subclasses. */ OutputStream* output; /** Used by AudioFormatWriter subclasses to copy data to different formats. */ @@ -229,7 +229,7 @@ protected: typedef AudioData::Pointer DestType; typedef AudioData::Pointer SourceType; - static void write (void* destData, int numDestChannels, const int** source, + static void write (void* destData, int numDestChannels, const int* const* source, int numSamples, const int sourceOffset = 0) noexcept { for (int i = 0; i < numDestChannels; ++i) @@ -253,7 +253,7 @@ private: String formatName; friend class ThreadedWriter; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatWriter); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioFormatWriter) }; -#endif // __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ +#endif // JUCE_AUDIOFORMATWRITER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp index 73323fc..bbe1ebd 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -51,17 +50,8 @@ AudioSubsectionReader::~AudioSubsectionReader() bool AudioSubsectionReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) { - if (startSampleInFile + numSamples > length) - { - for (int i = numDestChannels; --i >= 0;) - if (destSamples[i] != nullptr) - zeromem (destSamples[i], sizeof (int) * (size_t) numSamples); - - numSamples = jmin (numSamples, (int) (length - startSampleInFile)); - - if (numSamples <= 0) - return true; - } + clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, length); return source->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer, startSampleInFile + startSample, numSamples); diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h index 803c42f..fb9e6a0 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_AudioSubsectionReader.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ -#define __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ - -#include "juce_AudioFormatReader.h" +#ifndef JUCE_AUDIOSUBSECTIONREADER_H_INCLUDED +#define JUCE_AUDIOSUBSECTIONREADER_H_INCLUDED //============================================================================== @@ -46,7 +43,7 @@ class JUCE_API AudioSubsectionReader : public AudioFormatReader { public: //============================================================================== - /** Creates a AudioSubsectionReader for a given data source. + /** Creates an 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 @@ -68,14 +65,11 @@ public: //============================================================================== bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, - int64 startSampleInFile, int numSamples); + int64 startSampleInFile, int numSamples) override; - void readMaxLevels (int64 startSample, - int64 numSamples, - float& lowestLeft, - float& highestLeft, - float& lowestRight, - float& highestRight); + void readMaxLevels (int64 startSample, int64 numSamples, + float& lowestLeft, float& highestLeft, + float& lowestRight, float& highestRight) override; private: @@ -84,7 +78,7 @@ private: int64 startSample, length; const bool deleteSourceWhenDeleted; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSubsectionReader); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSubsectionReader) }; -#endif // __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ +#endif // JUCE_AUDIOSUBSECTIONREADER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp new file mode 100644 index 0000000..ecfe1d4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.cpp @@ -0,0 +1,172 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +BufferingAudioReader::BufferingAudioReader (AudioFormatReader* sourceReader, + TimeSliceThread& timeSliceThread, + int samplesToBuffer) + : AudioFormatReader (nullptr, sourceReader->getFormatName()), + source (sourceReader), thread (timeSliceThread), + nextReadPosition (0), + numBlocks (1 + (samplesToBuffer / samplesPerBlock)) +{ + sampleRate = source->sampleRate; + lengthInSamples = source->lengthInSamples; + numChannels = source->numChannels; + metadataValues = source->metadataValues; + bitsPerSample = 32; + usesFloatingPointData = true; + + for (int i = 3; --i >= 0;) + readNextBufferChunk(); + + timeSliceThread.addTimeSliceClient (this); +} + +BufferingAudioReader::~BufferingAudioReader() +{ + thread.removeTimeSliceClient (this); +} + +void BufferingAudioReader::setReadTimeout (int timeoutMilliseconds) noexcept +{ + timeoutMs = timeoutMilliseconds; +} + +bool BufferingAudioReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) +{ + const uint32 startTime = Time::getMillisecondCounter(); + clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, + startSampleInFile, numSamples, lengthInSamples); + + const ScopedLock sl (lock); + nextReadPosition = startSampleInFile; + + while (numSamples > 0) + { + if (const BufferedBlock* const block = getBlockContaining (startSampleInFile)) + { + const int offset = (int) (startSampleInFile - block->range.getStart()); + const int numToDo = jmin (numSamples, (int) (block->range.getEnd() - startSampleInFile)); + + for (int j = 0; j < numDestChannels; ++j) + { + if (float* dest = (float*) destSamples[j]) + { + dest += startOffsetInDestBuffer; + + if (j < (int) numChannels) + FloatVectorOperations::copy (dest, block->buffer.getReadPointer (j, offset), numToDo); + else + FloatVectorOperations::clear (dest, numToDo); + } + } + + startOffsetInDestBuffer += numToDo; + startSampleInFile += numToDo; + numSamples -= numToDo; + } + else + { + if (timeoutMs >= 0 && Time::getMillisecondCounter() >= startTime + (uint32) timeoutMs) + { + for (int j = 0; j < numDestChannels; ++j) + if (float* dest = (float*) destSamples[j]) + FloatVectorOperations::clear (dest + startOffsetInDestBuffer, numSamples); + + break; + } + else + { + ScopedUnlock ul (lock); + Thread::yield(); + } + } + } + + return true; +} + +BufferingAudioReader::BufferedBlock::BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples) + : range (pos, pos + numSamples), + buffer ((int) reader.numChannels, numSamples) +{ + reader.read (&buffer, 0, numSamples, pos, true, true); +} + +BufferingAudioReader::BufferedBlock* BufferingAudioReader::getBlockContaining (int64 pos) const noexcept +{ + for (int i = blocks.size(); --i >= 0;) + { + BufferedBlock* const b = blocks.getUnchecked(i); + + if (b->range.contains (pos)) + return b; + } + + return nullptr; +} + +int BufferingAudioReader::useTimeSlice() +{ + return readNextBufferChunk() ? 1 : 100; +} + +bool BufferingAudioReader::readNextBufferChunk() +{ + const int64 pos = nextReadPosition; + const int64 startPos = ((pos - 1024) / samplesPerBlock) * samplesPerBlock; + const int64 endPos = startPos + numBlocks * samplesPerBlock; + + OwnedArray newBlocks; + + for (int i = blocks.size(); --i >= 0;) + if (blocks.getUnchecked(i)->range.intersects (Range (startPos, endPos))) + newBlocks.add (blocks.getUnchecked(i)); + + if (newBlocks.size() == numBlocks) + { + newBlocks.clear (false); + return false; + } + + for (int64 p = startPos; p < endPos; p += samplesPerBlock) + { + if (getBlockContaining (p) == nullptr) + { + newBlocks.add (new BufferedBlock (*source, p, samplesPerBlock)); + break; // just do one block + } + } + + { + const ScopedLock sl (lock); + newBlocks.swapWith (blocks); + } + + for (int i = blocks.size(); --i >= 0;) + newBlocks.removeObject (blocks.getUnchecked(i), false); + + return true; +} diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h new file mode 100644 index 0000000..8f950be --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_BufferingAudioFormatReader.h @@ -0,0 +1,93 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_BUFFERINGAUDIOFORMATREADER_H_INCLUDED +#define JUCE_BUFFERINGAUDIOFORMATREADER_H_INCLUDED + +//============================================================================== +/** + An AudioFormatReader that uses a background thread to pre-read data from + another reader. + + @see AudioFormatReader +*/ +class JUCE_API BufferingAudioReader : public AudioFormatReader, + private TimeSliceClient +{ +public: + /** Creates a reader. + + @param sourceReader the source reader to wrap. This BufferingAudioReader + takes ownership of this object and will delete it later + when no longer needed + @param timeSliceThread the thread that should be used to do the background reading. + Make sure that the thread you supply is running, and won't + be deleted while the reader object still exists. + @param samplesToBuffer the total number of samples to buffer ahead. + */ + BufferingAudioReader (AudioFormatReader* sourceReader, + TimeSliceThread& timeSliceThread, + int samplesToBuffer); + + ~BufferingAudioReader(); + + /** Sets a number of milliseconds that the reader can block for in its readSamples() + method before giving up and returning silence. + A value of less that 0 means "wait forever". + The default timeout is 0. + */ + void setReadTimeout (int timeoutMilliseconds) noexcept; + + bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, + int64 startSampleInFile, int numSamples) override; + +private: + ScopedPointer source; + TimeSliceThread& thread; + int64 nextReadPosition; + const int numBlocks; + int timeoutMs; + + enum { samplesPerBlock = 32768 }; + + struct BufferedBlock + { + BufferedBlock (AudioFormatReader& reader, int64 pos, int numSamples); + + Range range; + AudioSampleBuffer buffer; + }; + + CriticalSection lock; + OwnedArray blocks; + + BufferedBlock* getBlockContaining (int64 pos) const noexcept; + int useTimeSlice() override; + bool readNextBufferChunk(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferingAudioReader) +}; + + +#endif // JUCE_BUFFERINGAUDIOFORMATREADER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h b/JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h new file mode 100644 index 0000000..2ec6388 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_formats/format/juce_MemoryMappedAudioFormatReader.h @@ -0,0 +1,106 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_MEMORYMAPPEDAUDIOFORMATREADER_H_INCLUDED +#define JUCE_MEMORYMAPPEDAUDIOFORMATREADER_H_INCLUDED + + +//============================================================================== +/** + A specialised type of AudioFormatReader that uses a MemoryMappedFile to read + directly from an audio file. + + This allows for incredibly fast random-access to sample data in the mapped + region of the file, but not all audio formats support it - see + AudioFormat::createMemoryMappedReader(). + + Note that before reading samples from a MemoryMappedAudioFormatReader, you must first + call mapEntireFile() or mapSectionOfFile() to ensure that the region you want to + read has been mapped. + + @see AudioFormat::createMemoryMappedReader, AudioFormatReader +*/ +class JUCE_API MemoryMappedAudioFormatReader : public AudioFormatReader +{ +protected: + //============================================================================== + /** Creates an MemoryMappedAudioFormatReader object. + + Note that before attempting to read any data, you must call mapEntireFile() + or mapSectionOfFile() to ensure that the region you want to read has + been mapped. + */ + MemoryMappedAudioFormatReader (const File& file, const AudioFormatReader& details, + int64 dataChunkStart, int64 dataChunkLength, int bytesPerFrame); + +public: + /** Returns the file that is being mapped */ + const File& getFile() const noexcept { return file; } + + /** Attempts to map the entire file into memory. */ + bool mapEntireFile(); + + /** Attempts to map a section of the file into memory. */ + bool mapSectionOfFile (Range samplesToMap); + + /** Returns the sample range that's currently memory-mapped and available for reading. */ + Range getMappedSection() const noexcept { return mappedSection; } + + /** Touches the memory for the given sample, to force it to be loaded into active memory. */ + void touchSample (int64 sample) const noexcept; + + /** Returns the number of bytes currently being mapped */ + size_t getNumBytesUsed() const { return map != nullptr ? map->getSize() : 0; } + +protected: + File file; + Range mappedSection; + ScopedPointer map; + int64 dataChunkStart, dataLength; + int bytesPerFrame; + + /** Converts a sample index to a byte position in the file. */ + inline int64 sampleToFilePos (int64 sample) const noexcept { return dataChunkStart + sample * bytesPerFrame; } + + /** Converts a byte position in the file to a sample index. */ + inline int64 filePosToSample (int64 filePos) const noexcept { return (filePos - dataChunkStart) / bytesPerFrame; } + + /** Converts a sample index to a pointer to the mapped file memory. */ + inline const void* sampleToPointer (int64 sample) const noexcept { return addBytesToPointer (map->getData(), sampleToFilePos (sample) - map->getRange().getStart()); } + + /** Used by AudioFormatReader subclasses to scan for min/max ranges in interleaved data. */ + template + void scanMinAndMaxInterleaved (int channel, int64 startSampleInFile, int64 numSamples, float& mn, float& mx) const noexcept + { + typedef AudioData::Pointer SourceType; + + SourceType (addBytesToPointer (sampleToPointer (startSampleInFile), ((int) bitsPerSample / 8) * channel), (int) numChannels) + .findMinAndMax ((size_t) numSamples, mn, mx); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAudioFormatReader) +}; + + +#endif // JUCE_MEMORYMAPPEDAUDIOFORMATREADER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp index ed54370..b68042f 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_AUDIO_FORMATS_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_AUDIO_FORMATS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -104,6 +103,7 @@ namespace juce #include "format/juce_AudioFormatReaderSource.cpp" #include "format/juce_AudioFormatWriter.cpp" #include "format/juce_AudioSubsectionReader.cpp" +#include "format/juce_BufferingAudioFormatReader.cpp" #include "sampler/juce_Sampler.cpp" #include "codecs/juce_AiffAudioFormat.cpp" #include "codecs/juce_CoreAudioFormat.cpp" @@ -112,6 +112,7 @@ namespace juce #include "codecs/juce_OggVorbisAudioFormat.cpp" #include "codecs/juce_QuickTimeAudioFormat.cpp" #include "codecs/juce_WavAudioFormat.cpp" +#include "codecs/juce_LAMEEncoderAudioFormat.cpp" #if JUCE_WINDOWS && JUCE_USE_WINDOWS_MEDIA_FORMAT #include "codecs/juce_WindowsMediaAudioFormat.cpp" diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h index e591be2..c15194e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIO_FORMATS_JUCEHEADER__ -#define __JUCE_AUDIO_FORMATS_JUCEHEADER__ +#ifndef JUCE_AUDIO_FORMATS_H_INCLUDED +#define JUCE_AUDIO_FORMATS_H_INCLUDED #include "../juce_audio_basics/juce_audio_basics.h" @@ -63,6 +62,13 @@ #define JUCE_USE_MP3AUDIOFORMAT 0 #endif +/** Config: JUCE_USE_LAME_AUDIO_FORMAT + Enables the LameEncoderAudioFormat class. +*/ +#ifndef JUCE_USE_LAME_AUDIO_FORMAT + #define JUCE_USE_LAME_AUDIO_FORMAT 0 +#endif + /** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT Enables the Windows Media SDK codecs. */ @@ -79,38 +85,26 @@ namespace juce { -// START_AUTOINCLUDE format, codecs, sampler -#ifndef __JUCE_AUDIOFORMAT_JUCEHEADER__ - #include "format/juce_AudioFormat.h" -#endif -#ifndef __JUCE_AUDIOFORMATMANAGER_JUCEHEADER__ - #include "format/juce_AudioFormatManager.h" -#endif -#ifndef __JUCE_AUDIOFORMATREADER_JUCEHEADER__ - #include "format/juce_AudioFormatReader.h" -#endif -#ifndef __JUCE_AUDIOFORMATREADERSOURCE_JUCEHEADER__ - #include "format/juce_AudioFormatReaderSource.h" -#endif -#ifndef __JUCE_AUDIOFORMATWRITER_JUCEHEADER__ - #include "format/juce_AudioFormatWriter.h" -#endif -#ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ - #include "format/juce_AudioSubsectionReader.h" -#endif +class AudioFormat; +#include "format/juce_AudioFormatReader.h" +#include "format/juce_AudioFormatWriter.h" +#include "format/juce_MemoryMappedAudioFormatReader.h" +#include "format/juce_AudioFormat.h" +#include "format/juce_AudioFormatManager.h" +#include "format/juce_AudioFormatReaderSource.h" +#include "format/juce_AudioSubsectionReader.h" +#include "format/juce_BufferingAudioFormatReader.h" #include "codecs/juce_AiffAudioFormat.h" #include "codecs/juce_CoreAudioFormat.h" #include "codecs/juce_FlacAudioFormat.h" +#include "codecs/juce_LAMEEncoderAudioFormat.h" #include "codecs/juce_MP3AudioFormat.h" #include "codecs/juce_OggVorbisAudioFormat.h" #include "codecs/juce_QuickTimeAudioFormat.h" #include "codecs/juce_WavAudioFormat.h" #include "codecs/juce_WindowsMediaAudioFormat.h" -#ifndef __JUCE_SAMPLER_JUCEHEADER__ - #include "sampler/juce_Sampler.h" -#endif -// END_AUTOINCLUDE +#include "sampler/juce_Sampler.h" } -#endif // __JUCE_AUDIO_FORMATS_JUCEHEADER__ +#endif // JUCE_AUDIO_FORMATS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm index c625cb2..9b32ae5 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_audio_formats.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_module_info b/JuceLibraryCode/modules/juce_audio_formats/juce_module_info index 06165e1..69f5c09 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_formats", "name": "JUCE audio file format codecs", - "version": "2.0.21", + "version": "3.0.6", "description": "Classes for reading and writing various audio file formats.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp index eb4a23b..9dbe7b3 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.cpp @@ -1,38 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -//============================================================================== -SamplerSound::SamplerSound (const String& name_, +SamplerSound::SamplerSound (const String& soundName, AudioFormatReader& source, - const BigInteger& midiNotes_, + const BigInteger& notes, const int midiNoteForNormalPitch, const double attackTimeSecs, const double releaseTimeSecs, const double maxSampleLengthSeconds) - : name (name_), - midiNotes (midiNotes_), + : name (soundName), + midiNotes (notes), midiRootNote (midiNoteForNormalPitch) { sourceSampleRate = source.sampleRate; @@ -61,7 +59,6 @@ SamplerSound::~SamplerSound() { } -//============================================================================== bool SamplerSound::appliesToNote (const int midiNoteNumber) { return midiNotes [midiNoteNumber]; @@ -72,15 +69,13 @@ bool SamplerSound::appliesToChannel (const int /*midiChannel*/) return true; } - //============================================================================== SamplerVoice::SamplerVoice() : pitchRatio (0.0), sourceSamplePosition (0.0), - lgain (0.0f), - rgain (0.0f), - isInAttack (false), - isInRelease (false) + lgain (0.0f), rgain (0.0f), + attackReleaseLevel (0), attackDelta (0), releaseDelta (0), + isInAttack (false), isInRelease (false) { } @@ -90,7 +85,7 @@ SamplerVoice::~SamplerVoice() bool SamplerVoice::canPlaySound (SynthesiserSound* sound) { - return dynamic_cast (sound) != nullptr; + return dynamic_cast (sound) != nullptr; } void SamplerVoice::startNote (const int midiNoteNumber, @@ -98,15 +93,10 @@ void SamplerVoice::startNote (const int midiNoteNumber, SynthesiserSound* s, const int /*currentPitchWheelPosition*/) { - const SamplerSound* const sound = dynamic_cast (s); - jassert (sound != nullptr); // this object can only play SamplerSounds! - - if (sound != nullptr) + if (const SamplerSound* const sound = dynamic_cast (s)) { - const double targetFreq = MidiMessage::getMidiNoteInHertz (midiNoteNumber); - const double naturalFreq = MidiMessage::getMidiNoteInHertz (sound->midiRootNote); - - pitchRatio = (targetFreq * sound->sourceSampleRate) / (naturalFreq * getSampleRate()); + pitchRatio = pow (2.0, (midiNoteNumber - sound->midiRootNote) / 12.0) + * sound->sourceSampleRate / getSampleRate(); sourceSamplePosition = 0.0; lgain = velocity; @@ -127,13 +117,13 @@ void SamplerVoice::startNote (const int midiNoteNumber, } if (sound->releaseSamples > 0) - { releaseDelta = (float) (-pitchRatio / sound->releaseSamples); - } else - { releaseDelta = 0.0f; - } + } + else + { + jassertfalse; // this object can only play SamplerSounds! } } @@ -162,16 +152,14 @@ void SamplerVoice::controllerMoved (const int /*controllerNumber*/, //============================================================================== void SamplerVoice::renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples) { - const SamplerSound* const playingSound = static_cast (getCurrentlyPlayingSound().getObject()); - - if (playingSound != nullptr) + if (const SamplerSound* const playingSound = static_cast (getCurrentlyPlayingSound().get())) { - const float* const inL = playingSound->data->getSampleData (0, 0); + const float* const inL = playingSound->data->getReadPointer (0); const float* const inR = playingSound->data->getNumChannels() > 1 - ? playingSound->data->getSampleData (1, 0) : nullptr; + ? playingSound->data->getReadPointer (1) : nullptr; - float* outL = outputBuffer.getSampleData (0, startSample); - float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getSampleData (1, startSample) : nullptr; + float* outL = outputBuffer.getWritePointer (0, startSample); + float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample) : nullptr; while (--numSamples >= 0) { diff --git a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h index 2ee1016..51133ad 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h +++ b/JuceLibraryCode/modules/juce_audio_formats/sampler/juce_Sampler.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_SAMPLER_JUCEHEADER__ -#define __JUCE_SAMPLER_JUCEHEADER__ +#ifndef JUCE_SAMPLER_H_INCLUDED +#define JUCE_SAMPLER_H_INCLUDED //============================================================================== @@ -74,17 +73,17 @@ public: //============================================================================== /** Returns the sample's name */ - const String& getName() const { return name; } + const String& getName() const noexcept { return name; } /** Returns the audio sample data. - This could be 0 if there was a problem loading it. + This could return nullptr if there was a problem loading the data. */ - AudioSampleBuffer* getAudioData() const { return data; } + AudioSampleBuffer* getAudioData() const noexcept { return data; } //============================================================================== - bool appliesToNote (const int midiNoteNumber); - bool appliesToChannel (const int midiChannel); + bool appliesToNote (const int midiNoteNumber) override; + bool appliesToChannel (const int midiChannel) override; private: @@ -92,13 +91,13 @@ private: friend class SamplerVoice; String name; - ScopedPointer data; + ScopedPointer data; double sourceSampleRate; BigInteger midiNotes; int length, attackSamples, releaseSamples; int midiRootNote; - JUCE_LEAK_DETECTOR (SamplerSound); + JUCE_LEAK_DETECTOR (SamplerSound) }; @@ -115,29 +114,22 @@ class JUCE_API SamplerVoice : public SynthesiserVoice { public: //============================================================================== - /** Creates a SamplerVoice. - */ + /** Creates a SamplerVoice. */ SamplerVoice(); /** Destructor. */ ~SamplerVoice(); - //============================================================================== - bool canPlaySound (SynthesiserSound* sound); + bool canPlaySound (SynthesiserSound*) override; - void startNote (const int midiNoteNumber, - const float velocity, - SynthesiserSound* sound, - const int currentPitchWheelPosition); + void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int pitchWheel) override; + void stopNote (bool allowTailOff) override; - void stopNote (const bool allowTailOff); + void pitchWheelMoved (int newValue); + void controllerMoved (int controllerNumber, int newValue) override; - void pitchWheelMoved (const int newValue); - void controllerMoved (const int controllerNumber, - const int newValue); - - void renderNextBlock (AudioSampleBuffer& outputBuffer, int startSample, int numSamples); + void renderNextBlock (AudioSampleBuffer&, int startSample, int numSamples) override; private: @@ -147,8 +139,8 @@ private: float lgain, rgain, attackReleaseLevel, attackDelta, releaseDelta; bool isInAttack, isInRelease; - JUCE_LEAK_DETECTOR (SamplerVoice); + JUCE_LEAK_DETECTOR (SamplerVoice) }; -#endif // __JUCE_SAMPLER_JUCEHEADER__ +#endif // JUCE_SAMPLER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.cpp index 32f992a..cbd2542 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.h index 3cf9e8b..d0af201 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormat.h @@ -1,41 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ -#define __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ - -#include "../processors/juce_AudioPluginInstance.h" -class PluginDescription; +#ifndef JUCE_AUDIOPLUGINFORMAT_H_INCLUDED +#define JUCE_AUDIOPLUGINFORMAT_H_INCLUDED //============================================================================== /** 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. + @see AudioFormatManager */ class JUCE_API AudioPluginFormat { @@ -46,7 +41,6 @@ public: //============================================================================== /** Returns the format name. - E.g. "VST", "AudioUnit", etc. */ virtual String getName() const = 0; @@ -60,14 +54,15 @@ public: (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, + 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; + virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, + double initialSampleRate, + int initialBufferSize) = 0; /** Should do a quick check to see if this file or directory might be a plugin of this format. @@ -77,19 +72,22 @@ public: */ virtual bool fileMightContainThisPluginType (const String& fileOrIdentifier) = 0; - /** Returns a readable version of the name of the plugin that this identifier refers to. - */ + /** Returns a readable version of the name of the plugin that this identifier refers to. */ virtual String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) = 0; - /** Checks whether this plugin could possibly be loaded. + /** Returns true if this plugin's version or date has changed and it should be re-checked. */ + virtual bool pluginNeedsRescanning (const PluginDescription&) = 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. + /** Returns true if this format needs to run a scan to find its list of plugins. */ + virtual bool canScanForPlugins() const = 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. */ @@ -98,18 +96,17 @@ public: /** 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) + Note that if this returns no paths, it means that the format doesn't search in + files or folders, e.g. AudioUnits. */ virtual FileSearchPath getDefaultLocationsToSearch() = 0; - protected: //============================================================================== AudioPluginFormat() noexcept; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormat) }; -#endif // __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ +#endif // JUCE_AUDIOPLUGINFORMAT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp index 568aee2..0afa7c7 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.cpp @@ -1,38 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioPluginFormatManager::AudioPluginFormatManager() -{ -} - -AudioPluginFormatManager::~AudioPluginFormatManager() -{ - clearSingletonInstance(); -} - -juce_ImplementSingleton_SingleThreaded (AudioPluginFormatManager); +AudioPluginFormatManager::AudioPluginFormatManager() {} +AudioPluginFormatManager::~AudioPluginFormatManager() {} //============================================================================== void AudioPluginFormatManager::addDefaultFormats() @@ -41,18 +32,18 @@ void AudioPluginFormatManager::addDefaultFormats() // you should only call this method once! for (int i = formats.size(); --i >= 0;) { - #if JUCE_PLUGINHOST_VST && ! (JUCE_MAC && JUCE_64BIT) + #if JUCE_PLUGINHOST_VST jassert (dynamic_cast (formats[i]) == nullptr); #endif + #if JUCE_PLUGINHOST_VST3 + jassert (dynamic_cast (formats[i]) == nullptr); + #endif + #if JUCE_PLUGINHOST_AU && JUCE_MAC jassert (dynamic_cast (formats[i]) == nullptr); #endif - #if JUCE_PLUGINHOST_DX && JUCE_WINDOWS - jassert (dynamic_cast (formats[i]) == nullptr); - #endif - #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX jassert (dynamic_cast (formats[i]) == nullptr); #endif @@ -63,12 +54,12 @@ void AudioPluginFormatManager::addDefaultFormats() formats.add (new AudioUnitPluginFormat()); #endif - #if JUCE_PLUGINHOST_VST && ! (JUCE_MAC && JUCE_64BIT) + #if JUCE_PLUGINHOST_VST formats.add (new VSTPluginFormat()); #endif - #if JUCE_PLUGINHOST_DX && JUCE_WINDOWS - formats.add (new DirectXPluginFormat()); + #if JUCE_PLUGINHOST_VST3 + formats.add (new VST3PluginFormat()); #endif #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX @@ -91,28 +82,16 @@ void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format) formats.add (format); } -AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, - String& errorMessage) const +AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, double rate, + int blockSize, String& errorMessage) const { - AudioPluginInstance* result = nullptr; - for (int i = 0; i < formats.size(); ++i) - { - result = formats.getUnchecked(i)->createInstanceFromDescription (description); + if (AudioPluginInstance* result = formats.getUnchecked(i)->createInstanceFromDescription (description, rate, blockSize)) + return result; - if (result != nullptr) - break; - } - - if (result == nullptr) - { - if (! doesPluginStillExist (description)) - errorMessage = TRANS ("This plug-in file no longer exists"); - else - errorMessage = TRANS ("This plug-in failed to load correctly"); - } - - return result; + errorMessage = doesPluginStillExist (description) ? TRANS ("This plug-in failed to load correctly") + : TRANS ("This plug-in file no longer exists"); + return nullptr; } bool AudioPluginFormatManager::doesPluginStillExist (const PluginDescription& description) const diff --git a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.h b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.h index 627845a..a6f1607 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ -#define __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ - -#include "juce_AudioPluginFormat.h" +#ifndef JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED +#define JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED //============================================================================== @@ -35,7 +32,7 @@ @see AudioPluginFormat */ -class JUCE_API AudioPluginFormatManager : public DeletedAtShutdown +class JUCE_API AudioPluginFormatManager { public: //============================================================================== @@ -44,8 +41,6 @@ public: /** Destructor. */ ~AudioPluginFormatManager(); - juce_DeclareSingleton_SingleThreaded (AudioPluginFormatManager, false); - //============================================================================== /** Adds any formats that it knows about, e.g. VST. */ @@ -78,10 +73,12 @@ public: 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 + If it can't load the plugin, it returns nullptr and leaves a message in the errorMessage string. */ AudioPluginInstance* createPluginInstance (const PluginDescription& description, + double initialSampleRate, + int initialBufferSize, String& errorMessage) const; /** Checks that the file or component for this plugin actually still exists. @@ -92,11 +89,11 @@ public: private: //============================================================================== - OwnedArray formats; + OwnedArray formats; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginFormatManager) }; -#endif // __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ +#endif // JUCE_AUDIOPLUGINFORMATMANAGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h index 985c305..46c2b10 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.h @@ -1,34 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ -#define __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ - -#include "../format/juce_AudioPluginFormat.h" - -#if JUCE_PLUGINHOST_AU && JUCE_MAC +#if (JUCE_PLUGINHOST_AU && JUCE_MAC) || DOXYGEN //============================================================================== /** @@ -42,20 +36,20 @@ public: ~AudioUnitPluginFormat(); //============================================================================== - String getName() const { return "AudioUnit"; } - void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); - AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const String& fileOrIdentifier); - String getNameOfPluginFromIdentifier (const String& fileOrIdentifier); - StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, bool recursive); - bool doesPluginStillExist (const PluginDescription& desc); - FileSearchPath getDefaultLocationsToSearch(); + String getName() const override { return "AudioUnit"; } + void findAllTypesForFile (OwnedArray&, const String& fileOrIdentifier) override; + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc, double, int) override; + bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; + String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; + bool pluginNeedsRescanning (const PluginDescription&) override; + StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive); + bool doesPluginStillExist (const PluginDescription&) override; + FileSearchPath getDefaultLocationsToSearch() override; + bool canScanForPlugins() const override { return true; } private: //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginFormat) }; #endif - -#endif // __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 74e24c7..a4e58b1 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -30,15 +29,18 @@ #include #include #include +#include +#include #if JUCE_SUPPORT_CARBON - #include #include #endif namespace juce { +#include "../../juce_audio_devices/native/juce_MidiDataConcatenator.h" + #if JUCE_SUPPORT_CARBON #include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h" #endif @@ -49,14 +51,16 @@ namespace juce #endif #if AU_LOGGING - #define log(a) Logger::writeToLog(a); + #define JUCE_AU_LOG(a) Logger::writeToLog(a); #else - #define log(a) + #define JUCE_AU_LOG(a) #endif namespace AudioUnitFormatHelpers { - static int insideCallback = 0; + #if JUCE_DEBUG + static ThreadLocalValue insideCallback; + #endif String osTypeToString (OSType type) { @@ -67,9 +71,12 @@ namespace AudioUnitFormatHelpers return String (s, 4); } - OSType stringToOSType (const String& s1) + OSType stringToOSType (String s) { - const String s (s1 + " "); + if (s.trim().length() >= 4) // (to avoid trimming leading spaces) + s = s.trim(); + + s += " "; return (((OSType) (unsigned char) s[0]) << 24) | (((OSType) (unsigned char) s[1]) << 16) @@ -79,7 +86,7 @@ namespace AudioUnitFormatHelpers static const char* auIdentifierPrefix = "AudioUnit:"; - String createAUPluginIdentifier (const ComponentDescription& desc) + String createPluginIdentifier (const AudioComponentDescription& desc) { jassert (osTypeToString ('abcd') == "abcd"); // agh, must have got the endianness wrong.. jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto @@ -103,69 +110,63 @@ namespace AudioUnitFormatHelpers return s; } - void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer) + void getNameAndManufacturer (AudioComponent comp, String& name, String& manufacturer) { - Handle componentNameHandle = NewHandle (sizeof (void*)); - Handle componentInfoHandle = NewHandle (sizeof (void*)); - - if (componentNameHandle != 0 && componentInfoHandle != 0) + CFStringRef cfName; + if (AudioComponentCopyName (comp, &cfName) == noErr) { - ComponentDescription desc; - - if (GetComponentInfo (comp, &desc, componentNameHandle, componentInfoHandle, 0) == noErr) - { - ConstStr255Param nameString = (ConstStr255Param) (*componentNameHandle); - ConstStr255Param infoString = (ConstStr255Param) (*componentInfoHandle); - - if (nameString != 0 && nameString[0] != 0) - { - const String all ((const char*) nameString + 1, nameString[0]); - DBG ("name: "+ all); - - manufacturer = all.upToFirstOccurrenceOf (":", false, false).trim(); - name = all.fromFirstOccurrenceOf (":", false, false).trim(); - } - - if (infoString != 0 && infoString[0] != 0) - { - DBG ("info: " + String ((const char*) infoString + 1, infoString[0])); - } - - if (name.isEmpty()) - name = ""; - } - - DisposeHandle (componentNameHandle); - DisposeHandle (componentInfoHandle); + name = String::fromCFString (cfName); + CFRelease (cfName); } + + if (name.containsChar (':')) + { + manufacturer = name.upToFirstOccurrenceOf (":", false, false).trim(); + name = name.fromFirstOccurrenceOf (":", false, false).trim(); + } + + if (name.isEmpty()) + name = ""; } - bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc, + bool getComponentDescFromIdentifier (const String& fileOrIdentifier, AudioComponentDescription& desc, String& name, String& version, String& manufacturer) { - zerostruct (desc); - if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix)) { String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (':'), fileOrIdentifier.lastIndexOfChar ('/')) + 1)); StringArray tokens; - tokens.addTokens (s, ",", String::empty); - tokens.trim(); + tokens.addTokens (s, ",", StringRef()); tokens.removeEmptyStrings(); if (tokens.size() == 3) { - desc.componentType = stringToOSType (tokens[0]); - desc.componentSubType = stringToOSType (tokens[1]); + zerostruct (desc); + desc.componentType = stringToOSType (tokens[0]); + desc.componentSubType = stringToOSType (tokens[1]); desc.componentManufacturer = stringToOSType (tokens[2]); - ComponentRecord* comp = FindNextComponent (0, &desc); - - if (comp != nullptr) + if (AudioComponent comp = AudioComponentFindNext (0, &desc)) { - getAUDetails (comp, name, manufacturer); + getNameAndManufacturer (comp, name, manufacturer); + + if (manufacturer.isEmpty()) + manufacturer = tokens[2]; + + if (version.isEmpty()) + { + UInt32 versionNum; + + if (AudioComponentGetVersion (comp, &versionNum) == noErr) + { + version << (int) (versionNum >> 16) << "." + << (int) ((versionNum >> 8) & 0xff) << "." + << (int) (versionNum & 0xff); + } + } + return true; } } @@ -174,22 +175,19 @@ namespace AudioUnitFormatHelpers return false; } - bool getComponentDescFromFile (const String& fileOrIdentifier, ComponentDescription& desc, + bool getComponentDescFromFile (const String& fileOrIdentifier, AudioComponentDescription& desc, String& name, String& version, String& manufacturer) { zerostruct (desc); - if (getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer)) - return true; - const File file (fileOrIdentifier); if (! file.hasFileExtension (".component")) return false; const char* const utf8 = fileOrIdentifier.toUTF8(); - CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory()); - if (url != 0) + + if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory())) { CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); @@ -214,14 +212,12 @@ namespace AudioUnitFormatHelpers if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) manufacturer = String::fromCFString ((CFStringRef) manuString); - short resFileId = CFBundleOpenBundleResourceMap (bundleRef); + const short resFileId = CFBundleOpenBundleResourceMap (bundleRef); UseResFile (resFileId); - for (int i = 1; i <= Count1Resources ('thng'); ++i) + for (ResourceIndex i = 1; i <= Count1Resources ('thng'); ++i) { - Handle h = Get1IndResource ('thng', i); - - if (h != 0) + if (Handle h = Get1IndResource ('thng', i)) { HLock (h); const uint32* const types = (const uint32*) *h; @@ -235,6 +231,10 @@ namespace AudioUnitFormatHelpers desc.componentType = types[0]; desc.componentSubType = types[1]; desc.componentManufacturer = types[2]; + + if (AudioComponent comp = AudioComponentFindNext (0, &desc)) + getNameAndManufacturer (comp, name, manufacturer); + break; } @@ -275,77 +275,92 @@ class AudioUnitPluginWindowCocoa; class AudioUnitPluginInstance : public AudioPluginInstance { public: - AudioUnitPluginInstance (const String& fileOrIdentifier) - : fileOrIdentifier (fileOrIdentifier), - wantsMidiMessages (false), wasPlaying (false), prepared (false), + AudioUnitPluginInstance (const String& fileOrId) + : fileOrIdentifier (fileOrId), + wantsMidiMessages (false), + producesMidiMessages (false), + wasPlaying (false), + prepared (false), currentBuffer (nullptr), numInputBusChannels (0), numOutputBusChannels (0), numInputBusses (0), numOutputBusses (0), - audioUnit (0) + audioUnit (nullptr), + eventListenerRef (0), + midiConcatenator (2048) { using namespace AudioUnitFormatHelpers; - try + #if JUCE_DEBUG + ++*insideCallback; + #endif + + JUCE_AU_LOG ("Opening AU: " + fileOrIdentifier); + + if (getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer) + || getComponentDescFromFile (fileOrIdentifier, componentDesc, pluginName, version, manufacturer)) { - ++insideCallback; - - log ("Opening AU: " + fileOrIdentifier); - - if (getComponentDescFromFile (fileOrIdentifier, componentDesc, pluginName, version, manufacturer)) + if (AudioComponent comp = AudioComponentFindNext (0, &componentDesc)) { - ComponentRecord* const comp = FindNextComponent (0, &componentDesc); + AudioComponentInstanceNew (comp, &audioUnit); - if (comp != nullptr) - { - audioUnit = (AudioUnit) OpenComponent (comp); - - wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice - || componentDesc.componentType == kAudioUnitType_MusicEffect; - } + wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice + || componentDesc.componentType == kAudioUnitType_MusicEffect; } + } - --insideCallback; - } - catch (...) - { - --insideCallback; - } + #if JUCE_DEBUG + --*insideCallback; + #endif } ~AudioUnitPluginInstance() { const ScopedLock sl (lock); - jassert (AudioUnitFormatHelpers::insideCallback == 0); + #if JUCE_DEBUG + // this indicates that some kind of recursive call is getting triggered that's + // deleting this plugin while it's still under construction. + jassert (AudioUnitFormatHelpers::insideCallback.get() == 0); + #endif - if (audioUnit != 0) + if (eventListenerRef != 0) { - AudioUnitUninitialize (audioUnit); - CloseComponent (audioUnit); - audioUnit = 0; + AUListenerDispose (eventListenerRef); + eventListenerRef = 0; + } + + if (audioUnit != nullptr) + { + if (prepared) + releaseResources(); + + AudioComponentInstanceDispose (audioUnit); + audioUnit = nullptr; } } - void initialise() + void initialise (double rate, int blockSize) { - refreshParameterListFromPlugin(); + refreshParameterList(); updateNumChannels(); + producesMidiMessages = canProduceMidiOutput(); setPluginCallbacks(); setPlayConfigDetails (numInputBusChannels * numInputBusses, - numOutputBusChannels * numOutputBusses, 0, 0); + numOutputBusChannels * numOutputBusses, + rate, blockSize); setLatencySamples (0); } //============================================================================== // AudioPluginInstance methods: - void fillInPluginDescription (PluginDescription& desc) const + void fillInPluginDescription (PluginDescription& desc) const override { desc.name = pluginName; desc.descriptiveName = pluginName; - desc.fileOrIdentifier = AudioUnitFormatHelpers::createAUPluginIdentifier (componentDesc); + desc.fileOrIdentifier = AudioUnitFormatHelpers::createPluginIdentifier (componentDesc); desc.uid = ((int) componentDesc.componentType) ^ ((int) componentDesc.componentSubType) ^ ((int) componentDesc.componentManufacturer); @@ -359,24 +374,42 @@ public: desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice); } - void* getPlatformSpecificData() { return audioUnit; } - const String getName() const { return pluginName; } - bool acceptsMidi() const { return wantsMidiMessages; } - bool producesMidi() const { return false; } + void* getPlatformSpecificData() override { return audioUnit; } + const String getName() const override { return pluginName; } + + bool silenceInProducesSilenceOut() const override + { + return getTailLengthSeconds() <= 0; + } + + double getTailLengthSeconds() const override + { + Float64 tail = 0; + UInt32 tailSize = sizeof (tail); + + if (audioUnit != nullptr) + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_TailTime, kAudioUnitScope_Global, + 0, &tail, &tailSize); + + return tail; + } + + bool acceptsMidi() const override { return wantsMidiMessages; } + bool producesMidi() const override { return producesMidiMessages; } //============================================================================== // AudioProcessor methods: - void prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) + void prepareToPlay (double newSampleRate, int estimatedSamplesPerBlock) override { - if (audioUnit != 0) + if (audioUnit != nullptr) { releaseResources(); updateNumChannels(); Float64 sampleRateIn = 0, sampleRateOut = 0; UInt32 sampleRateSize = sizeof (sampleRateIn); - const Float64 sr = sampleRate_; + const Float64 sr = newSampleRate; for (int i = 0; i < numInputBusses; ++i) { @@ -394,25 +427,25 @@ public: AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, i, &sr, sizeof (sr)); } + UInt32 frameSize = (UInt32) estimatedSamplesPerBlock; + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, + &frameSize, sizeof (frameSize)); + setPlayConfigDetails (numInputBusChannels * numInputBusses, numOutputBusChannels * numOutputBusses, - sampleRate_, estimatedSamplesPerBlock); + newSampleRate, estimatedSamplesPerBlock); Float64 latencySecs = 0.0; UInt32 latencySize = sizeof (latencySecs); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &latencySecs, &latencySize); - setLatencySamples (roundToInt (latencySecs * sampleRate_)); - - for (int i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); - for (int i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); - - AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); + setLatencySamples (roundToInt (latencySecs * newSampleRate)); { - AudioStreamBasicDescription stream = { 0 }; - stream.mSampleRate = sampleRate_; + AudioStreamBasicDescription stream; + zerostruct (stream); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) + stream.mSampleRate = sr; stream.mFormatID = kAudioFormatLinearPCM; stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian; stream.mFramesPerPacket = 1; @@ -443,28 +476,44 @@ public: currentBuffer = nullptr; wasPlaying = false; - prepared = (AudioUnitInitialize (audioUnit) == noErr); + resetBusses(); + + jassert (! prepared); + initialiseAudioUnit(); } } - void releaseResources() + void releaseResources() override { if (prepared) { AudioUnitUninitialize (audioUnit); - - for (int i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); - for (int i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); - + resetBusses(); AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); outputBufferList.free(); currentBuffer = nullptr; prepared = false; } + + incomingMidi.clear(); } - void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) + bool initialiseAudioUnit() + { + if (! prepared) + prepared = (AudioUnitInitialize (audioUnit) == noErr); + + return prepared; + } + + void resetBusses() + { + for (int i = 0; i < numInputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Input, i); + for (int i = 0; i < numOutputBusses; ++i) AudioUnitReset (audioUnit, kAudioUnitScope_Output, i); + } + + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override { const int numSamples = buffer.getNumSamples(); @@ -474,14 +523,16 @@ public: for (int i = 0; i < numOutputBusses; ++i) { - AudioBufferList* const abl = getAudioBufferListForBus(i); - abl->mNumberBuffers = numOutputBusChannels; - - for (int j = 0; j < numOutputBusChannels; ++j) + if (AudioBufferList* const abl = getAudioBufferListForBus(i)) { - abl->mBuffers[j].mNumberChannels = 1; - abl->mBuffers[j].mDataByteSize = sizeof (float) * numSamples; - abl->mBuffers[j].mData = buffer.getSampleData (i * numOutputBusChannels + j, 0); + abl->mNumberBuffers = numOutputBusChannels; + + for (int j = 0; j < numOutputBusChannels; ++j) + { + abl->mBuffers[j].mNumberChannels = 1; + abl->mBuffers[j].mDataByteSize = sizeof (float) * numSamples; + abl->mBuffers[j].mData = buffer.getWritePointer (i * numOutputBusChannels + j); + } } } @@ -491,9 +542,8 @@ public: { const uint8* midiEventData; int midiEventSize, midiEventPosition; - MidiBuffer::Iterator i (midiMessages); - while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) + for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (midiEventData, midiEventSize, midiEventPosition);) { if (midiEventSize <= 3) MusicDeviceMIDIEvent (audioUnit, @@ -506,10 +556,12 @@ public: midiMessages.clear(); } - AudioUnitRenderActionFlags flags = 0; for (int i = 0; i < numOutputBusses; ++i) + { + AudioUnitRenderActionFlags flags = 0; AudioUnitRender (audioUnit, &flags, &timeStamp, i, numSamples, getAudioBufferListForBus (i)); + } timeStamp.mSampleTime += numSamples; } @@ -519,99 +571,127 @@ public: for (int i = 0; i < getNumOutputChannels(); ++i) buffer.clear (i, 0, buffer.getNumSamples()); } + + if (producesMidiMessages) + { + const ScopedLock sl (midiInLock); + midiMessages.swapWith (incomingMidi); + incomingMidi.clear(); + } } //============================================================================== - bool hasEditor() const { return true; } - AudioProcessorEditor* createEditor(); + bool hasEditor() const override { return true; } + AudioProcessorEditor* createEditor() override; //============================================================================== - const String getInputChannelName (int index) const + const String getInputChannelName (int index) const override { if (isPositiveAndBelow (index, getNumInputChannels())) return "Input " + String (index + 1); - return String::empty; + return String(); } - const String getOutputChannelName (int index) const + const String getOutputChannelName (int index) const override { if (isPositiveAndBelow (index, getNumOutputChannels())) return "Output " + String (index + 1); - return String::empty; + return String(); } - bool isInputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); } - bool isOutputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumOutputChannels()); } + bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumInputChannels()); } + bool isOutputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumOutputChannels()); } //============================================================================== - int getNumParameters() { return parameterIds.size(); } + int getNumParameters() override { return parameters.size(); } - float getParameter (int index) + float getParameter (int index) override { const ScopedLock sl (lock); - Float32 value = 0.0f; + AudioUnitParameterValue value = 0; - if (audioUnit != 0 && isPositiveAndBelow (index, parameterIds.size())) - AudioUnitGetParameter (audioUnit, - (UInt32) parameterIds.getUnchecked (index), - kAudioUnitScope_Global, 0, - &value); + if (audioUnit != nullptr) + { + if (const ParamInfo* p = parameters[index]) + { + AudioUnitGetParameter (audioUnit, + p->paramID, + kAudioUnitScope_Global, 0, + &value); + + value = (value - p->minValue) / (p->maxValue - p->minValue); + } + } return value; } - void setParameter (int index, float newValue) + void setParameter (int index, float newValue) override { const ScopedLock sl (lock); - if (audioUnit != 0 && isPositiveAndBelow (index, parameterIds.size())) - AudioUnitSetParameter (audioUnit, - (UInt32) parameterIds.getUnchecked (index), - kAudioUnitScope_Global, 0, - newValue, 0); + if (audioUnit != nullptr) + { + if (const ParamInfo* p = parameters[index]) + { + AudioUnitSetParameter (audioUnit, p->paramID, kAudioUnitScope_Global, 0, + p->minValue + (p->maxValue - p->minValue) * newValue, 0); + + sendParameterChangeEvent (index); + } + } } - const String getParameterName (int index) + void sendParameterChangeEvent (int index) { - AudioUnitParameterInfo info = { 0 }; - UInt32 sz = sizeof (info); - String name; + jassert (audioUnit != nullptr); - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_ParameterInfo, - kAudioUnitScope_Global, - parameterIds [index], &info, &sz) == noErr) - { - if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) - name = String::fromCFString (info.cfNameString); - else - name = String (info.name, sizeof (info.name)); - } + const ParamInfo& p = *parameters.getUnchecked (index); - return name; + AudioUnitEvent ev; + ev.mEventType = kAudioUnitEvent_ParameterValueChange; + ev.mArgument.mParameter.mAudioUnit = audioUnit; + ev.mArgument.mParameter.mParameterID = p.paramID; + ev.mArgument.mParameter.mScope = kAudioUnitScope_Global; + ev.mArgument.mParameter.mElement = 0; + + AUEventListenerNotify (nullptr, nullptr, &ev); } - const String getParameterText (int index) { return String (getParameter (index)); } - - bool isParameterAutomatable (int index) const + void sendAllParametersChangedEvents() { - AudioUnitParameterInfo info; - UInt32 sz = sizeof (info); + jassert (audioUnit != nullptr); - if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterInfo, - kAudioUnitScope_Global, parameterIds [index], &info, &sz) == noErr) - { - return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; - } + AudioUnitParameter param; + param.mAudioUnit = audioUnit; + param.mParameterID = kAUParameterListener_AnyParameter; - return true; + AUParameterListenerNotify (nullptr, nullptr, ¶m); + } + + const String getParameterName (int index) override + { + if (const ParamInfo* p = parameters[index]) + return p->name; + + return String(); + } + + const String getParameterText (int index) override { return String (getParameter (index)); } + + bool isParameterAutomatable (int index) const override + { + if (const ParamInfo* p = parameters[index]) + return p->automatable; + + return false; } //============================================================================== - int getNumPrograms() + int getNumPrograms() override { CFArrayRef presets; UInt32 sz = sizeof (CFArrayRef); @@ -627,7 +707,7 @@ public: return num; } - int getCurrentProgram() + int getCurrentProgram() override { AUPreset current; current.presetNumber = 0; @@ -639,7 +719,7 @@ public: return current.presetNumber; } - void setCurrentProgram (int newIndex) + void setCurrentProgram (int newIndex) override { AUPreset current; current.presetNumber = newIndex; @@ -647,9 +727,11 @@ public: AudioUnitSetProperty (audioUnit, kAudioUnitProperty_PresentPreset, kAudioUnitScope_Global, 0, ¤t, sizeof (AUPreset)); + + sendAllParametersChangedEvents(); } - const String getProgramName (int index) + const String getProgramName (int index) override { String s; CFArrayRef presets; @@ -660,12 +742,13 @@ public: { for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i) { - const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i); - - if (p != nullptr && p->presetNumber == index) + if (const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i)) { - s = String::fromCFString (p->presetName); - break; + if (p->presetNumber == index) + { + s = String::fromCFString (p->presetName); + break; + } } } @@ -675,18 +758,18 @@ public: return s; } - void changeProgramName (int index, const String& newName) + void changeProgramName (int /*index*/, const String& /*newName*/) override { jassertfalse; // xxx not implemented! } //============================================================================== - void getStateInformation (MemoryBlock& destData) + void getStateInformation (MemoryBlock& destData) override { getCurrentProgramStateInformation (destData); } - void getCurrentProgramStateInformation (MemoryBlock& destData) + void getCurrentProgramStateInformation (MemoryBlock& destData) override { CFPropertyListRef propertyList = 0; UInt32 sz = sizeof (CFPropertyListRef); @@ -713,40 +796,40 @@ public: } } - void setStateInformation (const void* data, int sizeInBytes) + void setStateInformation (const void* data, int sizeInBytes) override { setCurrentProgramStateInformation (data, sizeInBytes); } - void setCurrentProgramStateInformation (const void* data, int sizeInBytes) + void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override { - CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, - (const UInt8*) data, - sizeInBytes, - kCFAllocatorNull); + CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) data, + sizeInBytes, kCFAllocatorNull); CFReadStreamOpen (stream); CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; - CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, - stream, - 0, - kCFPropertyListImmutable, - &format, - 0); + CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, stream, 0, + kCFPropertyListImmutable, &format, 0); CFRelease (stream); if (propertyList != 0) - AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_ClassInfo, - kAudioUnitScope_Global, + { + initialiseAudioUnit(); + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &propertyList, sizeof (propertyList)); + + sendAllParametersChangedEvents(); + + CFRelease (propertyList); + } } - void refreshParameterListFromPlugin() + void refreshParameterList() override { - parameterIds.clear(); + parameters.clear(); - if (audioUnit != 0) + if (audioUnit != nullptr) { UInt32 paramListSize = 0; AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, @@ -754,25 +837,67 @@ public: if (paramListSize > 0) { - parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int)); + const size_t numParams = paramListSize / sizeof (int); + + HeapBlock ids; + ids.calloc (numParams); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, - 0, parameterIds.getRawDataPointer(), ¶mListSize); + 0, ids, ¶mListSize); + + for (int i = 0; i < numParams; ++i) + { + AudioUnitParameterInfo info; + UInt32 sz = sizeof (info); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + ids[i], &info, &sz) == noErr) + { + ParamInfo* const param = new ParamInfo(); + parameters.add (param); + param->paramID = ids[i]; + param->minValue = info.minValue; + param->maxValue = info.maxValue; + param->automatable = (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; + + if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) + { + param->name = String::fromCFString (info.cfNameString); + + if ((info.flags & kAudioUnitParameterFlag_CFNameRelease) != 0) + CFRelease (info.cfNameString); + } + else + { + param->name = String (info.name, sizeof (info.name)); + } + } + } } } } + void handleIncomingMidiMessage (void*, const MidiMessage& message) + { + const ScopedLock sl (midiInLock); + incomingMidi.addEvent (message, 0); + } + + void handlePartialSysexMessage (void*, const uint8*, int, double) {} + private: //============================================================================== friend class AudioUnitPluginWindowCarbon; friend class AudioUnitPluginWindowCocoa; friend class AudioUnitPluginFormat; - ComponentDescription componentDesc; + AudioComponentDescription componentDesc; String pluginName, manufacturer, version; String fileOrIdentifier; CriticalSection lock; - bool wantsMidiMessages, wasPlaying, prepared; + bool wantsMidiMessages, producesMidiMessages, wasPlaying, prepared; HeapBlock outputBufferList; AudioTimeStamp timeStamp; @@ -780,14 +905,30 @@ private: int numInputBusChannels, numOutputBusChannels, numInputBusses, numOutputBusses; AudioUnit audioUnit; - Array parameterIds; + AUEventListenerRef eventListenerRef; + + struct ParamInfo + { + UInt32 paramID; + String name; + AudioUnitParameterValue minValue, maxValue; + bool automatable; + }; + + OwnedArray parameters; + + MidiDataConcatenator midiConcatenator; + CriticalSection midiInLock; + MidiBuffer incomingMidi; void setPluginCallbacks() { - if (audioUnit != 0) + if (audioUnit != nullptr) { { - AURenderCallbackStruct info = { 0 }; + AURenderCallbackStruct info; + zerostruct (info); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) + info.inputProcRefCon = this; info.inputProc = renderGetInputCallback; @@ -796,39 +937,122 @@ private: kAudioUnitScope_Input, i, &info, sizeof (info)); } + if (producesMidiMessages) { - HostCallbackInfo info = { 0 }; + AUMIDIOutputCallbackStruct info; + zerostruct (info); + + info.userData = this; + info.midiOutputCallback = renderMidiOutputCallback; + + producesMidiMessages = (AudioUnitSetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallback, + kAudioUnitScope_Global, 0, &info, sizeof (info)) == noErr); + } + + { + HostCallbackInfo info; + zerostruct (info); + info.hostUserData = this; info.beatAndTempoProc = getBeatAndTempoCallback; info.musicalTimeLocationProc = getMusicalTimeLocationCallback; info.transportStateProc = getTransportStateCallback; - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, - 0, &info, sizeof (info)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, + kAudioUnitScope_Global, 0, &info, sizeof (info)); + } + + AUEventListenerCreate (eventListenerCallback, this, + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 + CFRunLoopGetMain(), + #else + nullptr, + #endif + kCFRunLoopDefaultMode, 0, 0, &eventListenerRef); + + for (int i = 0; i < parameters.size(); ++i) + { + const ParamInfo& p = *parameters.getUnchecked(i); + + AudioUnitParameter paramToAdd; + paramToAdd.mAudioUnit = audioUnit; + paramToAdd.mParameterID = p.paramID; + paramToAdd.mScope = kAudioUnitScope_Global; + paramToAdd.mElement = 0; + + AudioUnitEvent event; + event.mArgument.mParameter = paramToAdd; + + event.mEventType = kAudioUnitEvent_ParameterValueChange; + AUEventListenerAddEventType (eventListenerRef, nullptr, &event); + + event.mEventType = kAudioUnitEvent_BeginParameterChangeGesture; + AUEventListenerAddEventType (eventListenerRef, nullptr, &event); + + event.mEventType = kAudioUnitEvent_EndParameterChangeGesture; + AUEventListenerAddEventType (eventListenerRef, nullptr, &event); } } } + void eventCallback (const AudioUnitEvent& event, AudioUnitParameterValue newValue) + { + switch (event.mEventType) + { + case kAudioUnitEvent_ParameterValueChange: + for (int i = 0; i < parameters.size(); ++i) + { + const ParamInfo& p = *parameters.getUnchecked(i); + + if (p.paramID == event.mArgument.mParameter.mParameterID) + { + sendParamChangeMessageToListeners (i, (newValue - p.minValue) / (p.maxValue - p.minValue)); + break; + } + } + + break; + + case kAudioUnitEvent_BeginParameterChangeGesture: + beginParameterChangeGesture (event.mArgument.mParameter.mParameterID); + break; + + case kAudioUnitEvent_EndParameterChangeGesture: + endParameterChangeGesture (event.mArgument.mParameter.mParameterID); + break; + + default: + break; + } + } + + static void eventListenerCallback (void* userRef, void*, const AudioUnitEvent* event, + UInt64, AudioUnitParameterValue value) + { + jassert (event != nullptr); + static_cast (userRef)->eventCallback (*event, value); + } //============================================================================== - OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, + OSStatus renderGetInput (AudioUnitRenderActionFlags*, + const AudioTimeStamp*, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) const { if (currentBuffer != nullptr) { - jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling + // if this ever happens, might need to add extra handling + jassert (inNumberFrames == (UInt32) currentBuffer->getNumSamples()); - for (int i = 0; i < ioData->mNumberBuffers; ++i) + for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i) { const int bufferChannel = inBusNumber * numInputBusChannels + i; if (bufferChannel < currentBuffer->getNumChannels()) { memcpy (ioData->mBuffers[i].mData, - currentBuffer->getSampleData (bufferChannel, 0), + currentBuffer->getReadPointer (bufferChannel), sizeof (float) * inNumberFrames); } else @@ -842,6 +1066,29 @@ private: return noErr; } + OSStatus renderMidiOutput (const MIDIPacketList* pktlist) + { + if (pktlist != nullptr && pktlist->numPackets) + { + const double time = Time::getMillisecondCounterHiRes() * 0.001; + const MIDIPacket* packet = &pktlist->packet[0]; + + for (UInt32 i = 0; i < pktlist->numPackets; ++i) + { + midiConcatenator.pushMidiData (packet->data, (int) packet->length, time, (void*) nullptr, *this); + packet = MIDIPacketNext (packet); + } + } + + return noErr; + } + + template + static void setIfNotNull (Type1* p, Type2 value) noexcept + { + if (p != nullptr) *p = value; + } + OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const { AudioPlayHead* const ph = getPlayHead(); @@ -849,13 +1096,13 @@ private: if (ph != nullptr && ph->getCurrentPosition (result)) { - if (outCurrentBeat != nullptr) *outCurrentBeat = result.ppqPosition; - if (outCurrentTempo != nullptr) *outCurrentTempo = result.bpm; + setIfNotNull (outCurrentBeat, result.ppqPosition); + setIfNotNull (outCurrentTempo, result.bpm); } else { - if (outCurrentBeat != nullptr) *outCurrentBeat = 0; - if (outCurrentTempo != nullptr) *outCurrentTempo = 120.0; + setIfNotNull (outCurrentBeat, 0); + setIfNotNull (outCurrentTempo, 120.0); } return noErr; @@ -869,17 +1116,17 @@ private: if (ph != nullptr && ph->getCurrentPosition (result)) { - if (outTimeSig_Numerator != nullptr) *outTimeSig_Numerator = result.timeSigNumerator; - if (outTimeSig_Denominator != nullptr) *outTimeSig_Denominator = result.timeSigDenominator; - if (outDeltaSampleOffsetToNextBeat != nullptr) *outDeltaSampleOffsetToNextBeat = 0; //xxx - if (outCurrentMeasureDownBeat != nullptr) *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong + setIfNotNull (outTimeSig_Numerator, result.timeSigNumerator); + setIfNotNull (outTimeSig_Denominator, result.timeSigDenominator); + setIfNotNull (outDeltaSampleOffsetToNextBeat, 0); //xxx + setIfNotNull (outCurrentMeasureDownBeat, result.ppqPositionOfLastBarStart); //xxx wrong } else { - if (outDeltaSampleOffsetToNextBeat != nullptr) *outDeltaSampleOffsetToNextBeat = 0; - if (outTimeSig_Numerator != nullptr) *outTimeSig_Numerator = 4; - if (outTimeSig_Denominator != nullptr) *outTimeSig_Denominator = 4; - if (outCurrentMeasureDownBeat != nullptr) *outCurrentMeasureDownBeat = 0; + setIfNotNull (outDeltaSampleOffsetToNextBeat, 0); + setIfNotNull (outTimeSig_Numerator, 4); + setIfNotNull (outTimeSig_Denominator, 4); + setIfNotNull (outCurrentMeasureDownBeat, 0); } return noErr; @@ -894,8 +1141,7 @@ private: if (ph != nullptr && ph->getCurrentPosition (result)) { - if (outIsPlaying != nullptr) - *outIsPlaying = result.isPlaying; + setIfNotNull (outIsPlaying, result.isPlaying); if (outTransportStateChanged != nullptr) { @@ -903,55 +1149,58 @@ private: wasPlaying = result.isPlaying; } - if (outCurrentSampleInTimeLine != nullptr) - *outCurrentSampleInTimeLine = roundToInt (result.timeInSeconds * getSampleRate()); - - if (outIsCycling != nullptr) *outIsCycling = false; - if (outCycleStartBeat != nullptr) *outCycleStartBeat = 0; - if (outCycleEndBeat != nullptr) *outCycleEndBeat = 0; + setIfNotNull (outCurrentSampleInTimeLine, result.timeInSamples); + setIfNotNull (outIsCycling, false); + setIfNotNull (outCycleStartBeat, 0); + setIfNotNull (outCycleEndBeat, 0); } else { - if (outIsPlaying != nullptr) *outIsPlaying = false; - if (outTransportStateChanged != nullptr) *outTransportStateChanged = false; - if (outCurrentSampleInTimeLine != nullptr) *outCurrentSampleInTimeLine = 0; - if (outIsCycling != nullptr) *outIsCycling = false; - if (outCycleStartBeat != nullptr) *outCycleStartBeat = 0; - if (outCycleEndBeat != nullptr) *outCycleEndBeat = 0; + setIfNotNull (outIsPlaying, false); + setIfNotNull (outTransportStateChanged, false); + setIfNotNull (outCurrentSampleInTimeLine, 0); + setIfNotNull (outIsCycling, false); + setIfNotNull (outCycleStartBeat, 0); + setIfNotNull (outCycleEndBeat, 0); } return noErr; } //============================================================================== - static OSStatus renderGetInputCallback (void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, + static OSStatus renderGetInputCallback (void* hostRef, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { - return static_cast (inRefCon) + return static_cast (hostRef) ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); } - static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) + static OSStatus renderMidiOutputCallback (void* hostRef, const AudioTimeStamp*, UInt32 /*midiOutNum*/, + const struct MIDIPacketList* pktlist) { - return static_cast (inHostUserData) - ->getBeatAndTempo (outCurrentBeat, outCurrentTempo); + return static_cast (hostRef)->renderMidiOutput (pktlist); } - static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat, + static OSStatus getBeatAndTempoCallback (void* hostRef, Float64* outCurrentBeat, Float64* outCurrentTempo) + { + return static_cast (hostRef)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); + } + + static OSStatus getMusicalTimeLocationCallback (void* hostRef, UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) { - return static_cast (inHostUserData) + return static_cast (hostRef) ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, outTimeSig_Denominator, outCurrentMeasureDownBeat); } - static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged, + static OSStatus getTransportStateCallback (void* hostRef, Boolean* outIsPlaying, Boolean* outTransportStateChanged, Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, Float64* outCycleStartBeat, Float64* outCycleEndBeat) { - return static_cast (inHostUserData) + return static_cast (hostRef) ->getTransportState (outIsPlaying, outTransportStateChanged, outCurrentSampleInTimeLine, outIsCycling, outCycleStartBeat, outCycleEndBeat); } @@ -981,7 +1230,7 @@ private: void updateNumChannels() { - numInputBusses = getElementCount (kAudioUnitScope_Input); + numInputBusses = getElementCount (kAudioUnitScope_Input); numOutputBusses = getElementCount (kAudioUnitScope_Output); AUChannelInfo supportedChannels [128]; @@ -996,7 +1245,7 @@ private: int maximumNumIns = 0; int maximumNumOuts = 0; - for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) + for (int i = 0; i < (int) (supportedChannelsSize / sizeof (AUChannelInfo)); ++i) { const int inChannels = (int) supportedChannels[i].inChannels; const int outChannels = (int) supportedChannels[i].outChannels; @@ -1038,19 +1287,41 @@ private: } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance); + bool canProduceMidiOutput() + { + UInt32 dataSize = 0; + Boolean isWritable = false; + + if (AudioUnitGetPropertyInfo (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo, + kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr + && dataSize != 0) + { + CFArrayRef midiArray; + + if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_MIDIOutputCallbackInfo, + kAudioUnitScope_Global, 0, &midiArray, &dataSize) == noErr) + { + bool result = (CFArrayGetCount (midiArray) > 0); + CFRelease (midiArray); + return result; + } + } + + return false; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginInstance) }; //============================================================================== -class AudioUnitPluginWindowCocoa : public AudioProcessorEditor, - public Timer +class AudioUnitPluginWindowCocoa : public AudioProcessorEditor { public: - AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& plugin_, const bool createGenericViewIfNeeded) - : AudioProcessorEditor (&plugin_), - plugin (plugin_) + AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& p, bool createGenericViewIfNeeded) + : AudioProcessorEditor (&p), + plugin (p) { - addAndMakeVisible (&wrapper); + addAndMakeVisible (wrapper); setOpaque (true); setVisible (true); @@ -1061,52 +1332,46 @@ public: ~AudioUnitPluginWindowCocoa() { - const bool wasValid = isValid(); - - wrapper.setVisible (false); - removeChildComponent (&wrapper); - wrapper.setView (nil); - - if (wasValid) + if (isValid()) + { + wrapper.setVisible (false); + removeChildComponent (&wrapper); + wrapper.setView (nil); plugin.editorBeingDeleted (this); + } } bool isValid() const { return wrapper.getView() != nil; } - void paint (Graphics& g) + void paint (Graphics& g) override { g.fillAll (Colours::white); } - void resized() + void resized() override { wrapper.setSize (getWidth(), getHeight()); } - void timerCallback() - { - wrapper.resizeToFitView(); - startTimer (jmin (713, getTimerInterval() + 51)); - } - - void childBoundsChanged (Component* child) + void childBoundsChanged (Component*) override { setSize (wrapper.getWidth(), wrapper.getHeight()); - startTimer (70); } private: AudioUnitPluginInstance& plugin; - NSViewComponent wrapper; + + AutoResizingNSViewComponent wrapper; bool createView (const bool createGenericViewIfNeeded) { + if (! plugin.initialiseAudioUnit()) + return false; + NSView* pluginView = nil; UInt32 dataSize = 0; Boolean isWritable = false; - AudioUnitInitialize (plugin.audioUnit); - if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr && dataSize != 0 @@ -1142,11 +1407,11 @@ private: } } - if (createGenericViewIfNeeded && (pluginView == 0)) + if (createGenericViewIfNeeded && (pluginView == nil)) { { // This forces CoreAudio.component to be loaded, otherwise the AUGenericView will assert - ComponentDescription desc; + AudioComponentDescription desc; String name, version, manufacturer; AudioUnitFormatHelpers::getComponentDescFromIdentifier ("AudioUnit:Output/auou,genr,appl", desc, name, version, manufacturer); @@ -1158,10 +1423,7 @@ private: wrapper.setView (pluginView); if (pluginView != nil) - { - timerCallback(); - startTimer (70); - } + wrapper.resizeToFitView(); return pluginView != nil; } @@ -1173,11 +1435,11 @@ private: class AudioUnitPluginWindowCarbon : public AudioProcessorEditor { public: - AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& plugin_) - : AudioProcessorEditor (&plugin_), - plugin (plugin_), - componentRecord (nullptr), - viewComponent (0) + AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& p) + : AudioProcessorEditor (&p), + plugin (p), + audioComponent (nullptr), + viewComponent (nullptr) { addAndMakeVisible (innerWrapper = new InnerWrapperComponent (*this)); @@ -1190,12 +1452,12 @@ public: kAudioUnitScope_Global, 0, &propertySize, NULL) == noErr && propertySize > 0) { - ComponentDescription views [propertySize / sizeof (ComponentDescription)]; + HeapBlock views (propertySize / sizeof (AudioComponentDescription)); if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, 0, &views[0], &propertySize) == noErr) { - componentRecord = FindNextComponent (0, &views[0]); + audioComponent = AudioComponentFindNext (nullptr, &views[0]); } } } @@ -1208,92 +1470,83 @@ public: plugin.editorBeingDeleted (this); } - bool isValid() const noexcept { return componentRecord != nullptr; } + bool isValid() const noexcept { return audioComponent != nullptr; } //============================================================================== - void paint (Graphics& g) + void paint (Graphics& g) override { g.fillAll (Colours::black); } - void resized() + void resized() override { if (innerWrapper != nullptr) innerWrapper->setSize (getWidth(), getHeight()); } //============================================================================== - bool keyStateChanged (bool) { return false; } - bool keyPressed (const KeyPress&) { return false; } + bool keyStateChanged (bool) override { return false; } + bool keyPressed (const KeyPress&) override { return false; } //============================================================================== AudioUnit getAudioUnit() const { return plugin.audioUnit; } AudioUnitCarbonView getViewComponent() { - if (viewComponent == 0 && componentRecord != nullptr) - viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); + if (viewComponent == nullptr && audioComponent != nullptr) + AudioComponentInstanceNew (audioComponent, &viewComponent); return viewComponent; } void closeViewComponent() { - if (viewComponent != 0) + if (viewComponent != nullptr) { - log ("Closing AU GUI: " + plugin.getName()); + JUCE_AU_LOG ("Closing AU GUI: " + plugin.getName()); - CloseComponent (viewComponent); - viewComponent = 0; + AudioComponentInstanceDispose (viewComponent); + viewComponent = nullptr; } } private: //============================================================================== AudioUnitPluginInstance& plugin; - ComponentRecord* componentRecord; + AudioComponent audioComponent; AudioUnitCarbonView viewComponent; //============================================================================== class InnerWrapperComponent : public CarbonViewWrapperComponent { public: - InnerWrapperComponent (AudioUnitPluginWindowCarbon& owner_) - : owner (owner_) - { - } + InnerWrapperComponent (AudioUnitPluginWindowCarbon& w) : owner (w) {} ~InnerWrapperComponent() { deleteWindow(); } - HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) + HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) override { - log ("Opening AU GUI: " + owner.plugin.getName()); + JUCE_AU_LOG ("Opening AU GUI: " + owner.plugin.getName()); - AudioUnitCarbonView viewComponent = owner.getViewComponent(); + AudioUnitCarbonView carbonView = owner.getViewComponent(); - if (viewComponent == 0) + if (carbonView == 0) return 0; Float32Point pos = { 0, 0 }; Float32Point size = { 250, 200 }; - HIViewRef pluginView = 0; - AudioUnitCarbonViewCreate (viewComponent, - owner.getAudioUnit(), - windowRef, - rootView, - &pos, - &size, - (ControlRef*) &pluginView); + AudioUnitCarbonViewCreate (carbonView, owner.getAudioUnit(), windowRef, rootView, + &pos, &size, (ControlRef*) &pluginView); return pluginView; } - void removeView (HIViewRef) + void removeView (HIViewRef) override { owner.closeViewComponent(); } @@ -1301,13 +1554,13 @@ private: private: AudioUnitPluginWindowCarbon& owner; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InnerWrapperComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InnerWrapperComponent) }; friend class InnerWrapperComponent; ScopedPointer innerWrapper; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginWindowCarbon); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioUnitPluginWindowCarbon) }; #endif @@ -1317,7 +1570,7 @@ AudioProcessorEditor* AudioUnitPluginInstance::createEditor() { ScopedPointer w (new AudioUnitPluginWindowCocoa (*this, false)); - if (! static_cast (w.get())->isValid()) + if (! static_cast (w.get())->isValid()) w = nullptr; #if JUCE_SUPPORT_CARBON @@ -1325,7 +1578,7 @@ AudioProcessorEditor* AudioUnitPluginInstance::createEditor() { w = new AudioUnitPluginWindowCarbon (*this); - if (! static_cast (w.get())->isValid()) + if (! static_cast (w.get())->isValid()) w = nullptr; } #endif @@ -1347,7 +1600,7 @@ AudioUnitPluginFormat::~AudioUnitPluginFormat() { } -void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, +void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) { if (! fileMightContainThisPluginType (fileOrIdentifier)) @@ -1359,14 +1612,10 @@ void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & try { - ScopedPointer createdInstance (createInstanceFromDescription (desc)); - AudioUnitPluginInstance* const auInstance = dynamic_cast ((AudioPluginInstance*) createdInstance); + ScopedPointer createdInstance (createInstanceFromDescription (desc, 44100.0, 512)); - if (auInstance != nullptr) - { - auInstance->fillInPluginDescription (desc); - results.add (new PluginDescription (desc)); - } + if (AudioUnitPluginInstance* auInstance = dynamic_cast (createdInstance.get())) + results.add (new PluginDescription (auInstance->getPluginDescription())); } catch (...) { @@ -1374,15 +1623,15 @@ void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & } } -AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc, double rate, int blockSize) { if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { - ScopedPointer result (new AudioUnitPluginInstance (desc.fileOrIdentifier)); + ScopedPointer result (new AudioUnitPluginInstance (desc.fileOrIdentifier)); - if (result->audioUnit != 0) + if (result->audioUnit != nullptr) { - result->initialise(); + result->initialise (rate, blockSize); return result.release(); } } @@ -1390,21 +1639,22 @@ AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const return nullptr; } -StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/, - const bool /*recursive*/) +StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath&, bool /*recursive*/) { StringArray result; - ComponentRecord* comp = nullptr; + AudioComponent comp = nullptr; for (;;) { - ComponentDescription desc = { 0 }; - comp = FindNextComponent (comp, &desc); + AudioComponentDescription desc; + zerostruct (desc); - if (comp == 0) + comp = AudioComponentFindNext (comp, &desc); + + if (comp == nullptr) break; - GetComponentInfo (comp, &desc, 0, 0, 0); + AudioComponentGetDescription (comp, &desc); if (desc.componentType == kAudioUnitType_MusicDevice || desc.componentType == kAudioUnitType_MusicEffect @@ -1412,9 +1662,7 @@ StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& || desc.componentType == kAudioUnitType_Generator || desc.componentType == kAudioUnitType_Panner) { - const String s (AudioUnitFormatHelpers::createAUPluginIdentifier (desc)); - DBG (s); - result.add (s); + result.add (AudioUnitFormatHelpers::createPluginIdentifier (desc)); } } @@ -1423,13 +1671,13 @@ StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { - ComponentDescription desc; + AudioComponentDescription desc; String name, version, manufacturer; if (AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer)) - return FindNextComponent (0, &desc) != 0; + return AudioComponentFindNext (nullptr, &desc) != nullptr; - const File f (fileOrIdentifier); + const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); return f.hasFileExtension (".component") && f.isDirectory(); @@ -1437,7 +1685,7 @@ bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOr String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { - ComponentDescription desc; + AudioComponentDescription desc; String name, version, manufacturer; AudioUnitFormatHelpers::getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer); @@ -1447,19 +1695,29 @@ String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileO return name; } +bool AudioUnitPluginFormat::pluginNeedsRescanning (const PluginDescription& desc) +{ + AudioComponentDescription newDesc; + String name, version, manufacturer; + + return ! (AudioUnitFormatHelpers::getComponentDescFromIdentifier (desc.fileOrIdentifier, newDesc, + name, version, manufacturer) + && version == desc.version); +} + bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) { if (desc.fileOrIdentifier.startsWithIgnoreCase (AudioUnitFormatHelpers::auIdentifierPrefix)) return fileMightContainThisPluginType (desc.fileOrIdentifier); - else - return File (desc.fileOrIdentifier).exists(); + + return File (desc.fileOrIdentifier).exists(); } FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() { - return FileSearchPath ("/(Default AudioUnit locations)"); + return FileSearchPath(); } -#undef log +#undef JUCE_AU_LOG #endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_DirectXPluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_DirectXPluginFormat.h deleted file mode 100644 index 80d9f00..0000000 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_DirectXPluginFormat.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#ifndef __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ -#define __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ - -#include "../format/juce_AudioPluginFormat.h" - -#if JUCE_PLUGINHOST_DX && JUCE_WINDOWS - - -// Sorry, this file is just a placeholder at the moment!... - - -//============================================================================== -/** - Implements a plugin format manager for DirectX plugins. -*/ -class JUCE_API DirectXPluginFormat : public AudioPluginFormat -{ -public: - //============================================================================== - DirectXPluginFormat(); - ~DirectXPluginFormat(); - - //============================================================================== - String getName() const { return "DirectX"; } - void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); - AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const String& fileOrIdentifier); - String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } - FileSearchPath getDefaultLocationsToSearch(); - -private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectXPluginFormat); -}; - -#endif - -#endif // __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp new file mode 100644 index 0000000..81b369a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -0,0 +1,703 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX + +} // (juce namespace) + +#include + +namespace juce +{ + +static int shellLADSPAUIDToCreate = 0; +static int insideLADSPACallback = 0; + +#define JUCE_LADSPA_LOGGING 1 + +#if JUCE_LADSPA_LOGGING + #define JUCE_LADSPA_LOG(x) Logger::writeToLog (x); +#else + #define JUCE_LADSPA_LOG(x) +#endif + +//============================================================================== +class LADSPAModuleHandle : public ReferenceCountedObject +{ +public: + LADSPAModuleHandle (const File& f) + : file (f), moduleMain (nullptr) + { + getActiveModules().add (this); + } + + ~LADSPAModuleHandle() + { + getActiveModules().removeFirstMatchingValue (this); + close(); + } + + typedef ReferenceCountedObjectPtr Ptr; + + static Array & getActiveModules() + { + static Array activeModules; + return activeModules; + } + + static LADSPAModuleHandle* findOrCreateModule (const File& file) + { + for (int i = getActiveModules().size(); --i >= 0;) + { + LADSPAModuleHandle* const module = getActiveModules().getUnchecked(i); + + if (module->file == file) + return module; + } + + ++insideLADSPACallback; + shellLADSPAUIDToCreate = 0; + + JUCE_LADSPA_LOG ("Loading LADSPA module: " + file.getFullPathName()); + + ScopedPointer m (new LADSPAModuleHandle (file)); + + if (! m->open()) + m = nullptr; + + --insideLADSPACallback; + + return m.release(); + } + + File file; + LADSPA_Descriptor_Function moduleMain; + +private: + DynamicLibrary module; + + bool open() + { + module.open (file.getFullPathName()); + moduleMain = (LADSPA_Descriptor_Function) module.getFunction ("ladspa_descriptor"); + return moduleMain != nullptr; + } + + void close() + { + module.close(); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAModuleHandle) +}; + + +//============================================================================== +class LADSPAPluginInstance : public AudioPluginInstance +{ +public: + LADSPAPluginInstance (const LADSPAModuleHandle::Ptr& m) + : plugin (nullptr), handle (nullptr), initialised (false), + tempBuffer (1, 1), module (m) + { + ++insideLADSPACallback; + + name = module->file.getFileNameWithoutExtension(); + + JUCE_LADSPA_LOG ("Creating LADSPA instance: " + name); + + if (module->moduleMain != nullptr) + { + plugin = module->moduleMain (shellLADSPAUIDToCreate); + + if (plugin == nullptr) + { + JUCE_LADSPA_LOG ("Cannot find any valid descriptor in shared library"); + --insideLADSPACallback; + return; + } + } + else + { + JUCE_LADSPA_LOG ("Cannot find any valid plugin in shared library"); + --insideLADSPACallback; + return; + } + + const double sampleRate = getSampleRate() > 0 ? getSampleRate() : 44100.0; + + handle = plugin->instantiate (plugin, (uint32) sampleRate); + + --insideLADSPACallback; + } + + ~LADSPAPluginInstance() + { + const ScopedLock sl (lock); + + jassert (insideLADSPACallback == 0); + + if (handle != nullptr && plugin != nullptr && plugin->cleanup != nullptr) + plugin->cleanup (handle); + + initialised = false; + module = nullptr; + plugin = nullptr; + handle = nullptr; + } + + void initialise (double initialSampleRate, int initialBlockSize) + { + setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize); + + if (initialised || plugin == nullptr || handle == nullptr) + return; + + JUCE_LADSPA_LOG ("Initialising LADSPA: " + name); + + initialised = true; + + inputs.clear(); + outputs.clear(); + parameters.clear(); + + for (unsigned int i = 0; i < plugin->PortCount; ++i) + { + const LADSPA_PortDescriptor portDesc = plugin->PortDescriptors[i]; + + if ((portDesc & LADSPA_PORT_CONTROL) != 0) + parameters.add (i); + + if ((portDesc & LADSPA_PORT_AUDIO) != 0) + { + if ((portDesc & LADSPA_PORT_INPUT) != 0) inputs.add (i); + if ((portDesc & LADSPA_PORT_OUTPUT) != 0) outputs.add (i); + } + } + + parameterValues.calloc (parameters.size()); + + for (int i = 0; i < parameters.size(); ++i) + plugin->connect_port (handle, parameters[i], &(parameterValues[i].scaled)); + + setPlayConfigDetails (inputs.size(), outputs.size(), initialSampleRate, initialBlockSize); + + setCurrentProgram (0); + setLatencySamples (0); + + // Some plugins crash if this doesn't happen: + if (plugin->activate != nullptr) plugin->activate (handle); + if (plugin->deactivate != nullptr) plugin->deactivate (handle); + } + + //============================================================================== + // AudioPluginInstance methods: + + void fillInPluginDescription (PluginDescription& desc) const + { + desc.name = getName(); + desc.fileOrIdentifier = module->file.getFullPathName(); + desc.uid = getUID(); + desc.lastFileModTime = module->file.getLastModificationTime(); + desc.pluginFormatName = "LADSPA"; + desc.category = getCategory(); + desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String(); + desc.version = getVersion(); + desc.numInputChannels = getNumInputChannels(); + desc.numOutputChannels = getNumOutputChannels(); + desc.isInstrument = false; + } + + const String getName() const + { + if (plugin != nullptr && plugin->Label != nullptr) + return plugin->Label; + + return name; + } + + int getUID() const + { + if (plugin != nullptr && plugin->UniqueID != 0) + return (int) plugin->UniqueID; + + return module->file.hashCode(); + } + + String getVersion() const { return LADSPA_VERSION; } + String getCategory() const { return "Effect"; } + + bool acceptsMidi() const { return false; } + bool producesMidi() const { return false; } + + bool silenceInProducesSilenceOut() const { return plugin == nullptr; } // ..any way to get a proper answer for these? + double getTailLengthSeconds() const { return 0.0; } + + //============================================================================== + void prepareToPlay (double newSampleRate, int samplesPerBlockExpected) + { + setLatencySamples (0); + + initialise (newSampleRate, samplesPerBlockExpected); + + if (initialised) + { + tempBuffer.setSize (jmax (1, outputs.size()), samplesPerBlockExpected); + + // dodgy hack to force some plugins to initialise the sample rate.. + if (getNumParameters() > 0) + { + const float old = getParameter (0); + setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); + setParameter (0, old); + } + + if (plugin->activate != nullptr) + plugin->activate (handle); + } + } + + void releaseResources() + { + if (handle != nullptr && plugin->deactivate != nullptr) + plugin->deactivate (handle); + + tempBuffer.setSize (1, 1); + } + + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) + { + const int numSamples = buffer.getNumSamples(); + + if (initialised && plugin != nullptr && handle != nullptr) + { + for (int i = 0; i < inputs.size(); ++i) + plugin->connect_port (handle, inputs[i], + i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr); + + if (plugin->run != nullptr) + { + for (int i = 0; i < outputs.size(); ++i) + plugin->connect_port (handle, outputs.getUnchecked(i), + i < buffer.getNumChannels() ? buffer.getWritePointer (i) : nullptr); + + plugin->run (handle, numSamples); + return; + } + + if (plugin->run_adding != nullptr) + { + tempBuffer.setSize (outputs.size(), numSamples); + tempBuffer.clear(); + + for (int i = 0; i < outputs.size(); ++i) + plugin->connect_port (handle, outputs.getUnchecked(i), tempBuffer.getWritePointer (i)); + + plugin->run_adding (handle, numSamples); + + for (int i = 0; i < outputs.size(); ++i) + if (i < buffer.getNumChannels()) + buffer.copyFrom (i, 0, tempBuffer, i, 0, numSamples); + + return; + } + + jassertfalse; // no callback to use? + } + + for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, numSamples); + } + + bool isInputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); } + bool isOutputChannelStereoPair (int index) const { return isPositiveAndBelow (index, getNumInputChannels()); } + + const String getInputChannelName (const int index) const + { + if (isPositiveAndBelow (index, getNumInputChannels())) + return String (plugin->PortNames [inputs [index]]).trim(); + + return String(); + } + + const String getOutputChannelName (const int index) const + { + if (isPositiveAndBelow (index, getNumInputChannels())) + return String (plugin->PortNames [outputs [index]]).trim(); + + return String(); + } + + //============================================================================== + int getNumParameters() { return handle != nullptr ? parameters.size() : 0; } + + bool isParameterAutomatable (int index) const + { + return plugin != nullptr + && (plugin->PortDescriptors [parameters[index]] & LADSPA_PORT_INPUT) != 0; + } + + float getParameter (int index) + { + if (plugin != nullptr && isPositiveAndBelow (index, parameters.size())) + { + const ScopedLock sl (lock); + return parameterValues[index].unscaled; + } + + return 0.0f; + } + + void setParameter (int index, float newValue) + { + if (plugin != nullptr && isPositiveAndBelow (index, parameters.size())) + { + const ScopedLock sl (lock); + + ParameterValue& p = parameterValues[index]; + + if (p.unscaled != newValue) + p = ParameterValue (getNewParamScaled (plugin->PortRangeHints [parameters[index]], newValue), newValue); + } + } + + const String getParameterName (int index) + { + if (plugin != nullptr) + { + jassert (isPositiveAndBelow (index, parameters.size())); + return String (plugin->PortNames [parameters [index]]).trim(); + } + + return String(); + } + + const String getParameterText (int index) + { + if (plugin != nullptr) + { + jassert (index >= 0 && index < parameters.size()); + + const LADSPA_PortRangeHint& hint = plugin->PortRangeHints [parameters [index]]; + + if (LADSPA_IS_HINT_INTEGER (hint.HintDescriptor)) + return String ((int) parameterValues[index].scaled); + + return String (parameterValues[index].scaled, 4); + } + + return String(); + } + + //============================================================================== + int getNumPrograms() { return 0; } + int getCurrentProgram() { return 0; } + + void setCurrentProgram (int newIndex) + { + if (plugin != nullptr) + for (int i = 0; i < parameters.size(); ++i) + parameterValues[i] = getParamValue (plugin->PortRangeHints [parameters[i]]); + } + + const String getProgramName (int index) + { + // XXX + return String(); + } + + void changeProgramName (int index, const String& newName) + { + // XXX + } + + //============================================================================== + void getStateInformation (MemoryBlock& destData) + { + destData.setSize (sizeof (float) * getNumParameters()); + destData.fillWith (0); + + float* const p = (float*) ((char*) destData.getData()); + for (int i = 0; i < getNumParameters(); ++i) + p[i] = getParameter(i); + } + + void getCurrentProgramStateInformation (MemoryBlock& destData) + { + getStateInformation (destData); + } + + void setStateInformation (const void* data, int sizeInBytes) + { + const float* p = static_cast (data); + + for (int i = 0; i < getNumParameters(); ++i) + setParameter (i, p[i]); + } + + void setCurrentProgramStateInformation (const void* data, int sizeInBytes) + { + setStateInformation (data, sizeInBytes); + } + + bool hasEditor() const + { + return false; + } + + AudioProcessorEditor* createEditor() + { + return nullptr; + } + + bool isValid() const + { + return handle != nullptr; + } + + LADSPAModuleHandle::Ptr module; + const LADSPA_Descriptor* plugin; + +private: + LADSPA_Handle handle; + String name; + CriticalSection lock; + bool initialised; + AudioSampleBuffer tempBuffer; + Array inputs, outputs, parameters; + + struct ParameterValue + { + inline ParameterValue() noexcept : scaled (0), unscaled (0) {} + inline ParameterValue (float s, float u) noexcept : scaled (s), unscaled (u) {} + + float scaled, unscaled; + }; + + HeapBlock parameterValues; + + //============================================================================== + static float scaledValue (float low, float high, float alpha, bool useLog) noexcept + { + if (useLog && low > 0 && high > 0) + return expf (logf (low) * (1.0f - alpha) + logf (high) * alpha); + + return low + (high - low) * alpha; + } + + static float toIntIfNecessary (const LADSPA_PortRangeHintDescriptor& desc, float value) + { + return LADSPA_IS_HINT_INTEGER (desc) ? ((float) (int) value) : value; + } + + float getNewParamScaled (const LADSPA_PortRangeHint& hint, float newValue) const + { + const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor; + + if (LADSPA_IS_HINT_TOGGLED (desc)) + return (newValue < 0.5f) ? 0.0f : 1.0f; + + const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f; + const float lower = hint.LowerBound * scale; + const float upper = hint.UpperBound * scale; + + if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_BOUNDED_ABOVE (desc)) + return toIntIfNecessary (desc, scaledValue (lower, upper, newValue, LADSPA_IS_HINT_LOGARITHMIC (desc))); + + if (LADSPA_IS_HINT_BOUNDED_BELOW (desc)) return toIntIfNecessary (desc, newValue); + if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc)) return toIntIfNecessary (desc, newValue * upper); + + return 0.0f; + } + + ParameterValue getParamValue (const LADSPA_PortRangeHint& hint) const + { + const LADSPA_PortRangeHintDescriptor& desc = hint.HintDescriptor; + + if (LADSPA_IS_HINT_HAS_DEFAULT (desc)) + { + if (LADSPA_IS_HINT_DEFAULT_0 (desc)) return ParameterValue(); + if (LADSPA_IS_HINT_DEFAULT_1 (desc)) return ParameterValue (1.0f, 1.0f); + if (LADSPA_IS_HINT_DEFAULT_100 (desc)) return ParameterValue (100.0f, 0.5f); + if (LADSPA_IS_HINT_DEFAULT_440 (desc)) return ParameterValue (440.0f, 0.5f); + + const float scale = LADSPA_IS_HINT_SAMPLE_RATE (desc) ? (float) getSampleRate() : 1.0f; + const float lower = hint.LowerBound * scale; + const float upper = hint.UpperBound * scale; + + if (LADSPA_IS_HINT_BOUNDED_BELOW (desc) && LADSPA_IS_HINT_DEFAULT_MINIMUM (desc)) return ParameterValue (lower, 0.0f); + if (LADSPA_IS_HINT_BOUNDED_ABOVE (desc) && LADSPA_IS_HINT_DEFAULT_MAXIMUM (desc)) return ParameterValue (upper, 1.0f); + + if (LADSPA_IS_HINT_BOUNDED_BELOW (desc)) + { + const bool useLog = LADSPA_IS_HINT_LOGARITHMIC (desc); + + if (LADSPA_IS_HINT_DEFAULT_LOW (desc)) return ParameterValue (scaledValue (lower, upper, 0.25f, useLog), 0.25f); + if (LADSPA_IS_HINT_DEFAULT_MIDDLE (desc)) return ParameterValue (scaledValue (lower, upper, 0.50f, useLog), 0.50f); + if (LADSPA_IS_HINT_DEFAULT_HIGH (desc)) return ParameterValue (scaledValue (lower, upper, 0.75f, useLog), 0.75f); + } + } + + return ParameterValue(); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginInstance) +}; + + +//============================================================================== +//============================================================================== +LADSPAPluginFormat::LADSPAPluginFormat() {} +LADSPAPluginFormat::~LADSPAPluginFormat() {} + +void LADSPAPluginFormat::findAllTypesForFile (OwnedArray & results, + const String& fileOrIdentifier) +{ + if (! fileMightContainThisPluginType (fileOrIdentifier)) + return; + + PluginDescription desc; + desc.fileOrIdentifier = fileOrIdentifier; + desc.uid = 0; + + ScopedPointer instance (dynamic_cast (createInstanceFromDescription (desc, 44100.0, 512))); + + if (instance == nullptr || ! instance->isValid()) + return; + + instance->initialise (44100.0, 512); + + instance->fillInPluginDescription (desc); + + if (instance->module->moduleMain != nullptr) + { + for (int uid = 0;; ++uid) + { + if (const LADSPA_Descriptor* plugin = instance->module->moduleMain (uid)) + { + desc.uid = uid; + desc.name = plugin->Name != nullptr ? plugin->Name : "Unknown"; + + if (! arrayContainsPlugin (results, desc)) + results.add (new PluginDescription (desc)); + } + else + { + break; + } + } + } +} + +AudioPluginInstance* LADSPAPluginFormat::createInstanceFromDescription (const PluginDescription& desc, + double sampleRate, int blockSize) +{ + ScopedPointer result; + + if (fileMightContainThisPluginType (desc.fileOrIdentifier)) + { + File file (desc.fileOrIdentifier); + + const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); + file.getParentDirectory().setAsCurrentWorkingDirectory(); + + const LADSPAModuleHandle::Ptr module (LADSPAModuleHandle::findOrCreateModule (file)); + + if (module != nullptr) + { + shellLADSPAUIDToCreate = desc.uid; + + result = new LADSPAPluginInstance (module); + + if (result->plugin != nullptr && result->isValid()) + result->initialise (sampleRate, blockSize); + else + result = nullptr; + } + + previousWorkingDirectory.setAsCurrentWorkingDirectory(); + } + + return result.release(); +} + +bool LADSPAPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) +{ + const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); + return f.existsAsFile() && f.hasFileExtension (".so"); +} + +String LADSPAPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) +{ + return fileOrIdentifier; +} + +bool LADSPAPluginFormat::pluginNeedsRescanning (const PluginDescription& desc) +{ + return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime; +} + +bool LADSPAPluginFormat::doesPluginStillExist (const PluginDescription& desc) +{ + return File::createFileWithoutCheckingPath (desc.fileOrIdentifier).exists(); +} + +StringArray LADSPAPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive) +{ + StringArray results; + + for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) + recursiveFileSearch (results, directoriesToSearch[j], recursive); + + return results; +} + +void LADSPAPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive) +{ + DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); + + while (iter.next()) + { + const File f (iter.getFile()); + bool isPlugin = false; + + if (fileMightContainThisPluginType (f.getFullPathName())) + { + isPlugin = true; + results.add (f.getFullPathName()); + } + + if (recursive && (! isPlugin) && f.isDirectory()) + recursiveFileSearch (results, f, true); + } +} + +FileSearchPath LADSPAPluginFormat::getDefaultLocationsToSearch() +{ + return FileSearchPath (SystemStats::getEnvironmentVariable ("LADSPA_PATH", + "/usr/lib/ladspa;/usr/local/lib/ladspa;~/.ladspa") + .replace (":", ";")); +} + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.h index 6bd3a48..21d3167 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.h @@ -1,42 +1,32 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ -#define __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ - -#include "../format/juce_AudioPluginFormat.h" - -#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX - - -// Sorry, this file is just a placeholder at the moment!... - +#if (JUCE_PLUGINHOST_LADSPA && JUCE_LINUX) || DOXYGEN //============================================================================== /** - Implements a plugin format manager for DirectX plugins. + Implements a plugin format manager for LADSPA plugins. */ class JUCE_API LADSPAPluginFormat : public AudioPluginFormat { @@ -46,17 +36,22 @@ public: ~LADSPAPluginFormat(); //============================================================================== - String getName() const { return "LADSPA"; } - void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); - AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const String& fileOrIdentifier); - String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } - FileSearchPath getDefaultLocationsToSearch(); + String getName() const override { return "LADSPA"; } + void findAllTypesForFile (OwnedArray&, const String& fileOrIdentifier) override; + AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override; + bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; + String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; + bool pluginNeedsRescanning (const PluginDescription&) override; + StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override; + bool doesPluginStillExist (const PluginDescription&) override; + FileSearchPath getDefaultLocationsToSearch() override; + bool canScanForPlugins() const override { return true; } private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginFormat); + void recursiveFileSearch (StringArray&, const File&, bool recursive); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LADSPAPluginFormat) }; -#endif -#endif // __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ +#endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h new file mode 100644 index 0000000..d7e66cd --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Common.h @@ -0,0 +1,420 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_VST3COMMON_H_INCLUDED +#define JUCE_VST3COMMON_H_INCLUDED + +//============================================================================== +#define JUCE_DECLARE_VST3_COM_REF_METHODS \ + Steinberg::uint32 PLUGIN_API addRef() override { return (Steinberg::uint32) ++refCount; } \ + Steinberg::uint32 PLUGIN_API release() override { const int r = --refCount; if (r == 0) delete this; return (Steinberg::uint32) r; } + +#define JUCE_DECLARE_VST3_COM_QUERY_METHODS \ + Steinberg::tresult PLUGIN_API queryInterface (const Steinberg::TUID, void** obj) override \ + { \ + jassertfalse; \ + *obj = nullptr; \ + return Steinberg::kNotImplemented; \ + } + +static bool doUIDsMatch (const Steinberg::TUID a, const Steinberg::TUID b) noexcept +{ + return std::memcmp (a, b, sizeof (Steinberg::TUID)) == 0; +} + +#define TEST_FOR_AND_RETURN_IF_VALID(iidToTest, ClassType) \ + if (doUIDsMatch (iidToTest, ClassType::iid)) \ + { \ + addRef(); \ + *obj = dynamic_cast (this); \ + return Steinberg::kResultOk; \ + } + +#define TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID(iidToTest, CommonClassType, SourceClassType) \ + if (doUIDsMatch (iidToTest, CommonClassType::iid)) \ + { \ + addRef(); \ + *obj = (CommonClassType*) static_cast (this); \ + return Steinberg::kResultOk; \ + } + +//============================================================================== +static juce::String toString (const Steinberg::char8* string) noexcept { return juce::String (string); } +static juce::String toString (const Steinberg::char16* string) noexcept { return juce::String (juce::CharPointer_UTF16 ((juce::CharPointer_UTF16::CharType*) string)); } + +// NB: The casts are handled by a Steinberg::UString operator +static juce::String toString (const Steinberg::UString128& string) noexcept { return toString (static_cast (string)); } +static juce::String toString (const Steinberg::UString256& string) noexcept { return toString (static_cast (string)); } + +static void toString128 (Steinberg::Vst::String128 result, const juce::String& source) +{ + Steinberg::UString (result, 128).fromAscii (source.toUTF8()); +} + +static Steinberg::Vst::TChar* toString (const juce::String& source) noexcept +{ + return reinterpret_cast (source.toUTF16().getAddress()); +} + +#if JUCE_WINDOWS + static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeHWND; +#else + static const Steinberg::FIDString defaultVST3WindowType = Steinberg::kPlatformTypeNSView; +#endif + + +//============================================================================== +static Steinberg::Vst::SpeakerArrangement getArrangementForBus (Steinberg::Vst::IAudioProcessor* processor, + bool isInput, int busIndex) +{ + Steinberg::Vst::SpeakerArrangement arrangement = Steinberg::Vst::SpeakerArr::kEmpty; + + if (processor != nullptr) + processor->getBusArrangement (isInput ? Steinberg::Vst::kInput : Steinberg::Vst::kOutput, + (Steinberg::int32) busIndex, arrangement); + + return arrangement; +} + +/** For the sake of simplicity, there can only be 1 arrangement type per channel count. + i.e.: 4 channels == k31Cine OR k40Cine +*/ +static Steinberg::Vst::SpeakerArrangement getArrangementForNumChannels (int numChannels) noexcept +{ + using namespace Steinberg::Vst::SpeakerArr; + + switch (numChannels) + { + case 0: return kEmpty; + case 1: return kMono; + case 2: return kStereo; + case 3: return k30Cine; + case 4: return k31Cine; + case 5: return k50; + case 6: return k51; + case 7: return k61Cine; + case 8: return k71CineFullFront; + case 9: return k90; + case 10: return k91; + case 11: return k101; + case 12: return k111; + case 13: return k130; + case 14: return k131; + case 24: return (Steinberg::Vst::SpeakerArrangement) 1929904127; // k222 + default: break; + } + + jassert (numChannels >= 0); + + juce::BigInteger bi; + bi.setRange (0, jmin (numChannels, (int) (sizeof (Steinberg::Vst::SpeakerArrangement) * 8)), true); + return (Steinberg::Vst::SpeakerArrangement) bi.toInt64(); +} + +//============================================================================== +template +class ComSmartPtr +{ +public: + ComSmartPtr() noexcept : source (nullptr) {} + ComSmartPtr (ObjectType* object, bool autoAddRef = true) noexcept : source (object) { if (source != nullptr && autoAddRef) source->addRef(); } + ComSmartPtr (const ComSmartPtr& other) noexcept : source (other.source) { if (source != nullptr) source->addRef(); } + ~ComSmartPtr() { if (source != nullptr) source->release(); } + + operator ObjectType*() const noexcept { return source; } + ObjectType* get() const noexcept { return source; } + ObjectType& operator*() const noexcept { return *source; } + ObjectType* operator->() const noexcept { return source; } + + ComSmartPtr& operator= (const ComSmartPtr& other) { return operator= (other.source); } + + ComSmartPtr& operator= (ObjectType* const newObjectToTakePossessionOf) + { + ComSmartPtr p (newObjectToTakePossessionOf); + std::swap (p.source, source); + return *this; + } + + bool operator== (ObjectType* const other) noexcept { return source == other; } + bool operator!= (ObjectType* const other) noexcept { return source != other; } + + bool loadFrom (Steinberg::FUnknown* o) + { + *this = nullptr; + return o != nullptr && o->queryInterface (ObjectType::iid, (void**) &source) == Steinberg::kResultOk; + } + + bool loadFrom (Steinberg::IPluginFactory* factory, const Steinberg::TUID& uuid) + { + jassert (factory != nullptr); + *this = nullptr; + return factory->createInstance (uuid, ObjectType::iid, (void**) &source) == Steinberg::kResultOk; + } + +private: + ObjectType* source; +}; + +//============================================================================== +class MidiEventList : public Steinberg::Vst::IEventList +{ +public: + MidiEventList() {} + virtual ~MidiEventList() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + //============================================================================== + void clear() + { + events.clearQuick(); + } + + Steinberg::int32 PLUGIN_API getEventCount() override + { + return (Steinberg::int32) events.size(); + } + + // NB: This has to cope with out-of-range indexes from some plugins. + Steinberg::tresult PLUGIN_API getEvent (Steinberg::int32 index, Steinberg::Vst::Event& e) override + { + if (isPositiveAndBelow ((int) index, events.size())) + { + e = events.getReference ((int) index); + return Steinberg::kResultTrue; + } + + return Steinberg::kResultFalse; + } + + Steinberg::tresult PLUGIN_API addEvent (Steinberg::Vst::Event& e) override + { + events.add (e); + return Steinberg::kResultTrue; + } + + //============================================================================== + static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList) + { + const int32 numEvents = eventList.getEventCount(); + + for (Steinberg::int32 i = 0; i < numEvents; ++i) + { + Steinberg::Vst::Event e; + + if (eventList.getEvent (i, e) == Steinberg::kResultOk) + { + switch (e.type) + { + case Steinberg::Vst::Event::kNoteOnEvent: + result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel), + createSafeNote (e.noteOn.pitch), + (Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity)), + e.sampleOffset); + break; + + case Steinberg::Vst::Event::kNoteOffEvent: + result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel), + createSafeNote (e.noteOff.pitch), + (Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity)), + e.sampleOffset); + break; + + case Steinberg::Vst::Event::kPolyPressureEvent: + result.addEvent (MidiMessage::aftertouchChange (createSafeChannel (e.polyPressure.channel), + createSafeNote (e.polyPressure.pitch), + denormaliseToMidiValue (e.polyPressure.pressure)), + e.sampleOffset); + break; + + case Steinberg::Vst::Event::kDataEvent: + result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, e.data.size), + e.sampleOffset); + break; + + default: + break; + } + } + } + } + + static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer) + { + MidiBuffer::Iterator iterator (midiBuffer); + MidiMessage msg; + int midiEventPosition = 0; + + enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once + int numEvents = 0; + + while (iterator.getNextEvent (msg, midiEventPosition)) + { + if (++numEvents > maxNumEvents) + break; + + Steinberg::Vst::Event e = { 0 }; + + if (msg.isNoteOn()) + { + e.type = Steinberg::Vst::Event::kNoteOnEvent; + e.noteOn.channel = createSafeChannel (msg.getChannel()); + e.noteOn.pitch = createSafeNote (msg.getNoteNumber()); + e.noteOn.velocity = normaliseMidiValue (msg.getVelocity()); + e.noteOn.length = 0; + e.noteOn.tuning = 0.0f; + e.noteOn.noteId = -1; + } + else if (msg.isNoteOff()) + { + e.type = Steinberg::Vst::Event::kNoteOffEvent; + e.noteOff.channel = createSafeChannel (msg.getChannel()); + e.noteOff.pitch = createSafeNote (msg.getNoteNumber()); + e.noteOff.velocity = normaliseMidiValue (msg.getVelocity()); + e.noteOff.tuning = 0.0f; + e.noteOff.noteId = -1; + } + else if (msg.isSysEx()) + { + e.type = Steinberg::Vst::Event::kDataEvent; + e.data.bytes = msg.getSysExData(); + e.data.size = msg.getSysExDataSize(); + e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx; + } + else if (msg.isAftertouch()) + { + e.type = Steinberg::Vst::Event::kPolyPressureEvent; + e.polyPressure.channel = createSafeChannel (msg.getChannel()); + e.polyPressure.pitch = createSafeNote (msg.getNoteNumber()); + e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue()); + } + else + { + continue; + } + + e.busIndex = 0; + e.sampleOffset = midiEventPosition; + + result.addEvent (e); + } + } + +private: + Array events; + Atomic refCount; + + static Steinberg::int16 createSafeChannel (int channel) noexcept { return (Steinberg::int16) jlimit (0, 15, channel - 1); } + static int createSafeChannel (Steinberg::int16 channel) noexcept { return (int) jlimit (1, 16, channel + 1); } + + static Steinberg::int16 createSafeNote (int note) noexcept { return (Steinberg::int16) jlimit (0, 127, note); } + static int createSafeNote (Steinberg::int16 note) noexcept { return jlimit (0, 127, (int) note); } + + static float normaliseMidiValue (int value) noexcept { return jlimit (0.0f, 1.0f, (float) value / 127.0f); } + static int denormaliseToMidiValue (float value) noexcept { return roundToInt (jlimit (0.0f, 127.0f, value * 127.0f)); } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList) +}; + +//============================================================================== +namespace VST3BufferExchange +{ + typedef Array Bus; + typedef Array BusMap; + + /** Assigns a series of AudioSampleBuffer's channels to an AudioBusBuffers' + + @warning For speed, does not check the channel count and offsets + according to the AudioSampleBuffer + */ + void associateBufferTo (Steinberg::Vst::AudioBusBuffers& vstBuffers, + Bus& bus, + AudioSampleBuffer& buffer, + int numChannels, int channelStartOffset, + int sampleOffset = 0) + { + const int channelEnd = numChannels + channelStartOffset; + jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels()); + + bus.clearQuick(); + + for (int i = channelStartOffset; i < channelEnd; ++i) + bus.add (buffer.getWritePointer (i, sampleOffset)); + + vstBuffers.channelBuffers32 = bus.getRawDataPointer(); + vstBuffers.numChannels = numChannels; + vstBuffers.silenceFlags = 0; + } + + static void mapArrangementToBusses (int& channelIndexOffset, int index, + Array& result, + BusMap& busMapToUse, Steinberg::Vst::SpeakerArrangement arrangement, + AudioSampleBuffer& source) + { + const int numChansForBus = BigInteger ((juce::int64) arrangement).countNumberOfSetBits(); + + if (index >= result.size()) + result.add (Steinberg::Vst::AudioBusBuffers()); + + if (index >= busMapToUse.size()) + busMapToUse.add (Bus()); + + if (numChansForBus > 0) + { + associateBufferTo (result.getReference (index), + busMapToUse.getReference (index), + source, numChansForBus, channelIndexOffset); + } + + channelIndexOffset += numChansForBus; + } + + static void mapBufferToBusses (Array& result, BusMap& busMapToUse, + const Array& arrangements, + AudioSampleBuffer& source) + { + int channelIndexOffset = 0; + + for (int i = 0; i < arrangements.size(); ++i) + mapArrangementToBusses (channelIndexOffset, i, result, busMapToUse, + arrangements.getUnchecked (i), source); + } + + static void mapBufferToBusses (Array& result, + Steinberg::Vst::IAudioProcessor& processor, + BusMap& busMapToUse, bool isInput, int numBusses, + AudioSampleBuffer& source) + { + int channelIndexOffset = 0; + + for (int i = 0; i < numBusses; ++i) + mapArrangementToBusses (channelIndexOffset, i, + result, busMapToUse, + getArrangementForBus (&processor, isInput, i), + source); + } +} + +#endif // JUCE_VST3COMMON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h new file mode 100644 index 0000000..ffb90e8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3Headers.h @@ -0,0 +1,172 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_VST3HEADERS_H_INCLUDED +#define JUCE_VST3HEADERS_H_INCLUDED + +#undef Point +#undef Component + +// Wow, those Steinberg guys really don't worry too much about compiler warnings. +#if _MSC_VER + #pragma warning (disable: 4505) + #pragma warning (push, 0) + #pragma warning (disable: 4702) +#elif __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wnon-virtual-dtor" + #pragma clang diagnostic ignored "-Wreorder" + #pragma clang diagnostic ignored "-Wunsequenced" + #pragma clang diagnostic ignored "-Wint-to-pointer-cast" + #pragma clang diagnostic ignored "-Wunused-parameter" + #pragma clang diagnostic ignored "-Wconversion" + #pragma clang diagnostic ignored "-Woverloaded-virtual" + #pragma clang diagnostic ignored "-Wshadow" + #pragma clang diagnostic ignored "-Wdeprecated-register" +#endif + +/* These files come with the Steinberg VST3 SDK - to get them, you'll need to + visit the Steinberg website and agree to whatever is currently required to + get them. + + Then, you'll need to make sure your include path contains your "VST3 SDK" + directory (or whatever you've named it on your machine). The Introjucer has + a special box for setting this path. +*/ +#if JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + +//============================================================================== +namespace Steinberg +{ + /** Missing IIDs */ + DEF_CLASS_IID (IPluginBase) + DEF_CLASS_IID (IPlugView) + DEF_CLASS_IID (IPlugFrame) + DEF_CLASS_IID (IBStream) + DEF_CLASS_IID (ISizeableStream) + DEF_CLASS_IID (IPluginFactory) + DEF_CLASS_IID (IPluginFactory2) + DEF_CLASS_IID (IPluginFactory3) +} +#endif //JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY + +#if _MSC_VER + #pragma warning (pop) +#elif __clang__ + #pragma clang diagnostic pop +#endif + +//============================================================================== +#undef ASSERT +#undef WARNING +#undef PRINTSYSERROR +#undef DEBUGSTR +#undef DBPRT0 +#undef DBPRT1 +#undef DBPRT2 +#undef DBPRT3 +#undef DBPRT4 +#undef DBPRT5 +#undef min +#undef max +#undef MIN +#undef MAX +#undef calloc +#undef free +#undef malloc +#undef realloc +#undef NEW +#undef NEWVEC +#undef VERIFY +#undef VERIFY_IS +#undef VERIFY_NOT +#undef META_CREATE_FUNC +#undef CLASS_CREATE_FUNC +#undef SINGLE_CREATE_FUNC +#undef _META_CLASS +#undef _META_CLASS_IFACE +#undef _META_CLASS_SINGLE +#undef META_CLASS +#undef META_CLASS_IFACE +#undef META_CLASS_SINGLE +#undef SINGLETON +#undef OBJ_METHODS +#undef QUERY_INTERFACE +#undef LICENCE_UID +#undef BEGIN_FACTORY +#undef DEF_CLASS +#undef DEF_CLASS1 +#undef DEF_CLASS2 +#undef DEF_CLASS_W +#undef END_FACTORY +#undef Point +#undef Component + +#endif // JUCE_VST3HEADERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp new file mode 100644 index 0000000..7abe6e3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -0,0 +1,2516 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_PLUGINHOST_VST3 + +} // namespace juce + +#if JucePlugin_Build_VST3 + #undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY + #define JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY 1 +#endif + +#include +#include "juce_VST3Headers.h" + +#undef JUCE_VST3HEADERS_INCLUDE_HEADERS_ONLY + +namespace juce +{ + +#include "juce_VST3Common.h" + +using namespace Steinberg; + +//============================================================================== +struct VST3Classes +{ + +#ifndef JUCE_VST3_DEBUGGING + #define JUCE_VST3_DEBUGGING 0 +#endif + +#if JUCE_VST3_DEBUGGING + #define VST3_DBG(a) Logger::writeToLog (a); +#else + #define VST3_DBG(a) +#endif + +#if JUCE_DEBUG +static int warnOnFailure (int result) +{ + const char* message = "Unknown result!"; + + switch (result) + { + case kResultOk: return result; + case kNotImplemented: message = "kNotImplemented"; break; + case kNoInterface: message = "kNoInterface"; break; + case kResultFalse: message = "kResultFalse"; break; + case kInvalidArgument: message = "kInvalidArgument"; break; + case kInternalError: message = "kInternalError"; break; + case kNotInitialized: message = "kNotInitialized"; break; + case kOutOfMemory: message = "kOutOfMemory"; break; + default: break; + } + + DBG (message); + return result; +} +#else + #define warnOnFailure(x) x +#endif + +//============================================================================== +static int getHashForTUID (const TUID& tuid) noexcept +{ + int value = 0; + + for (int i = 0; i < numElementsInArray (tuid); ++i) + value = (value * 31) + tuid[i]; + + return value; +} + +template +static void fillDescriptionWith (PluginDescription& description, ObjectType& object) +{ + description.version = toString (object.version).trim(); + description.category = toString (object.subCategories).trim(); + + if (description.manufacturerName.trim().isEmpty()) + description.manufacturerName = toString (object.vendor).trim(); +} + +static void createPluginDescription (PluginDescription& description, + const File& pluginFile, const String& company, const String& name, + const PClassInfo& info, PClassInfo2* info2, PClassInfoW* infoW, + int numInputs, int numOutputs) +{ + description.fileOrIdentifier = pluginFile.getFullPathName(); + description.lastFileModTime = pluginFile.getLastModificationTime(); + description.manufacturerName = company; + description.name = name; + description.descriptiveName = name; + description.pluginFormatName = "VST3"; + description.numInputChannels = numInputs; + description.numOutputChannels = numOutputs; + description.uid = getHashForTUID (info.cid); + + if (infoW != nullptr) fillDescriptionWith (description, *infoW); + else if (info2 != nullptr) fillDescriptionWith (description, *info2); + + if (description.category.isEmpty()) + description.category = toString (info.category).trim(); + + description.isInstrument = description.category.containsIgnoreCase ("Instrument"); // This seems to be the only way to find that out! ARGH! +} + +static int getNumSingleDirectionBussesFor (Vst::IComponent* component, + bool checkInputs, + bool checkAudioChannels) +{ + jassert (component != nullptr); + + return (int) component->getBusCount (checkAudioChannels ? Vst::kAudio : Vst::kEvent, + checkInputs ? Vst::kInput : Vst::kOutput); +} + +/** Gives the total number of channels for a particular type of bus direction and media type */ +static int getNumSingleDirectionChannelsFor (Vst::IComponent* component, + bool checkInputs, + bool checkAudioChannels) +{ + jassert (component != nullptr); + + const Vst::BusDirections direction = checkInputs ? Vst::kInput : Vst::kOutput; + const Vst::MediaTypes mediaType = checkAudioChannels ? Vst::kAudio : Vst::kEvent; + const Steinberg::int32 numBuses = component->getBusCount (mediaType, direction); + + int numChannels = 0; + + for (Steinberg::int32 i = numBuses; --i >= 0;) + { + Vst::BusInfo busInfo; + warnOnFailure (component->getBusInfo (mediaType, direction, i, busInfo)); + numChannels += (int) busInfo.channelCount; + } + + return numChannels; +} + +static void setStateForAllBussesOfType (Vst::IComponent* component, + bool state, + bool activateInputs, + bool activateAudioChannels) +{ + jassert (component != nullptr); + + const Vst::BusDirections direction = activateInputs ? Vst::kInput : Vst::kOutput; + const Vst::MediaTypes mediaType = activateAudioChannels ? Vst::kAudio : Vst::kEvent; + const Steinberg::int32 numBuses = component->getBusCount (mediaType, direction); + + for (Steinberg::int32 i = numBuses; --i >= 0;) + warnOnFailure (component->activateBus (mediaType, direction, i, state)); +} + +//============================================================================== +/** Assigns a complete AudioSampleBuffer's channels to an AudioBusBuffers' */ +static void associateWholeBufferTo (Vst::AudioBusBuffers& vstBuffers, AudioSampleBuffer& buffer) noexcept +{ + vstBuffers.channelBuffers32 = buffer.getArrayOfWritePointers(); + vstBuffers.numChannels = buffer.getNumChannels(); + vstBuffers.silenceFlags = 0; +} + +//============================================================================== +static void toProcessContext (Vst::ProcessContext& context, AudioPlayHead* playHead, double sampleRate) +{ + jassert (sampleRate > 0.0); //Must always be valid, as stated by the VST3 SDK + + using namespace Vst; + + zerostruct (context); + context.sampleRate = sampleRate; + + if (playHead != nullptr) + { + AudioPlayHead::CurrentPositionInfo position; + playHead->getCurrentPosition (position); + + context.projectTimeSamples = position.timeInSamples; //Must always be valid, as stated by the VST3 SDK + context.projectTimeMusic = position.timeInSeconds; //Does not always need to be valid... + context.tempo = position.bpm; + context.timeSigNumerator = position.timeSigNumerator; + context.timeSigDenominator = position.timeSigDenominator; + context.barPositionMusic = position.ppqPositionOfLastBarStart; + context.cycleStartMusic = position.ppqLoopStart; + context.cycleEndMusic = position.ppqLoopEnd; + + switch (position.frameRate) + { + case AudioPlayHead::fps24: context.frameRate.framesPerSecond = 24; break; + case AudioPlayHead::fps25: context.frameRate.framesPerSecond = 25; break; + case AudioPlayHead::fps30: context.frameRate.framesPerSecond = 30; break; + + case AudioPlayHead::fps2997: + case AudioPlayHead::fps2997drop: + case AudioPlayHead::fps30drop: + { + context.frameRate.framesPerSecond = 30; + context.frameRate.flags = FrameRate::kDropRate; + + if (position.frameRate == AudioPlayHead::fps2997drop) + context.frameRate.flags |= FrameRate::kPullDownRate; + } + break; + + case AudioPlayHead::fpsUnknown: break; + + default: jassertfalse; break; // New frame rate? + } + + if (position.isPlaying) context.state |= ProcessContext::kPlaying; + if (position.isRecording) context.state |= ProcessContext::kRecording; + if (position.isLooping) context.state |= ProcessContext::kCycleActive; + } + else + { + context.tempo = 120.0; + context.frameRate.framesPerSecond = 30; + context.timeSigNumerator = 4; + context.timeSigDenominator = 4; + } + + if (context.projectTimeMusic >= 0.0) context.state |= ProcessContext::kProjectTimeMusicValid; + if (context.barPositionMusic >= 0.0) context.state |= ProcessContext::kBarPositionValid; + if (context.tempo > 0.0) context.state |= ProcessContext::kTempoValid; + if (context.frameRate.framesPerSecond > 0) context.state |= ProcessContext::kSmpteValid; + + if (context.cycleStartMusic >= 0.0 + && context.cycleEndMusic > 0.0 + && context.cycleEndMusic > context.cycleStartMusic) + { + context.state |= ProcessContext::kCycleValid; + } + + if (context.timeSigNumerator > 0 && context.timeSigDenominator > 0) + context.state |= ProcessContext::kTimeSigValid; +} + +//============================================================================== +/** Get a list of speaker arrangements as per their speaker names + + (e.g.: 2 regular channels, aliased as 'kStringStereoS', is "L R") +*/ +static StringArray getSpeakerArrangements() +{ + using namespace Vst::SpeakerArr; + + const Vst::CString arrangements[] = + { + kStringMonoS, kStringStereoS, kStringStereoRS, kStringStereoCS, + kStringStereoSS, kStringStereoCLfeS, kString30CineS, kString30MusicS, + kString31CineS, kString31MusicS, kString40CineS, kString40MusicS, + kString41CineS, kString41MusicS, kString50S, kString51S, + kString60CineS, kString60MusicS, kString61CineS, kString61MusicS, + kString70CineS, kString70MusicS, kString71CineS, kString71MusicS, + kString80CineS, kString80MusicS, kString81CineS, kString81MusicS, + kString80CubeS, kStringBFormat1stOrderS, kString71CineTopCenterS, + kString71CineCenterHighS, kString71CineFrontHighS, kString71CineSideHighS, + kString71CineFullRearS, kString90S, kString91S, + kString100S, kString101S, kString110S, kString111S, + kString130S, kString131S, kString102S, kString122S, + nullptr + }; + + return StringArray (arrangements); +} + +/** Get a list of speaker arrangements as per their named configurations + + (e.g.: 2 regular channels, aliased as 'kStringStereoS', is "L R") +*/ +static StringArray getNamedSpeakerArrangements() +{ + using namespace Vst::SpeakerArr; + + const Vst::CString arrangements[] = + { + kStringEmpty, kStringMono, kStringStereo, kStringStereoR, + kStringStereoC, kStringStereoSide, kStringStereoCLfe, kString30Cine, + kString30Music, kString31Cine, kString31Music, kString40Cine, + kString40Music, kString41Cine, kString41Music, kString50, + kString51, kString60Cine, kString60Music, kString61Cine, + kString61Music, kString70Cine, kString70Music, kString71Cine, + kString71Music, kString71CineTopCenter, kString71CineCenterHigh, + kString71CineFrontHigh, kString71CineSideHigh, kString71CineFullRear, + kString80Cine, kString80Music, kString80Cube, kString81Cine, + kString81Music, kString102, kString122, kString90, + kString91, kString100, kString101, kString110, + kString111, kString130, kString131, + nullptr + }; + + return StringArray (arrangements); +} + +static Vst::SpeakerArrangement getSpeakerArrangementFrom (const String& string) +{ + return Vst::SpeakerArr::getSpeakerArrangementFromString (string.toUTF8()); +} + +//============================================================================== +static StringArray getPluginEffectCategories() +{ + using namespace Vst::PlugType; + + const Vst::CString categories[] = + { + kFxAnalyzer, kFxDelay, kFxDistortion, kFxDynamics, + kFxEQ, kFxFilter, kFx, kFxInstrument, + kFxInstrumentExternal, kFxSpatial, kFxGenerator, kFxMastering, + kFxModulation, kFxPitchShift, kFxRestoration, kFxReverb, + kFxSurround, kFxTools, kSpatial, kSpatialFx, + nullptr + }; + + return StringArray (categories); +} + +static StringArray getPluginInstrumentCategories() +{ + using namespace Vst::PlugType; + + const Vst::CString categories[] = + { + kInstrumentSynthSampler, kInstrumentDrum, + kInstrumentSampler, kInstrumentSynth, + kInstrumentExternal, kFxInstrument, + kFxInstrumentExternal, kFxSpatial, + kFxGenerator, + nullptr + }; + + return StringArray (categories); +} + +//============================================================================== +class VST3PluginInstance; + +class VST3HostContext : public Vst::IComponentHandler, // From VST V3.0.0 + public Vst::IComponentHandler2, // From VST V3.1.0 (a very well named class, of course!) + public Vst::IComponentHandler3, // From VST V3.5.0 (also very well named!) + public Vst::IContextMenuTarget, + public Vst::IHostApplication, + public Vst::IParamValueQueue, + public Vst::IUnitHandler +{ +public: + VST3HostContext (VST3PluginInstance* pluginInstance) : owner (pluginInstance) + { + appName = File::getSpecialLocation (File::currentApplicationFile).getFileNameWithoutExtension(); + attributeList = new AttributeList (this); + } + + virtual ~VST3HostContext() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + + FUnknown* getFUnknown() { return static_cast (this); } + + static bool hasFlag (Steinberg::int32 source, Steinberg::int32 flag) noexcept + { + return (source & flag) == flag; + } + + //============================================================================== + tresult PLUGIN_API beginEdit (Vst::ParamID paramID) override + { + const int index = getIndexOfParamID (paramID); + + if (index < 0) + return kResultFalse; + + owner->beginParameterChangeGesture (index); + return kResultTrue; + } + + tresult PLUGIN_API performEdit (Vst::ParamID paramID, Vst::ParamValue valueNormalized) override + { + const int index = getIndexOfParamID (paramID); + + if (index < 0) + return kResultFalse; + + owner->sendParamChangeMessageToListeners (index, (float) valueNormalized); + return owner->editController->setParamNormalized (paramID, valueNormalized); + } + + tresult PLUGIN_API endEdit (Vst::ParamID paramID) override + { + const int index = getIndexOfParamID (paramID); + + if (index < 0) + return kResultFalse; + + owner->endParameterChangeGesture (index); + return kResultTrue; + } + + tresult PLUGIN_API restartComponent (Steinberg::int32 flags) override + { + if (owner != nullptr) + { + if (hasFlag (flags, Vst::kReloadComponent)) + owner->reset(); + + if (hasFlag (flags, Vst::kIoChanged)) + { + double sampleRate = owner->getSampleRate(); + int numSamples = owner->getBlockSize(); + + if (sampleRate <= 8000.0) + sampleRate = 44100.0; + + if (numSamples <= 0) + numSamples = 1024; + + owner->prepareToPlay (owner->getSampleRate(), owner->getBlockSize()); + } + + if (hasFlag (flags, Vst::kLatencyChanged)) + if (owner->processor != nullptr) + owner->setLatencySamples (jmax (0, (int) owner->processor->getLatencySamples())); + + owner->updateHostDisplay(); + return kResultTrue; + } + + jassertfalse; + return kResultFalse; + } + + //============================================================================== + tresult PLUGIN_API setDirty (TBool) override + { + return kResultFalse; + } + + tresult PLUGIN_API requestOpenEditor (FIDString name) override + { + (void) name; + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API startGroupEdit() override + { + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API finishGroupEdit() override + { + jassertfalse; + return kResultFalse; + } + + //============================================================================== + class ContextMenu : public Vst::IContextMenu + { + public: + ContextMenu (VST3PluginInstance& pluginInstance) : owner (pluginInstance) {} + virtual ~ContextMenu() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + Steinberg::int32 PLUGIN_API getItemCount() override { return (Steinberg::int32) items.size(); } + + tresult PLUGIN_API addItem (const Item& item, IContextMenuTarget* target) override + { + jassert (target != nullptr); + + ItemAndTarget newItem; + newItem.item = item; + newItem.target = target; + + items.add (newItem); + return kResultOk; + } + + tresult PLUGIN_API removeItem (const Item& toRemove, IContextMenuTarget* target) override + { + for (int i = items.size(); --i >= 0;) + { + ItemAndTarget& item = items.getReference(i); + + if (item.item.tag == toRemove.tag && item.target == target) + items.remove (i); + } + + return kResultOk; + } + + tresult PLUGIN_API getItem (Steinberg::int32 tag, Item& result, IContextMenuTarget** target) override + { + for (int i = 0; i < items.size(); ++i) + { + const ItemAndTarget& item = items.getReference(i); + + if (item.item.tag == tag) + { + result = item.item; + + if (target != nullptr) + *target = item.target; + + return kResultTrue; + } + } + + zerostruct (result); + return kResultFalse; + } + + tresult PLUGIN_API popup (Steinberg::UCoord x, Steinberg::UCoord y) override + { + Array subItemStack; + OwnedArray menuStack; + PopupMenu* topLevelMenu = menuStack.add (new PopupMenu()); + + for (int i = 0; i < items.size(); ++i) + { + const Item& item = items.getReference (i).item; + + PopupMenu* menuToUse = menuStack.getLast(); + + if (hasFlag (item.flags, Item::kIsGroupStart & ~Item::kIsDisabled)) + { + subItemStack.add (&item); + menuStack.add (new PopupMenu()); + } + else if (hasFlag (item.flags, Item::kIsGroupEnd)) + { + if (const Item* subItem = subItemStack.getLast()) + { + if (PopupMenu* m = menuStack [menuStack.size() - 2]) + m->addSubMenu (toString (subItem->name), *menuToUse, + ! hasFlag (subItem->flags, Item::kIsDisabled), + nullptr, + hasFlag (subItem->flags, Item::kIsChecked)); + + menuStack.removeLast (1); + subItemStack.removeLast (1); + } + } + else if (hasFlag (item.flags, Item::kIsSeparator)) + { + menuToUse->addSeparator(); + } + else + { + menuToUse->addItem (item.tag != 0 ? (int) item.tag : (int) zeroTagReplacement, + toString (item.name), + ! hasFlag (item.flags, Item::kIsDisabled), + hasFlag (item.flags, Item::kIsChecked)); + } + } + + PopupMenu::Options options; + + if (AudioProcessorEditor* ed = owner.getActiveEditor()) + options = options.withTargetScreenArea (ed->getScreenBounds().translated ((int) x, (int) y).withSize (1, 1)); + + #if JUCE_MODAL_LOOPS_PERMITTED + // Unfortunately, Steinberg's docs explicitly say this should be modal.. + handleResult (topLevelMenu->showMenu (options)); + #else + topLevelMenu->showMenuAsync (options, ModalCallbackFunction::create (menuFinished, ComSmartPtr (this))); + #endif + + return kResultOk; + } + + #if ! JUCE_MODAL_LOOPS_PERMITTED + static void menuFinished (int modalResult, ComSmartPtr menu) { menu->handleResult (modalResult); } + #endif + + private: + enum { zeroTagReplacement = 0x7fffffff }; + + Atomic refCount; + VST3PluginInstance& owner; + + struct ItemAndTarget + { + Item item; + ComSmartPtr target; + }; + + Array items; + + void handleResult (int result) + { + if (result == 0) + return; + + if (result == zeroTagReplacement) + result = 0; + + for (int i = 0; i < items.size(); ++i) + { + const ItemAndTarget& item = items.getReference(i); + + if ((int) item.item.tag == result) + { + if (item.target != nullptr) + item.target->executeMenuItem ((Steinberg::int32) result); + + break; + } + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContextMenu) + }; + + Vst::IContextMenu* PLUGIN_API createContextMenu (IPlugView*, const Vst::ParamID*) override + { + if (owner != nullptr) + return new ContextMenu (*owner); + + return nullptr; + } + + tresult PLUGIN_API executeMenuItem (Steinberg::int32) override + { + jassertfalse; + return kResultFalse; + } + + //============================================================================== + tresult PLUGIN_API getName (Vst::String128 name) override + { + Steinberg::String str (appName.toUTF8()); + str.copyTo (name, 0, 127); + return kResultOk; + } + + tresult PLUGIN_API createInstance (TUID cid, TUID iid, void** obj) override + { + *obj = nullptr; + + if (! doUIDsMatch (cid, iid)) + { + jassertfalse; + return kInvalidArgument; + } + + if (doUIDsMatch (cid, Vst::IMessage::iid) && doUIDsMatch (iid, Vst::IMessage::iid)) + { + ComSmartPtr m (new Message (*this, attributeList)); + messageQueue.add (m); + m->addRef(); + *obj = m; + return kResultOk; + } + else if (doUIDsMatch (cid, Vst::IAttributeList::iid) && doUIDsMatch (iid, Vst::IAttributeList::iid)) + { + ComSmartPtr l (new AttributeList (this)); + l->addRef(); + *obj = l; + return kResultOk; + } + + jassertfalse; + return kNotImplemented; + } + + //============================================================================== + Vst::ParamID PLUGIN_API getParameterId() override + { + jassertfalse; + return 0; + } + + Steinberg::int32 PLUGIN_API getPointCount() override + { + jassertfalse; + return 0; + } + + tresult PLUGIN_API getPoint (Steinberg::int32, Steinberg::int32&, Vst::ParamValue&) override + { + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API addPoint (Steinberg::int32, Vst::ParamValue, Steinberg::int32&) override + { + jassertfalse; + return kResultFalse; + } + + //============================================================================== + tresult PLUGIN_API notifyUnitSelection (Vst::UnitID) override + { + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API notifyProgramListChange (Vst::ProgramListID, Steinberg::int32) override + { + jassertfalse; + return kResultFalse; + } + + //============================================================================== + tresult PLUGIN_API queryInterface (const TUID iid, void** obj) override + { + if (doUIDsMatch (iid, Vst::IAttributeList::iid)) + { + *obj = attributeList.get(); + return kResultOk; + } + + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler) + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler2) + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IComponentHandler3) + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IContextMenuTarget) + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IHostApplication) + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IParamValueQueue) + TEST_FOR_AND_RETURN_IF_VALID (iid, Vst::IUnitHandler) + TEST_FOR_COMMON_BASE_AND_RETURN_IF_VALID (iid, FUnknown, Vst::IComponentHandler) + + *obj = nullptr; + return kNotImplemented; + } + +private: + //============================================================================== + VST3PluginInstance* const owner; + Atomic refCount; + String appName; + + typedef std::map ParamMapType; + ParamMapType paramToIndexMap; + + int getIndexOfParamID (Vst::ParamID paramID) + { + if (owner == nullptr || owner->editController == nullptr) + return -1; + + int result = getMappedParamID (paramID); + + if (result < 0) + { + const int numParams = owner->editController->getParameterCount(); + + for (int i = 0; i < numParams; ++i) + { + Vst::ParameterInfo paramInfo; + owner->editController->getParameterInfo (i, paramInfo); + paramToIndexMap[paramInfo.id] = i; + } + + result = getMappedParamID (paramID); + } + + return result; + } + + int getMappedParamID (Vst::ParamID paramID) + { + const ParamMapType::iterator it (paramToIndexMap.find (paramID)); + return it != paramToIndexMap.end() ? it->second : -1; + } + + //============================================================================== + class Message : public Vst::IMessage + { + public: + Message (VST3HostContext& o, Vst::IAttributeList* list) + : owner (o), attributeList (list) + { + } + + Message (VST3HostContext& o, Vst::IAttributeList* list, FIDString id) + : owner (o), attributeList (list), messageId (toString (id)) + { + } + + Message (VST3HostContext& o, Vst::IAttributeList* list, FIDString id, const var& v) + : value (v), owner (o), attributeList (list), messageId (toString (id)) + { + } + + virtual ~Message() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + FIDString PLUGIN_API getMessageID() override { return messageId.toRawUTF8(); } + void PLUGIN_API setMessageID (FIDString id) override { messageId = toString (id); } + Vst::IAttributeList* PLUGIN_API getAttributes() override { return attributeList; } + + var value; + + private: + VST3HostContext& owner; + ComSmartPtr attributeList; + String messageId; + Atomic refCount; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Message) + }; + + Array, CriticalSection> messageQueue; + + //============================================================================== + class AttributeList : public Vst::IAttributeList + { + public: + AttributeList (VST3HostContext* o) : owner (o) {} + virtual ~AttributeList() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + //============================================================================== + tresult PLUGIN_API setInt (AttrID id, Steinberg::int64 value) override + { + jassert (id != nullptr); + + if (! setValueForId (id, value)) + owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, value))); + + return kResultTrue; + } + + tresult PLUGIN_API setFloat (AttrID id, double value) override + { + jassert (id != nullptr); + + if (! setValueForId (id, value)) + owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, value))); + + return kResultTrue; + } + + tresult PLUGIN_API setString (AttrID id, const Vst::TChar* string) override + { + jassert (id != nullptr); + jassert (string != nullptr); + + const String text (toString (string)); + + if (! setValueForId (id, text)) + owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, text))); + + return kResultTrue; + } + + tresult PLUGIN_API setBinary (AttrID id, const void* data, Steinberg::uint32 size) override + { + jassert (id != nullptr); + jassert (data != nullptr && size > 0); + + MemoryBlock block (data, (size_t) size); + + if (! setValueForId (id, block)) + owner->messageQueue.add (ComSmartPtr (new Message (*owner, this, id, block))); + + return kResultTrue; + } + + //============================================================================== + tresult PLUGIN_API getInt (AttrID id, Steinberg::int64& result) override + { + jassert (id != nullptr); + + if (fetchValueForId (id, result)) + return kResultTrue; + + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API getFloat (AttrID id, double& result) override + { + jassert (id != nullptr); + + if (fetchValueForId (id, result)) + return kResultTrue; + + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API getString (AttrID id, Vst::TChar* result, Steinberg::uint32 length) override + { + jassert (id != nullptr); + + String stringToFetch; + if (fetchValueForId (id, stringToFetch)) + { + Steinberg::String str (stringToFetch.toRawUTF8()); + str.copyTo (result, 0, (Steinberg::int32) jmin (length, (Steinberg::uint32) std::numeric_limits::max())); + + return kResultTrue; + } + + jassertfalse; + return kResultFalse; + } + + tresult PLUGIN_API getBinary (AttrID id, const void*& data, Steinberg::uint32& size) override + { + jassert (id != nullptr); + + for (int i = owner->messageQueue.size(); --i >= 0;) + { + Message* const message = owner->messageQueue.getReference (i); + + if (std::strcmp (message->getMessageID(), id) == 0) + { + if (MemoryBlock* binaryData = message->value.getBinaryData()) + { + data = binaryData->getData(); + size = (Steinberg::uint32) binaryData->getSize(); + return kResultTrue; + } + } + } + + return kResultFalse; + } + + private: + VST3HostContext* owner; + Atomic refCount; + + //============================================================================== + template + bool setValueForId (AttrID id, const Type& value) + { + jassert (id != nullptr); + + for (int i = owner->messageQueue.size(); --i >= 0;) + { + VST3HostContext::Message* const message = owner->messageQueue.getReference (i); + + if (std::strcmp (message->getMessageID(), id) == 0) + { + message->value = value; + return true; + } + } + + return false; // No message found with that Id + } + + template + bool fetchValueForId (AttrID id, Type& value) + { + jassert (id != nullptr); + + for (int i = owner->messageQueue.size(); --i >= 0;) + { + VST3HostContext::Message* const message = owner->messageQueue.getReference (i); + + if (std::strcmp (message->getMessageID(), id) == 0) + { + value = message->value; + return true; + } + } + + return false; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AttributeList) + }; + + ComSmartPtr attributeList; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3HostContext) +}; + +//============================================================================== +class DescriptionFactory +{ +public: + DescriptionFactory (VST3HostContext* host, IPluginFactory* pluginFactory) + : vst3HostContext (host), factory (pluginFactory) + { + jassert (pluginFactory != nullptr); + } + + virtual ~DescriptionFactory() {} + + Result findDescriptionsAndPerform (const File& file) + { + StringArray foundNames; + PFactoryInfo factoryInfo; + factory->getFactoryInfo (&factoryInfo); + const String companyName (toString (factoryInfo.vendor).trim()); + + Result result (Result::ok()); + + const Steinberg::int32 numClasses = factory->countClasses(); + + for (Steinberg::int32 i = 0; i < numClasses; ++i) + { + PClassInfo info; + factory->getClassInfo (i, &info); + + if (std::strcmp (info.category, kVstAudioEffectClass) != 0) + continue; + + const String name (toString (info.name).trim()); + + if (foundNames.contains (name, true)) + continue; + + ScopedPointer info2; + ScopedPointer infoW; + + { + ComSmartPtr pf2; + ComSmartPtr pf3; + + if (pf2.loadFrom (factory)) + { + info2 = new PClassInfo2(); + pf2->getClassInfo2 (i, info2); + } + + if (pf3.loadFrom (factory)) + { + infoW = new PClassInfoW(); + pf3->getClassInfoUnicode (i, infoW); + } + } + + foundNames.add (name); + + PluginDescription desc; + + { + ComSmartPtr component; + + if (component.loadFrom (factory, info.cid)) + { + if (component->initialize (vst3HostContext->getFUnknown()) == kResultOk) + { + const int numInputs = getNumSingleDirectionChannelsFor (component, true, true); + const int numOutputs = getNumSingleDirectionChannelsFor (component, false, true); + + createPluginDescription (desc, file, companyName, name, + info, info2, infoW, numInputs, numOutputs); + + component->terminate(); + } + else + { + jassertfalse; + } + } + else + { + jassertfalse; + } + } + + result = performOnDescription (desc); + + if (result.failed()) + break; + } + + return result; + } + +protected: + virtual Result performOnDescription (PluginDescription& description) = 0; + +private: + ComSmartPtr vst3HostContext; + ComSmartPtr factory; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionFactory) +}; + +struct MatchingDescriptionFinder : public DescriptionFactory +{ + MatchingDescriptionFinder (VST3HostContext* host, IPluginFactory* pluginFactory, const PluginDescription& desc) + : DescriptionFactory (host, pluginFactory), description (desc) + { + } + + static const char* getSuccessString() noexcept { return "Found Description"; } + + Result performOnDescription (PluginDescription& desc) + { + if (description.isDuplicateOf (desc)) + return Result::fail (getSuccessString()); + + return Result::ok(); + } + +private: + const PluginDescription& description; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MatchingDescriptionFinder) +}; + +struct DescriptionLister : public DescriptionFactory +{ + DescriptionLister (VST3HostContext* host, IPluginFactory* pluginFactory) + : DescriptionFactory (host, pluginFactory) + { + } + + Result performOnDescription (PluginDescription& desc) + { + list.add (new PluginDescription (desc)); + return Result::ok(); + } + + OwnedArray list; + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DescriptionLister) +}; + +//============================================================================== +struct DLLHandle +{ + DLLHandle (const String& modulePath) + : factory (nullptr) + { + if (modulePath.trim().isNotEmpty()) + open (modulePath); + } + + ~DLLHandle() + { + typedef bool (PLUGIN_API *ExitModuleFn) (); + + #if JUCE_WINDOWS + if (ExitModuleFn exitFn = (ExitModuleFn) getFunction ("ExitDll")) + exitFn(); + + releaseFactory(); + library.close(); + + #else + if (bundleRef != nullptr) + { + if (ExitModuleFn exitFn = (ExitModuleFn) getFunction ("bundleExit")) + exitFn(); + + releaseFactory(); + + CFRelease (bundleRef); + bundleRef = nullptr; + } + #endif + } + + void open (const PluginDescription& description) + { + #if JUCE_WINDOWS + jassert (description.fileOrIdentifier.isNotEmpty()); + jassert (File (description.fileOrIdentifier).existsAsFile()); + library.open (description.fileOrIdentifier); + #else + open (description.fileOrIdentifier); + #endif + } + + /** @note The factory should begin with a refCount of 1, + so don't increment the reference count + (ie: don't use a ComSmartPtr in here)! + Its lifetime will be handled by this DllHandle, + when such will be destroyed. + + @see releaseFactory + */ + IPluginFactory* JUCE_CALLTYPE getPluginFactory() + { + if (factory == nullptr) + if (GetFactoryProc proc = (GetFactoryProc) getFunction ("GetPluginFactory")) + factory = proc(); + + jassert (factory != nullptr); // The plugin NEEDS to provide a factory to be able to be called a VST3! + return factory; + } + + void* getFunction (const char* functionName) + { + #if JUCE_WINDOWS + return library.getFunction (functionName); + #else + if (bundleRef == nullptr) + return nullptr; + + CFStringRef name = String (functionName).toCFString(); + void* fn = CFBundleGetFunctionPointerForName (bundleRef, name); + CFRelease (name); + return fn; + #endif + } + +private: + IPluginFactory* factory; + + void releaseFactory() + { + if (factory != nullptr) + factory->release(); + } + + #if JUCE_WINDOWS + DynamicLibrary library; + + bool open (const String& filePath) + { + if (library.open (filePath)) + { + typedef bool (PLUGIN_API *InitModuleProc) (); + if (InitModuleProc proc = (InitModuleProc) getFunction ("InitDll")) + { + if (proc()) + return true; + } + else + { + return true; + } + + library.close(); + } + + return false; + } + + #else + CFBundleRef bundleRef; + + bool open (const String& filePath) + { + const File file (filePath); + const char* const utf8 = file.getFullPathName().toRawUTF8(); + + if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, (CFIndex) std::strlen (utf8), file.isDirectory())) + { + bundleRef = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + + if (bundleRef != nullptr) + { + CFErrorRef error = nullptr; + + if (CFBundleLoadExecutableAndReturnError (bundleRef, &error)) + { + typedef bool (*BundleEntryProc)(CFBundleRef); + + if (BundleEntryProc proc = (BundleEntryProc) getFunction ("bundleEntry")) + { + if (proc (bundleRef)) + return true; + } + else + { + return true; + } + } + + if (error != nullptr) + { + if (CFStringRef failureMessage = CFErrorCopyFailureReason (error)) + { + DBG (String::fromCFString (failureMessage)); + CFRelease (failureMessage); + } + + CFRelease (error); + } + + CFRelease (bundleRef); + bundleRef = nullptr; + } + } + + return false; + } + #endif + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DLLHandle) +}; + +//============================================================================== +class VST3ModuleHandle : public ReferenceCountedObject +{ +public: + explicit VST3ModuleHandle (const File& pluginFile) : file (pluginFile) + { + getActiveModules().add (this); + } + + ~VST3ModuleHandle() + { + getActiveModules().removeFirstMatchingValue (this); + } + + /** + Since there is no apparent indication if a VST3 plugin is a shell or not, + we're stuck iterating through a VST3's factory, creating a description + for every housed plugin. + */ + static bool getAllDescriptionsForFile (OwnedArray& results, + const String& fileOrIdentifier) + { + DLLHandle tempModule (fileOrIdentifier); + + ComSmartPtr pluginFactory (tempModule.getPluginFactory()); + + if (pluginFactory != nullptr) + { + ComSmartPtr host (new VST3HostContext (nullptr)); + DescriptionLister lister (host, pluginFactory); + const Result result (lister.findDescriptionsAndPerform (File (fileOrIdentifier))); + + results.addCopiesOf (lister.list); + + return result.wasOk(); + } + + jassertfalse; + return false; + } + + //============================================================================== + typedef ReferenceCountedObjectPtr Ptr; + + static VST3ModuleHandle::Ptr findOrCreateModule (const File& file, const PluginDescription& description) + { + Array& activeModules = getActiveModules(); + + for (int i = activeModules.size(); --i >= 0;) + { + VST3ModuleHandle* const module = activeModules.getUnchecked (i); + + // VST3s are basically shells, you must therefore check their name along with their file: + if (module->file == file && module->name == description.name) + return module; + } + + VST3ModuleHandle::Ptr m (new VST3ModuleHandle (file)); + + if (! m->open (file, description)) + m = nullptr; + + return m; + } + + //============================================================================== + IPluginFactory* getPluginFactory() { return dllHandle->getPluginFactory(); } + + File file; + String name; + +private: + ScopedPointer dllHandle; + + //============================================================================== + static Array& getActiveModules() + { + static Array activeModules; + return activeModules; + } + + //============================================================================== + bool open (const File& f, const PluginDescription& description) + { + dllHandle = new DLLHandle (f.getFullPathName()); + + ComSmartPtr pluginFactory (dllHandle->getPluginFactory()); + + if (pluginFactory != nullptr) + { + ComSmartPtr host (new VST3HostContext (nullptr)); + MatchingDescriptionFinder finder (host, pluginFactory, description); + + const Result result (finder.findDescriptionsAndPerform (f)); + + if (result.getErrorMessage() == MatchingDescriptionFinder::getSuccessString()) + { + name = description.name; + return true; + } + } + + return false; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3ModuleHandle) +}; + +//============================================================================== +class VST3PluginWindow : public AudioProcessorEditor, + public ComponentMovementWatcher, + public IPlugFrame +{ +public: + VST3PluginWindow (AudioProcessor* owner, IPlugView* pluginView) + : AudioProcessorEditor (owner), + ComponentMovementWatcher (this), + refCount (1), + view (pluginView, false), + pluginHandle (nullptr), + recursiveResize (false) + { + setSize (10, 10); + setOpaque (true); + setVisible (true); + + warnOnFailure (view->setFrame (this)); + + ViewRect rect; + warnOnFailure (view->getSize (&rect)); + resizeWithRect (*this, rect); + } + + ~VST3PluginWindow() + { + warnOnFailure (view->removed()); + getAudioProcessor()->editorBeingDeleted (this); + + #if JUCE_MAC + dummyComponent.setView (nullptr); + #endif + + view = nullptr; + } + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + void paint (Graphics& g) override + { + g.fillAll (Colours::black); + } + + void mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel) override + { + view->onWheel (wheel.deltaY); + } + + void focusGained (FocusChangeType) override { view->onFocus (true); } + void focusLost (FocusChangeType) override { view->onFocus (false); } + + /** It seems that most, if not all, plugins do their own keyboard hooks, + but IPlugView does have a set of keyboard related methods... + */ + bool keyStateChanged (bool /*isKeyDown*/) override { return true; } + bool keyPressed (const KeyPress& /*key*/) override { return true; } + + //============================================================================== + void componentMovedOrResized (bool, bool wasResized) override + { + if (recursiveResize) + return; + + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != nullptr) + { + #if JUCE_WINDOWS + const Point pos (topComp->getLocalPoint (this, Point())); + #endif + + recursiveResize = true; + + ViewRect rect; + + if (wasResized && view->canResize() == kResultTrue) + { + rect.right = (Steinberg::int32) getWidth(); + rect.bottom = (Steinberg::int32) getHeight(); + view->checkSizeConstraint (&rect); + + setSize ((int) rect.getWidth(), (int) rect.getHeight()); + + #if JUCE_WINDOWS + SetWindowPos (pluginHandle, 0, + pos.x, pos.y, rect.getWidth(), rect.getHeight(), + isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); + #elif JUCE_MAC + dummyComponent.setBounds (getLocalBounds()); + #endif + + view->onSize (&rect); + } + else + { + warnOnFailure (view->getSize (&rect)); + + #if JUCE_WINDOWS + SetWindowPos (pluginHandle, 0, + pos.x, pos.y, rect.getWidth(), rect.getHeight(), + isVisible() ? SWP_SHOWWINDOW : SWP_HIDEWINDOW); + #elif JUCE_MAC + dummyComponent.setBounds (0, 0, (int) rect.getWidth(), (int) rect.getHeight()); + #endif + } + + // Some plugins don't update their cursor correctly when mousing out the window + Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); + + recursiveResize = false; + } + } + + void componentPeerChanged() override { } + + void componentVisibilityChanged() override + { + attachPluginWindow(); + componentMovedOrResized (true, true); + } + + tresult PLUGIN_API resizeView (IPlugView* incomingView, ViewRect* newSize) override + { + if (incomingView != nullptr + && newSize != nullptr + && incomingView == view) + { + resizeWithRect (dummyComponent, *newSize); + setSize (dummyComponent.getWidth(), dummyComponent.getHeight()); + return kResultTrue; + } + + jassertfalse; + return kInvalidArgument; + } + +private: + //============================================================================== + Atomic refCount; + ComSmartPtr view; + + #if JUCE_WINDOWS + class ChildComponent : public Component + { + public: + ChildComponent() {} + void paint (Graphics& g) override { g.fillAll (Colours::cornflowerblue); } + + using Component::createNewPeer; + + private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildComponent) + }; + + ChildComponent dummyComponent; + ScopedPointer peer; + typedef HWND HandleFormat; + #elif JUCE_MAC + AutoResizingNSViewComponentWithParent dummyComponent; + typedef NSView* HandleFormat; + #else + Component dummyComponent; + typedef void* HandleFormat; + #endif + + HandleFormat pluginHandle; + bool recursiveResize; + + //============================================================================== + static void resizeWithRect (Component& comp, const ViewRect& rect) + { + comp.setBounds ((int) rect.left, (int) rect.top, + jmax (10, std::abs ((int) rect.getWidth())), + jmax (10, std::abs ((int) rect.getHeight()))); + } + + void attachPluginWindow() + { + if (pluginHandle == nullptr) + { + #if JUCE_WINDOWS + if (Component* topComp = getTopLevelComponent()) + peer = dummyComponent.createNewPeer (0, topComp->getWindowHandle()); + else + peer = nullptr; + + if (peer != nullptr) + pluginHandle = (HandleFormat) peer->getNativeHandle(); + #elif JUCE_MAC + dummyComponent.setBounds (getLocalBounds()); + addAndMakeVisible (dummyComponent); + pluginHandle = (NSView*) dummyComponent.getView(); + jassert (pluginHandle != nil); + #endif + + if (pluginHandle != nullptr) + warnOnFailure (view->attached (pluginHandle, defaultVST3WindowType)); + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginWindow) +}; + +//============================================================================== +class VST3PluginInstance : public AudioPluginInstance +{ +public: + VST3PluginInstance (const VST3ModuleHandle::Ptr& handle) + : module (handle), + numInputAudioBusses (0), + numOutputAudioBusses (0), + inputParameterChanges (new ParameterChangeList()), + outputParameterChanges (new ParameterChangeList()), + midiInputs (new MidiEventList()), + midiOutputs (new MidiEventList()), + isComponentInitialised (false), + isControllerInitialised (false), + isActive (false) + { + host = new VST3HostContext (this); + } + + ~VST3PluginInstance() + { + jassert (getActiveEditor() == nullptr); // You must delete any editors before deleting the plugin instance! + + releaseResources(); + + if (editControllerConnection != nullptr && componentConnection != nullptr) + { + editControllerConnection->disconnect (componentConnection); + componentConnection->disconnect (editControllerConnection); + } + + editController->setComponentHandler (nullptr); + + if (isControllerInitialised) editController->terminate(); + if (isComponentInitialised) component->terminate(); + + componentConnection = nullptr; + editControllerConnection = nullptr; + unitData = nullptr; + unitInfo = nullptr; + programListData = nullptr; + componentHandler2 = nullptr; + componentHandler = nullptr; + processor = nullptr; + editController2 = nullptr; + editController = nullptr; + component = nullptr; + host = nullptr; + module = nullptr; + } + + bool initialise() + { + #if JUCE_WINDOWS + // On Windows it's highly advisable to create your plugins using the message thread, + // because many plugins need a chance to create HWNDs that will get their messages + // delivered by the main message thread, and that's not possible from a background thread. + jassert (MessageManager::getInstance()->isThisTheMessageThread()); + #endif + + ComSmartPtr factory (module->getPluginFactory()); + + PFactoryInfo factoryInfo; + factory->getFactoryInfo (&factoryInfo); + company = toString (factoryInfo.vendor).trim(); + + if (! fetchComponentAndController (factory, factory->countClasses())) + return false; + + // (May return an error if the plugin combines the IComponent and IEditController implementations) + editController->initialize (host->getFUnknown()); + + isControllerInitialised = true; + editController->setComponentHandler (host); + grabInformationObjects(); + synchroniseStates(); + interconnectComponentAndController(); + setupIO(); + return true; + } + + //============================================================================== + void fillInPluginDescription (PluginDescription& description) const override + { + jassert (module != nullptr); + + createPluginDescription (description, module->file, + company, module->name, + *info, info2, infoW, + getNumInputChannels(), + getNumOutputChannels()); + } + + void* getPlatformSpecificData() override { return component; } + void refreshParameterList() override {} + + //============================================================================== + const String getName() const override + { + return module != nullptr ? module->name : String::empty; + } + + void repopulateArrangements() + { + inputArrangements.clearQuick(); + outputArrangements.clearQuick(); + + // NB: Some plugins need a valid arrangement despite specifying 0 for their I/O busses + for (int i = 0; i < jmax (1, numInputAudioBusses); ++i) + inputArrangements.add (getArrangementForBus (processor, true, i)); + + for (int i = 0; i < jmax (1, numOutputAudioBusses); ++i) + outputArrangements.add (getArrangementForBus (processor, false, i)); + } + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override + { + // Avoid redundantly calling things like setActive, which can be a heavy-duty call for some plugins: + if (isActive + && getSampleRate() == sampleRate + && getBlockSize() == estimatedSamplesPerBlock) + return; + + using namespace Vst; + + ProcessSetup setup; + setup.symbolicSampleSize = kSample32; + setup.maxSamplesPerBlock = estimatedSamplesPerBlock; + setup.sampleRate = sampleRate; + setup.processMode = isNonRealtime() ? kOffline : kRealtime; + + warnOnFailure (processor->setupProcessing (setup)); + + if (! isComponentInitialised) + isComponentInitialised = component->initialize (host->getFUnknown()) == kResultTrue; + + editController->setComponentHandler (host); + + if (inputArrangements.size() <= 0 || outputArrangements.size() <= 0) + repopulateArrangements(); + + warnOnFailure (processor->setBusArrangements (inputArrangements.getRawDataPointer(), numInputAudioBusses, + outputArrangements.getRawDataPointer(), numOutputAudioBusses)); + + // Update the num. busses in case the configuration has been modified by the plugin. (May affect number of channels!): + const int newNumInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); + const int newNumOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); + + // Repopulate arrangements if the number of busses have changed: + if (numInputAudioBusses != newNumInputAudioBusses + || numOutputAudioBusses != newNumOutputAudioBusses) + { + numInputAudioBusses = newNumInputAudioBusses; + numOutputAudioBusses = newNumOutputAudioBusses; + + repopulateArrangements(); + } + + // Needed for having the same sample rate in processBlock(); some plugins need this! + setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), + getNumSingleDirectionChannelsFor (component, false, true), + sampleRate, estimatedSamplesPerBlock); + + setStateForAllBusses (true); + + setLatencySamples (jmax (0, (int) processor->getLatencySamples())); + + warnOnFailure (component->setActive (true)); + warnOnFailure (processor->setProcessing (true)); + + isActive = true; + } + + void releaseResources() override + { + if (! isActive) + return; // Avoids redundantly calling things like setActive + + JUCE_TRY + { + isActive = false; + + setStateForAllBusses (false); + + if (processor != nullptr) + warnOnFailure (processor->setProcessing (false)); + + if (component != nullptr) + warnOnFailure (component->setActive (false)); + } + JUCE_CATCH_ALL_ASSERT + } + + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override + { + using namespace Vst; + + if (isActive + && processor != nullptr + && processor->canProcessSampleSize (kSample32) == kResultTrue) + { + const int numSamples = buffer.getNumSamples(); + + ProcessData data; + data.processMode = isNonRealtime() ? kOffline : kRealtime; + data.symbolicSampleSize = kSample32; + data.numInputs = numInputAudioBusses; + data.numOutputs = numOutputAudioBusses; + data.inputParameterChanges = inputParameterChanges; + data.outputParameterChanges = outputParameterChanges; + data.numSamples = (Steinberg::int32) numSamples; + + updateTimingInformation (data, getSampleRate()); + + for (int i = getNumInputChannels(); i < buffer.getNumChannels(); ++i) + buffer.clear (i, 0, numSamples); + + associateTo (data, buffer); + associateTo (data, midiMessages); + + processor->process (data); + + MidiEventList::toMidiBuffer (midiMessages, *midiOutputs); + } + } + + //============================================================================== + String getChannelName (int channelIndex, bool forInput, bool forAudioChannel) const + { + const int numBusses = getNumSingleDirectionBussesFor (component, forInput, forAudioChannel); + int numCountedChannels = 0; + + for (int i = 0; i < numBusses; ++i) + { + Vst::BusInfo busInfo (getBusInfo (forInput, forAudioChannel, i)); + + numCountedChannels += busInfo.channelCount; + + if (channelIndex < numCountedChannels) + return toString (busInfo.name); + } + + return String::empty; + } + + const String getInputChannelName (int channelIndex) const override { return getChannelName (channelIndex, true, true); } + const String getOutputChannelName (int channelIndex) const override { return getChannelName (channelIndex, false, true); } + + bool isInputChannelStereoPair (int channelIndex) const override + { + if (channelIndex < 0 || channelIndex >= getNumInputChannels()) + return false; + + return getBusInfo (true, true).channelCount == 2; + } + + bool isOutputChannelStereoPair (int channelIndex) const override + { + if (channelIndex < 0 || channelIndex >= getNumOutputChannels()) + return false; + + return getBusInfo (false, true).channelCount == 2; + } + + bool acceptsMidi() const override { return getBusInfo (true, false).channelCount > 0; } + bool producesMidi() const override { return getBusInfo (false, false).channelCount > 0; } + + //============================================================================== + bool silenceInProducesSilenceOut() const override + { + if (processor != nullptr) + return processor->getTailSamples() == Vst::kNoTail; + + return true; + } + + /** May return a negative value as a means of informing us that the plugin has "infinite tail," or 0 for "no tail." */ + double getTailLengthSeconds() const override + { + if (processor != nullptr) + { + const double sampleRate = getSampleRate(); + + if (sampleRate > 0.0) + return jlimit (0, 0x7fffffff, (int) processor->getTailSamples()) / sampleRate; + } + + return 0.0; + } + + //============================================================================== + AudioProcessorEditor* createEditor() override + { + if (IPlugView* view = tryCreatingView()) + return new VST3PluginWindow (this, view); + + return nullptr; + } + + bool hasEditor() const override + { + // (if possible, avoid creating a second instance of the editor, because that crashes some plugins) + if (getActiveEditor() != nullptr) + return true; + + ComSmartPtr view (tryCreatingView(), false); + return view != nullptr; + } + + //============================================================================== + int getNumParameters() override + { + if (editController != nullptr) + return (int) editController->getParameterCount(); + + return 0; + } + + const String getParameterName (int parameterIndex) override + { + return toString (getParameterInfoForIndex (parameterIndex).title); + } + + float getParameter (int parameterIndex) override + { + if (editController != nullptr) + { + const uint32 id = getParameterInfoForIndex (parameterIndex).id; + return (float) editController->getParamNormalized (id); + } + + return 0.0f; + } + + const String getParameterText (int parameterIndex) override + { + if (editController != nullptr) + { + const uint32 id = getParameterInfoForIndex (parameterIndex).id; + + Vst::String128 result; + warnOnFailure (editController->getParamStringByValue (id, editController->getParamNormalized (id), result)); + + return toString (result); + } + + return String::empty; + } + + void setParameter (int parameterIndex, float newValue) override + { + if (editController != nullptr) + { + const uint32 id = getParameterInfoForIndex (parameterIndex).id; + editController->setParamNormalized (id, (double) newValue); + } + } + + //============================================================================== + int getNumPrograms() override { return getProgramListInfo (0).programCount; } + int getCurrentProgram() override { return 0; } + void setCurrentProgram (int) override {} + void changeProgramName (int, const String&) override {} + + const String getProgramName (int index) override + { + Vst::String128 result; + unitInfo->getProgramName (getProgramListInfo (0).id, index, result); + return toString (result); + } + + //============================================================================== + void reset() override + { + if (component != nullptr) + { + component->setActive (false); + component->setActive (true); + } + } + + //============================================================================== + void getStateInformation (MemoryBlock& destData) override + { + XmlElement state ("VST3PluginState"); + + appendStateFrom (state, component, "IComponent"); + appendStateFrom (state, editController, "IEditController"); + + AudioProcessor::copyXmlToBinary (state, destData); + } + + void setStateInformation (const void* data, int sizeInBytes) override + { + ScopedPointer head (AudioProcessor::getXmlFromBinary (data, sizeInBytes)); + + if (head != nullptr) + { + ComSmartPtr s (createMemoryStreamForState (*head, "IComponent")); + + if (s != nullptr && component != nullptr) + component->setState (s); + + if (editController != nullptr) + { + if (s != nullptr) + editController->setComponentState (s); + + s = createMemoryStreamForState (*head, "IEditController"); + + if (s != nullptr) + editController->setState (s); + } + } + } + + /** @note Not applicable to VST3 */ + void getCurrentProgramStateInformation (MemoryBlock& destData) override + { + destData.setSize (0, true); + } + + /** @note Not applicable to VST3 */ + void setCurrentProgramStateInformation (const void* data, int sizeInBytes) override + { + (void) data; + (void) sizeInBytes; + } + +private: + //============================================================================== + VST3ModuleHandle::Ptr module; + + friend VST3HostContext; + ComSmartPtr host; + + // Information objects: + String company; + ScopedPointer info; + ScopedPointer info2; + ScopedPointer infoW; + + // Rudimentary interfaces: + ComSmartPtr component; + ComSmartPtr editController; + ComSmartPtr editController2; + ComSmartPtr processor; + ComSmartPtr componentHandler; + ComSmartPtr componentHandler2; + ComSmartPtr unitInfo; + ComSmartPtr unitData; + ComSmartPtr programListData; + ComSmartPtr componentConnection; + ComSmartPtr editControllerConnection; + + /** The number of IO busses MUST match that of the plugin, + even if there aren't enough channels to process, + as very poorly specified by the Steinberg SDK + */ + int numInputAudioBusses, numOutputAudioBusses; + Array inputArrangements, outputArrangements; // Caching to improve performance and to avoid possible non-thread-safe calls to getBusArrangements(). + VST3BufferExchange::BusMap inputBusMap, outputBusMap; + Array inputBusses, outputBusses; + + //============================================================================== + template + static void appendStateFrom (XmlElement& head, ComSmartPtr& object, const String& identifier) + { + if (object != nullptr) + { + Steinberg::MemoryStream stream; + + if (object->getState (&stream) == kResultTrue) + { + MemoryBlock info (stream.getData(), (std::size_t) stream.getSize()); + head.createNewChildElement (identifier)->addTextElement (info.toBase64Encoding()); + } + } + } + + static Steinberg::MemoryStream* createMemoryStreamForState (XmlElement& head, StringRef identifier) + { + Steinberg::MemoryStream* stream = nullptr; + + if (XmlElement* const state = head.getChildByName (identifier)) + { + MemoryBlock mem; + + if (mem.fromBase64Encoding (state->getAllSubText())) + stream = new Steinberg::MemoryStream (mem.getData(), (TSize) mem.getSize()); + } + + return stream; + } + + //============================================================================== + class ParameterChangeList : public Vst::IParameterChanges + { + public: + ParameterChangeList() {} + virtual ~ParameterChangeList() {} + + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS + + Steinberg::int32 PLUGIN_API getParameterCount() override { return 0; } + + Vst::IParamValueQueue* PLUGIN_API getParameterData (Steinberg::int32) override + { + return nullptr; + } + + Vst::IParamValueQueue* PLUGIN_API addParameterData (const Vst::ParamID&, Steinberg::int32& index) override + { + index = 0; + return nullptr; + } + + private: + Atomic refCount; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParameterChangeList) + }; + + ComSmartPtr inputParameterChanges, outputParameterChanges; + ComSmartPtr midiInputs, midiOutputs; + Vst::ProcessContext timingInfo; //< Only use this in processBlock()! + bool isComponentInitialised, isControllerInitialised, isActive; + + //============================================================================== + bool fetchComponentAndController (IPluginFactory* factory, const Steinberg::int32 numClasses) + { + jassert (numClasses >= 0); // The plugin must provide at least an IComponent and IEditController! + + for (Steinberg::int32 j = 0; j < numClasses; ++j) + { + info = new PClassInfo(); + factory->getClassInfo (j, info); + + if (std::strcmp (info->category, kVstAudioEffectClass) != 0) + continue; + + const String name (toString (info->name).trim()); + + if (module->name != name) + continue; + + { + ComSmartPtr pf2; + ComSmartPtr pf3; + + if (pf2.loadFrom (factory)) + { + info2 = new PClassInfo2(); + pf2->getClassInfo2 (j, info2); + } + else + { + info2 = nullptr; + } + + if (pf3.loadFrom (factory)) + { + pf3->setHostContext (host->getFUnknown()); + infoW = new PClassInfoW(); + pf3->getClassInfoUnicode (j, infoW); + } + else + { + infoW = nullptr; + } + } + + bool failed = true; + + if (component.loadFrom (factory, info->cid) && component != nullptr) + { + warnOnFailure (component->setIoMode (isNonRealtime() ? Vst::kOffline : Vst::kRealtime)); + + if (warnOnFailure (component->initialize (host->getFUnknown())) != kResultOk) + return false; + + isComponentInitialised = true; + + // Get the IEditController: + TUID controllerCID = { 0 }; + + if (component->getControllerClassId (controllerCID) == kResultTrue && FUID (controllerCID).isValid()) + editController.loadFrom (factory, controllerCID); + + if (editController == nullptr) + { + // Try finding the IEditController the long way around: + for (Steinberg::int32 i = 0; i < numClasses; ++i) + { + PClassInfo classInfo; + factory->getClassInfo (i, &classInfo); + + if (std::strcmp (classInfo.category, kVstComponentControllerClass) == 0) + editController.loadFrom (factory, classInfo.cid); + } + } + + if (editController == nullptr) + editController.loadFrom (component); + + failed = editController == nullptr; + } + + if (failed) + { + jassertfalse; // The plugin won't function without a valid IComponent and IEditController implementation! + + if (component != nullptr) + { + component->terminate(); + component = nullptr; + } + + if (editController != nullptr) + { + editController->terminate(); + editController = nullptr; + } + + break; + } + + return true; + } + + return false; + } + + /** Some plugins need to be "connected" to intercommunicate between their implemented classes */ + void interconnectComponentAndController() + { + componentConnection.loadFrom (component); + editControllerConnection.loadFrom (editController); + + if (componentConnection != nullptr && editControllerConnection != nullptr) + { + warnOnFailure (editControllerConnection->connect (componentConnection)); + warnOnFailure (componentConnection->connect (editControllerConnection)); + } + } + + void synchroniseStates() + { + Steinberg::MemoryStream stream; + + if (component->getState (&stream) == kResultTrue) + if (stream.seek (0, Steinberg::IBStream::kIBSeekSet, nullptr) == kResultTrue) + warnOnFailure (editController->setComponentState (&stream)); + } + + void grabInformationObjects() + { + processor.loadFrom (component); + unitInfo.loadFrom (component); + programListData.loadFrom (component); + unitData.loadFrom (component); + editController2.loadFrom (component); + componentHandler.loadFrom (component); + componentHandler2.loadFrom (component); + + if (processor == nullptr) processor.loadFrom (editController); + if (unitInfo == nullptr) unitInfo.loadFrom (editController); + if (programListData == nullptr) programListData.loadFrom (editController); + if (unitData == nullptr) unitData.loadFrom (editController); + if (editController2 == nullptr) editController2.loadFrom (editController); + if (componentHandler == nullptr) componentHandler.loadFrom (editController); + if (componentHandler2 == nullptr) componentHandler2.loadFrom (editController); + } + + void setStateForAllBusses (bool newState) + { + setStateForAllBussesOfType (component, newState, true, true); // Activate/deactivate audio inputs + setStateForAllBussesOfType (component, newState, false, true); // Activate/deactivate audio outputs + setStateForAllBussesOfType (component, newState, true, false); // Activate/deactivate MIDI inputs + setStateForAllBussesOfType (component, newState, false, false); // Activate/deactivate MIDI outputs + } + + void setupIO() + { + setStateForAllBusses (true); + + Vst::ProcessSetup setup; + setup.symbolicSampleSize = Vst::kSample32; + setup.maxSamplesPerBlock = 1024; + setup.sampleRate = 44100.0; + setup.processMode = Vst::kRealtime; + + warnOnFailure (processor->setupProcessing (setup)); + + numInputAudioBusses = getNumSingleDirectionBussesFor (component, true, true); + numOutputAudioBusses = getNumSingleDirectionBussesFor (component, false, true); + + setPlayConfigDetails (getNumSingleDirectionChannelsFor (component, true, true), + getNumSingleDirectionChannelsFor (component, false, true), + setup.sampleRate, (int) setup.maxSamplesPerBlock); + } + + //============================================================================== + Vst::BusInfo getBusInfo (bool forInput, bool forAudio, int index = 0) const + { + Vst::BusInfo busInfo; + busInfo.mediaType = forAudio ? Vst::kAudio : Vst::kEvent; + busInfo.direction = forInput ? Vst::kInput : Vst::kOutput; + + component->getBusInfo (busInfo.mediaType, busInfo.direction, + (Steinberg::int32) index, busInfo); + return busInfo; + } + + //============================================================================== + /** @note An IPlugView, when first created, should start with a ref-count of 1! */ + IPlugView* tryCreatingView() const + { + IPlugView* v = editController->createView (Vst::ViewType::kEditor); + + if (v == nullptr) v = editController->createView (nullptr); + if (v == nullptr) editController->queryInterface (IPlugView::iid, (void**) &v); + + return v; + } + + //============================================================================== + void associateTo (Vst::ProcessData& destination, AudioSampleBuffer& buffer) + { + using namespace VST3BufferExchange; + + mapBufferToBusses (inputBusses, inputBusMap, inputArrangements, buffer); + mapBufferToBusses (outputBusses, outputBusMap, outputArrangements, buffer); + + destination.inputs = inputBusses.getRawDataPointer(); + destination.outputs = outputBusses.getRawDataPointer(); + } + + void associateTo (Vst::ProcessData& destination, MidiBuffer& midiBuffer) + { + midiInputs->clear(); + midiOutputs->clear(); + + MidiEventList::toEventList (*midiInputs, midiBuffer); + + destination.inputEvents = midiInputs; + destination.outputEvents = midiOutputs; + } + + void updateTimingInformation (Vst::ProcessData& destination, double processSampleRate) + { + toProcessContext (timingInfo, getPlayHead(), processSampleRate); + destination.processContext = &timingInfo; + } + + Vst::ParameterInfo getParameterInfoForIndex (int index) const + { + Vst::ParameterInfo paramInfo = { 0 }; + + if (processor != nullptr) + editController->getParameterInfo (index, paramInfo); + + return paramInfo; + } + + Vst::ProgramListInfo getProgramListInfo (int index) const + { + Vst::ProgramListInfo paramInfo = { 0 }; + + if (unitInfo != nullptr) + unitInfo->getProgramListInfo (index, paramInfo); + + return paramInfo; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginInstance) +}; + +}; + +//============================================================================== +VST3PluginFormat::VST3PluginFormat() {} +VST3PluginFormat::~VST3PluginFormat() {} + +void VST3PluginFormat::findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) +{ + if (! fileMightContainThisPluginType (fileOrIdentifier)) + return; + + VST3Classes::VST3ModuleHandle::getAllDescriptionsForFile (results, fileOrIdentifier); +} + +AudioPluginInstance* VST3PluginFormat::createInstanceFromDescription (const PluginDescription& description, double, int) +{ + ScopedPointer result; + + if (fileMightContainThisPluginType (description.fileOrIdentifier)) + { + File file (description.fileOrIdentifier); + + const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); + file.getParentDirectory().setAsCurrentWorkingDirectory(); + + if (const VST3Classes::VST3ModuleHandle::Ptr module = VST3Classes::VST3ModuleHandle::findOrCreateModule (file, description)) + { + result = new VST3Classes::VST3PluginInstance (module); + + if (! result->initialise()) + result = nullptr; + } + + previousWorkingDirectory.setAsCurrentWorkingDirectory(); + } + + return result.release(); +} + +bool VST3PluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) +{ + const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); + + return f.hasFileExtension (".vst3") + #if JUCE_MAC + && f.exists(); + #else + && f.existsAsFile(); + #endif +} + +String VST3PluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) +{ + return fileOrIdentifier; //Impossible to tell because every VST3 is a type of shell... +} + +bool VST3PluginFormat::pluginNeedsRescanning (const PluginDescription& description) +{ + return File (description.fileOrIdentifier).getLastModificationTime() != description.lastFileModTime; +} + +bool VST3PluginFormat::doesPluginStillExist (const PluginDescription& description) +{ + return File (description.fileOrIdentifier).exists(); +} + +StringArray VST3PluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive) +{ + StringArray results; + + for (int i = 0; i < directoriesToSearch.getNumPaths(); ++i) + recursiveFileSearch (results, directoriesToSearch[i], recursive); + + return results; +} + +void VST3PluginFormat::recursiveFileSearch (StringArray& results, const File& directory, const bool recursive) +{ + DirectoryIterator iter (directory, false, "*", File::findFilesAndDirectories); + + while (iter.next()) + { + const File f (iter.getFile()); + bool isPlugin = false; + + if (fileMightContainThisPluginType (f.getFullPathName())) + { + isPlugin = true; + results.add (f.getFullPathName()); + } + + if (recursive && (! isPlugin) && f.isDirectory()) + recursiveFileSearch (results, f, true); + } +} + +FileSearchPath VST3PluginFormat::getDefaultLocationsToSearch() +{ + #if JUCE_WINDOWS + const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); + return FileSearchPath (programFiles + "\\Common Files\\VST3"); + #elif JUCE_MAC + return FileSearchPath ("/Library/Audio/Plug-Ins/VST3;~/Library/Audio/Plug-Ins/VST3"); + #else + return FileSearchPath(); + #endif +} + +#endif //JUCE_PLUGINHOST_VST3 diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h new file mode 100644 index 0000000..308ff5a --- /dev/null +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h @@ -0,0 +1,72 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_VST3PLUGINFORMAT_H_INCLUDED +#define JUCE_VST3PLUGINFORMAT_H_INCLUDED + +#if JUCE_PLUGINHOST_VST3 +/** + Implements a plugin format for VST3s. +*/ +class JUCE_API VST3PluginFormat : public AudioPluginFormat +{ +public: + /** Constructor */ + VST3PluginFormat(); + + /** Destructor */ + ~VST3PluginFormat(); + + //============================================================================== + /** @internal */ + String getName() const override { return "VST3"; } + /** @internal */ + void findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) override; + /** @internal */ + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& description, double, int) override; + /** @internal */ + bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; + /** @internal */ + String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; + /** @internal */ + bool pluginNeedsRescanning (const PluginDescription& description) override; + /** @internal */ + StringArray searchPathsForPlugins (const FileSearchPath& searchPath, bool recursive) override; + /** @internal */ + bool doesPluginStillExist (const PluginDescription& description) override; + /** @internal */ + FileSearchPath getDefaultLocationsToSearch() override; + /** @internal */ + bool canScanForPlugins() const override { return true; } + +private: + //============================================================================== + void recursiveFileSearch (StringArray&, const File&, bool recursive); + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat) +}; + +#endif // JUCE_PLUGINHOST_VST3 +#endif // JUCE_VST3PLUGINFORMAT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h index 89a6c39..f442eab 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h @@ -1,33 +1,31 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifdef __aeffect__ - -#ifndef __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ -#define __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ +#ifdef __aeffect__ // NB: this must come first, *before* the header-guard. +#ifndef JUCE_VSTMIDIEVENTLIST_H_INCLUDED +#define JUCE_VSTMIDIEVENTLIST_H_INCLUDED //============================================================================== /** Holds a set of VSTMidiEvent objects and makes it easy to add @@ -79,7 +77,7 @@ public: } e->deltaFrames = frameOffset; - memcpy (e->midiData, midiData, numBytes); + memcpy (e->midiData, midiData, (size_t) numBytes); } else { @@ -89,7 +87,7 @@ public: delete[] se->sysexDump; se->sysexDump = new char [numBytes]; - memcpy (se->sysexDump, midiData, numBytes); + memcpy (se->sysexDump, midiData, (size_t) numBytes); se->type = kVstSysExType; se->byteSize = sizeof (VstMidiSysexEvent); @@ -134,7 +132,7 @@ public: { numEventsNeeded = (numEventsNeeded + 32) & ~31; - const int size = 20 + sizeof (VstEvent*) * numEventsNeeded; + const size_t size = 20 + sizeof (VstEvent*) * (size_t) numEventsNeeded; if (events == nullptr) events.calloc (size, 1); @@ -185,6 +183,5 @@ private: } }; - -#endif // __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ -#endif // __JUCE_VSTMIDIEVENTLIST_JUCEHEADER__ +#endif // JUCE_VSTMIDIEVENTLIST_H_INCLUDED +#endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index 16b6be0..458d59b 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,8 +25,6 @@ #if JUCE_PLUGINHOST_VST //============================================================================== -#if ! (JUCE_MAC && JUCE_64BIT) - #if JUCE_MAC && JUCE_SUPPORT_CARBON #include "../../juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h" #endif @@ -35,7 +32,7 @@ #if JUCE_MAC static bool makeFSRefFromPath (FSRef* destFSRef, const String& path) { - return FSPathMakeRef (reinterpret_cast (path.toUTF8().getAddress()), destFSRef, 0) == noErr; + return FSPathMakeRef (reinterpret_cast (path.toRawUTF8()), destFSRef, 0) == noErr; } #endif @@ -46,6 +43,8 @@ #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4996) +#elif ! JUCE_MINGW + #define __cdecl #endif /* Obviously you're going to need the Steinberg vstsdk2.4 folder in @@ -54,7 +53,7 @@ If you're not interested in VSTs, you can disable them by setting the JUCE_PLUGINHOST_VST flag to 0. */ -#include +#include "pluginterfaces/vst2.x/aeffectx.h" #if JUCE_MSVC #pragma warning (pop) @@ -62,113 +61,121 @@ #endif //============================================================================== -#if JUCE_LINUX - #define Font juce::Font - #define KeyPress juce::KeyPress - #define Drawable juce::Drawable - #define Time juce::Time -#endif - #include "juce_VSTMidiEventList.h" -#if ! JUCE_WINDOWS +#if JUCE_MINGW + #ifndef WM_APPCOMMAND + #define WM_APPCOMMAND 0x0319 + #endif + + extern "C" void _fpreset(); + extern "C" void _clearfp(); +#elif ! JUCE_WINDOWS static void _fpreset() {} static void _clearfp() {} #endif +#ifndef JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN + #define JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN +#endif + +#ifndef JUCE_VST_WRAPPER_INVOKE_MAIN + #define JUCE_VST_WRAPPER_INVOKE_MAIN effect = module->moduleMain (&audioMaster); +#endif + //============================================================================== const int fxbVersionNum = 1; struct fxProgram { - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxCk' - long version; - long fxID; // fx unique id - long fxVersion; - long numParams; + VstInt32 chunkMagic; // 'CcnK' + VstInt32 byteSize; // of this chunk, excl. magic + byteSize + VstInt32 fxMagic; // 'FxCk' + VstInt32 version; + VstInt32 fxID; // fx unique id + VstInt32 fxVersion; + VstInt32 numParams; char prgName[28]; float params[1]; // variable no. of parameters }; struct fxSet { - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxBk' - long version; - long fxID; // fx unique id - long fxVersion; - long numPrograms; + VstInt32 chunkMagic; // 'CcnK' + VstInt32 byteSize; // of this chunk, excl. magic + byteSize + VstInt32 fxMagic; // 'FxBk' + VstInt32 version; + VstInt32 fxID; // fx unique id + VstInt32 fxVersion; + VstInt32 numPrograms; char future[128]; fxProgram programs[1]; // variable no. of programs }; struct fxChunkSet { - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' - long version; - long fxID; // fx unique id - long fxVersion; - long numPrograms; + VstInt32 chunkMagic; // 'CcnK' + VstInt32 byteSize; // of this chunk, excl. magic + byteSize + VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + VstInt32 version; + VstInt32 fxID; // fx unique id + VstInt32 fxVersion; + VstInt32 numPrograms; char future[128]; - long chunkSize; + VstInt32 chunkSize; char chunk[8]; // variable }; struct fxProgramSet { - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' - long version; - long fxID; // fx unique id - long fxVersion; - long numPrograms; + VstInt32 chunkMagic; // 'CcnK' + VstInt32 byteSize; // of this chunk, excl. magic + byteSize + VstInt32 fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + VstInt32 version; + VstInt32 fxID; // fx unique id + VstInt32 fxVersion; + VstInt32 numPrograms; char name[28]; - long chunkSize; + VstInt32 chunkSize; char chunk[8]; // variable }; namespace { - long vst_swap (const long x) noexcept + VstInt32 vst_swap (const VstInt32 x) noexcept { - #ifdef JUCE_LITTLE_ENDIAN - return (long) ByteOrder::swap ((uint32) x); - #else + #ifdef JUCE_LITTLE_ENDIAN + return (VstInt32) ByteOrder::swap ((uint32) x); + #else return x; - #endif + #endif } float vst_swapFloat (const float x) noexcept { - #ifdef JUCE_LITTLE_ENDIAN + #ifdef JUCE_LITTLE_ENDIAN union { uint32 asInt; float asFloat; } n; n.asFloat = x; n.asInt = ByteOrder::swap (n.asInt); return n.asFloat; - #else + #else return x; - #endif + #endif } double getVSTHostTimeNanoseconds() { - #if JUCE_WINDOWS + #if JUCE_WINDOWS return timeGetTime() * 1000000.0; - #elif JUCE_LINUX + #elif JUCE_LINUX timeval micro; gettimeofday (µ, 0); return micro.tv_usec * 1000.0; - #elif JUCE_MAC + #elif JUCE_MAC UnsignedWide micro; Microseconds (µ); return micro.lo * 1000.0; - #endif + #endif } } @@ -199,7 +206,7 @@ public: private: const bool isMessageThread; - JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer); + JUCE_DECLARE_NON_COPYABLE (IdleCallRecursionPreventer) }; class VSTPluginWindow; @@ -207,13 +214,13 @@ class VSTPluginWindow; //============================================================================== // Change this to disable logging of various VST activities #ifndef VST_LOGGING - #define VST_LOGGING 1 + #define VST_LOGGING 1 #endif #if VST_LOGGING - #define log(a) Logger::writeToLog(a); + #define JUCE_VST_LOG(a) Logger::writeToLog(a); #else - #define log(a) + #define JUCE_VST_LOG(a) #endif //============================================================================== @@ -237,17 +244,17 @@ extern XContext windowHandleXContext; typedef void (*EventProcPtr) (XEvent* ev); -static bool xErrorTriggered; - namespace { - int temporaryErrorHandler (Display*, XErrorEvent*) + static bool xErrorTriggered = false; + + static int temporaryErrorHandler (Display*, XErrorEvent*) { xErrorTriggered = true; return 0; } - int getPropertyFromXWindow (Window handle, Atom atom) + static EventProcPtr getPropertyFromXWindow (Window handle, Atom atom) { XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); xErrorTriggered = false; @@ -262,7 +269,7 @@ namespace XSetErrorHandler (oldErrorHandler); - return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast (data) + return (userCount == 1 && ! xErrorTriggered) ? *reinterpret_cast (data) : 0; } @@ -270,7 +277,7 @@ namespace { Window rootWindow, parentWindow; Window* childWindows; - unsigned int numChildren; + unsigned int numChildren = 0; XQueryTree (display, windowToCheck, @@ -285,7 +292,7 @@ namespace return 0; } - void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) noexcept + static void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) noexcept { if (e.mods.isLeftButtonDown()) { @@ -304,21 +311,21 @@ namespace } } - void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) noexcept + static void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) noexcept { if (e.mods.isLeftButtonDown()) ev.xmotion.state |= Button1Mask; else if (e.mods.isRightButtonDown()) ev.xmotion.state |= Button3Mask; else if (e.mods.isMiddleButtonDown()) ev.xmotion.state |= Button2Mask; } - void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) noexcept + static void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) noexcept { if (e.mods.isLeftButtonDown()) ev.xcrossing.state |= Button1Mask; else if (e.mods.isRightButtonDown()) ev.xcrossing.state |= Button3Mask; else if (e.mods.isMiddleButtonDown()) ev.xcrossing.state |= Button2Mask; } - void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) noexcept + static void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) noexcept { if (increment < 0) { @@ -341,8 +348,11 @@ class ModuleHandle : public ReferenceCountedObject public: //============================================================================== File file; - MainCall moduleMain; + MainCall moduleMain, customMain; String pluginName; + ScopedPointer vstXml; + + typedef ReferenceCountedObjectPtr Ptr; static Array & getActiveModules() { @@ -366,9 +376,9 @@ public: const IdleCallRecursionPreventer icrp; shellUIDToCreate = 0; - log ("Attempting to load VST: " + file.getFullPathName()); + JUCE_VST_LOG ("Attempting to load VST: " + file.getFullPathName()); - ScopedPointer m (new ModuleHandle (file)); + ScopedPointer m (new ModuleHandle (file)); if (! m->open()) m = nullptr; @@ -379,27 +389,29 @@ public: } //============================================================================== - ModuleHandle (const File& file_) - : file (file_), - moduleMain (0) + ModuleHandle (const File& f) + : file (f), moduleMain (nullptr), customMain (nullptr) #if JUCE_MAC - , fragId (0), resHandle (0), bundleRef (0), resFileId (0) + #if JUCE_PPC + , fragId (0) + #endif + , resHandle (0), bundleRef (0), resFileId (0) #endif { getActiveModules().add (this); #if JUCE_WINDOWS || JUCE_LINUX - fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName(); + fullParentDirectoryPathName = f.getParentDirectory().getFullPathName(); #elif JUCE_MAC FSRef ref; - makeFSRefFromPath (&ref, file_.getParentDirectory().getFullPathName()); + makeFSRefFromPath (&ref, f.getParentDirectory().getFullPathName()); FSGetCatalogInfo (&ref, kFSCatInfoNone, 0, 0, &parentDirFSSpec, 0); #endif } ~ModuleHandle() { - getActiveModules().removeValue (this); + getActiveModules().removeFirstMatchingValue (this); close(); } @@ -410,16 +422,6 @@ public: bool open() { - #if JUCE_WINDOWS - static bool timePeriodSet = false; - - if (! timePeriodSet) - { - timePeriodSet = true; - timeBeginPeriod (2); - } - #endif - pluginName = file.getFileNameWithoutExtension(); module.open (file.getFullPathName()); @@ -429,6 +431,18 @@ public: if (moduleMain == nullptr) moduleMain = (MainCall) module.getFunction ("main"); + JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN + + if (moduleMain != nullptr) + { + vstXml = XmlDocument::parse (file.withFileExtension ("vstxml")); + + #if JUCE_WINDOWS + if (vstXml == nullptr) + vstXml = XmlDocument::parse (getDLLResource (file, "VSTXML", 1)); + #endif + } + return moduleMain != nullptr; } @@ -444,8 +458,31 @@ public: eff->dispatcher (eff, effClose, 0, 0, 0, 0); } + #if JUCE_WINDOWS + static String getDLLResource (const File& dllFile, const String& type, int resID) + { + DynamicLibrary dll (dllFile.getFullPathName()); + HMODULE dllModule = (HMODULE) dll.getNativeHandle(); + + if (dllModule != INVALID_HANDLE_VALUE) + { + if (HRSRC res = FindResource (dllModule, MAKEINTRESOURCE (resID), type.toWideCharPointer())) + { + if (HGLOBAL hGlob = LoadResource (dllModule, res)) + { + const char* data = static_cast (LockResource (hGlob)); + return String::fromUTF8 (data, SizeofResource (dllModule, res)); + } + } + } + + return String::empty; + } + #endif #else + #if JUCE_PPC CFragConnectionID fragId; + #endif Handle resHandle; CFBundleRef bundleRef; FSSpec parentDirFSSpec; @@ -454,15 +491,13 @@ public: bool open() { bool ok = false; - const String filename (file.getFullPathName()); if (file.hasFileExtension (".vst")) { - const char* const utf8 = filename.toUTF8().getAddress(); - CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory()); + const char* const utf8 = file.getFullPathName().toRawUTF8(); - if (url != 0) + if (CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory())) { bundleRef = CFBundleCreate (kCFAllocatorDefault, url); CFRelease (url); @@ -473,14 +508,14 @@ public: { moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho")); - if (moduleMain == 0) + if (moduleMain == nullptr) moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain")); - if (moduleMain != 0) - { - CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); + JUCE_VST_WRAPPER_LOAD_CUSTOM_MAIN - if (name != 0) + if (moduleMain != nullptr) + { + if (CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName"))) { if (CFGetTypeID (name) == CFStringGetTypeID()) { @@ -497,6 +532,14 @@ public: resFileId = CFBundleOpenBundleResourceMap (bundleRef); ok = true; + + Array vstXmlFiles; + file.getChildFile ("Contents") + .getChildFile ("Resources") + .findChildFiles (vstXmlFiles, File::findFiles, false, "*.vstxml"); + + if (vstXmlFiles.size() > 0) + vstXml = XmlDocument::parse (vstXmlFiles.getReference(0)); } } @@ -514,7 +557,7 @@ public: { FSRef fn; - if (FSPathMakeRef ((UInt8*) filename.toUTF8().getAddress(), &fn, 0) == noErr) + if (FSPathMakeRef ((UInt8*) file.getFullPathName().toRawUTF8(), &fn, 0) == noErr) { resFileId = FSOpenResFile (&fn, fsRdPerm); @@ -536,7 +579,7 @@ public: DetachResource (resHandle); HLock (resHandle); - Ptr ptr; + ::Ptr ptr; Str255 errorText; OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle), @@ -572,7 +615,7 @@ public: #if JUCE_PPC if (fragId != 0) { - if (moduleMain != 0) + if (moduleMain != nullptr) disposeMachOFromCFM ((void*) moduleMain); CloseConnection (&fragId); @@ -647,11 +690,11 @@ public: { if (fragId != 0) { - eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher); - eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process); - eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter); - eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter); - eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing); + eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher); + eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process); + eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter); + eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter); + eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing); } } #endif @@ -659,26 +702,100 @@ public: #endif private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleHandle); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ModuleHandle) }; -//============================================================================== -/** - An instance of a plugin, created by a VSTPluginFormat. +static const int defaultVSTSampleRateValue = 44100; +static const int defaultVSTBlockSizeValue = 512; -*/ +//============================================================================== +//============================================================================== class VSTPluginInstance : public AudioPluginInstance, private Timer, private AsyncUpdater { public: - //============================================================================== - ~VSTPluginInstance(); + VSTPluginInstance (const ModuleHandle::Ptr& module_) + : effect (nullptr), + module (module_), + usesCocoaNSView (false), + name (module_->pluginName), + wantsMidiMessages (false), + initialised (false), + isPowerOn (false), + tempBuffer (1, 1) + { + try + { + const IdleCallRecursionPreventer icrp; + _fpreset(); - //============================================================================== - // AudioPluginInstance methods: + JUCE_VST_LOG ("Creating VST instance: " + name); - void fillInPluginDescription (PluginDescription& desc) const + #if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + + #if JUCE_PPC + if (module->fragId != 0) + { + static void* audioMasterCoerced = nullptr; + if (audioMasterCoerced == nullptr) + audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster); + + effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); + } + else + #endif + #endif + { + JUCE_VST_WRAPPER_INVOKE_MAIN + } + + if (effect != nullptr && effect->magic == kEffectMagic) + { + #if JUCE_PPC + module->coerceAEffectFunctionCalls (effect); + #endif + + jassert (effect->resvd2 == 0); + jassert (effect->object != 0); + + _fpreset(); // some dodgy plugs fuck around with this + } + else + { + effect = nullptr; + } + } + catch (...) + {} + } + + ~VSTPluginInstance() + { + const ScopedLock sl (lock); + + if (effect != nullptr && effect->magic == kEffectMagic) + { + #if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + #endif + + // Must delete any editors before deleting the plugin instance! + jassert (getActiveEditor() == 0); + + _fpreset(); // some dodgy plugs fuck around with this + + module->closeEffect (effect); + } + + module = nullptr; + effect = nullptr; + } + + void fillInPluginDescription (PluginDescription& desc) const override { desc.name = name; @@ -710,61 +827,847 @@ public: desc.isInstrument = (effect != nullptr && (effect->flags & effFlagsIsSynth) != 0); } - void* getPlatformSpecificData() { return effect; } - const String getName() const { return name; } - int getUID() const; - bool acceptsMidi() const { return wantsMidiMessages; } - bool producesMidi() const { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; } + void initialise (double initialSampleRate, int initialBlockSize) + { + if (initialised || effect == nullptr) + return; + + #if JUCE_WINDOWS + // On Windows it's highly advisable to create your plugins using the message thread, + // because many plugins need a chance to create HWNDs that will get their + // messages delivered by the main message thread, and that's not possible from + // a background thread. + jassert (MessageManager::getInstance()->isThisTheMessageThread()); + #endif + + JUCE_VST_LOG ("Initialising VST: " + module->pluginName + " (" + getVersion() + ")"); + initialised = true; + + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + initialSampleRate, initialBlockSize); + + dispatch (effIdentify, 0, 0, 0, 0); + + if (getSampleRate() > 0) + dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); + + if (getBlockSize() > 0) + dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); + + dispatch (effOpen, 0, 0, 0, 0); + + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + getSampleRate(), getBlockSize()); + + if (getNumPrograms() > 1) + setCurrentProgram (0); + else + dispatch (effSetProgram, 0, 0, 0, 0); + + for (int i = effect->numInputs; --i >= 0;) dispatch (effConnectInput, i, 1, 0, 0); + for (int i = effect->numOutputs; --i >= 0;) dispatch (effConnectOutput, i, 1, 0, 0); + + if (getVstCategory() != kPlugCategShell) // (workaround for Waves 5 plugins which crash during this call) + updateStoredProgramNames(); + + wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; + + #if JUCE_MAC && JUCE_SUPPORT_CARBON + usesCocoaNSView = (dispatch (effCanDo, 0, 0, (void*) "hasCockosViewAsConfig", 0) & 0xffff0000) == 0xbeef0000; + #endif + + setLatencySamples (effect->initialDelay); + } + + void* getPlatformSpecificData() override { return effect; } + const String getName() const override { return name; } + + int getUID() const + { + int uid = effect != nullptr ? effect->uniqueID : 0; + + if (uid == 0) + uid = module->file.hashCode(); + + return uid; + } + + bool silenceInProducesSilenceOut() const override + { + return effect == nullptr || (effect->flags & effFlagsNoSoundInStop) != 0; + } + + double getTailLengthSeconds() const override + { + if (effect == nullptr) + return 0.0; + + const double sampleRate = getSampleRate(); + + if (sampleRate <= 0) + return 0.0; + + VstIntPtr samples = dispatch (effGetTailSize, 0, 0, 0, 0); + return samples / sampleRate; + } + + bool acceptsMidi() const override { return wantsMidiMessages; } + bool producesMidi() const override { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; } + + VstPlugCategory getVstCategory() const noexcept { return (VstPlugCategory) dispatch (effGetPlugCategory, 0, 0, 0, 0); } //============================================================================== - // AudioProcessor methods: + void prepareToPlay (double rate, int samplesPerBlockExpected) override + { + setPlayConfigDetails (effect->numInputs, effect->numOutputs, rate, samplesPerBlockExpected); - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages); + vstHostTime.tempo = 120.0; + vstHostTime.timeSigNumerator = 4; + vstHostTime.timeSigDenominator = 4; + vstHostTime.sampleRate = rate; + vstHostTime.samplePos = 0; + vstHostTime.flags = kVstNanosValid | kVstAutomationWriting | kVstAutomationReading; - bool hasEditor() const { return effect != nullptr && (effect->flags & effFlagsHasEditor) != 0; } - AudioProcessorEditor* createEditor(); + initialise (rate, samplesPerBlockExpected); - const String getInputChannelName (int index) const; - bool isInputChannelStereoPair (int index) const; + if (initialised) + { + wantsMidiMessages = wantsMidiMessages + || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0); - const String getOutputChannelName (int index) const; - bool isOutputChannelStereoPair (int index) const; + if (wantsMidiMessages) + midiEventsToSend.ensureSize (256); + else + midiEventsToSend.freeEvents(); + + incomingMidi.clear(); + + dispatch (effSetSampleRate, 0, 0, 0, (float) rate); + dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); + + tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); + + if (! isPowerOn) + setPower (true); + + // dodgy hack to force some plugins to initialise the sample rate.. + if ((! hasEditor()) && getNumParameters() > 0) + { + const float old = getParameter (0); + setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); + setParameter (0, old); + } + + dispatch (effStartProcess, 0, 0, 0, 0); + + setLatencySamples (effect->initialDelay); + } + } + + void releaseResources() override + { + if (initialised) + { + dispatch (effStopProcess, 0, 0, 0, 0); + setPower (false); + } + + tempBuffer.setSize (1, 1); + incomingMidi.clear(); + + midiEventsToSend.freeEvents(); + } + + void reset() override + { + if (isPowerOn) + { + setPower (false); + setPower (true); + } + } + + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override + { + const int numSamples = buffer.getNumSamples(); + + if (initialised) + { + if (AudioPlayHead* const playHead = getPlayHead()) + { + AudioPlayHead::CurrentPositionInfo position; + playHead->getCurrentPosition (position); + + vstHostTime.samplePos = (double) position.timeInSamples; + vstHostTime.tempo = position.bpm; + vstHostTime.timeSigNumerator = position.timeSigNumerator; + vstHostTime.timeSigDenominator = position.timeSigDenominator; + vstHostTime.ppqPos = position.ppqPosition; + vstHostTime.barStartPos = position.ppqPositionOfLastBarStart; + vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid; + + VstInt32 newTransportFlags = 0; + if (position.isPlaying) newTransportFlags |= kVstTransportPlaying; + if (position.isRecording) newTransportFlags |= kVstTransportRecording; + + if (newTransportFlags != (vstHostTime.flags & (kVstTransportPlaying | kVstTransportRecording))) + vstHostTime.flags = (vstHostTime.flags & ~(kVstTransportPlaying | kVstTransportRecording)) | newTransportFlags | kVstTransportChanged; + else + vstHostTime.flags &= ~kVstTransportChanged; + + switch (position.frameRate) + { + case AudioPlayHead::fps24: setHostTimeFrameRate (0, 24.0, position.timeInSeconds); break; + case AudioPlayHead::fps25: setHostTimeFrameRate (1, 25.0, position.timeInSeconds); break; + case AudioPlayHead::fps2997: setHostTimeFrameRate (2, 29.97, position.timeInSeconds); break; + case AudioPlayHead::fps30: setHostTimeFrameRate (3, 30.0, position.timeInSeconds); break; + case AudioPlayHead::fps2997drop: setHostTimeFrameRate (4, 29.97, position.timeInSeconds); break; + case AudioPlayHead::fps30drop: setHostTimeFrameRate (5, 29.97, position.timeInSeconds); break; + default: break; + } + + if (position.isLooping) + { + vstHostTime.cycleStartPos = position.ppqLoopStart; + vstHostTime.cycleEndPos = position.ppqLoopEnd; + vstHostTime.flags |= kVstCyclePosValid; + } + else + { + vstHostTime.flags &= ~kVstCyclePosValid; + } + } + + vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds(); + + if (wantsMidiMessages) + { + midiEventsToSend.clear(); + midiEventsToSend.ensureSize (1); + + MidiBuffer::Iterator iter (midiMessages); + const uint8* midiData; + int numBytesOfMidiData, samplePosition; + + while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition)) + { + midiEventsToSend.addEvent (midiData, numBytesOfMidiData, + jlimit (0, numSamples - 1, samplePosition)); + } + + effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend.events, 0); + } + + _clearfp(); + + if ((effect->flags & effFlagsCanReplacing) != 0) + { + effect->processReplacing (effect, buffer.getArrayOfWritePointers(), buffer.getArrayOfWritePointers(), numSamples); + } + else + { + tempBuffer.setSize (effect->numOutputs, numSamples); + tempBuffer.clear(); + + effect->process (effect, buffer.getArrayOfWritePointers(), tempBuffer.getArrayOfWritePointers(), numSamples); + + for (int i = effect->numOutputs; --i >= 0;) + buffer.copyFrom (i, 0, tempBuffer.getReadPointer (i), numSamples); + } + } + else + { + // Not initialised, so just bypass.. + for (int i = 0; i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + } + + { + // copy any incoming midi.. + const ScopedLock sl (midiInLock); + + midiMessages.swapWith (incomingMidi); + incomingMidi.clear(); + } + } //============================================================================== - int getNumParameters() { return effect != nullptr ? effect->numParams : 0; } - float getParameter (int index); - void setParameter (int index, float newValue); - const String getParameterName (int index); - const String getParameterText (int index); - bool isParameterAutomatable (int index) const; + bool hasEditor() const override { return effect != nullptr && (effect->flags & effFlagsHasEditor) != 0; } + AudioProcessorEditor* createEditor() override; //============================================================================== - int getNumPrograms() { return effect != nullptr ? effect->numPrograms : 0; } - int getCurrentProgram() { return dispatch (effGetProgram, 0, 0, 0, 0); } - void setCurrentProgram (int index); - const String getProgramName (int index); - void changeProgramName (int index, const String& newName); + const String getInputChannelName (int index) const override + { + if (index >= 0 && index < getNumInputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; + } + + bool isInputChannelStereoPair (int index) const override + { + if (index < 0 || index >= getNumInputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; + } + + const String getOutputChannelName (int index) const override + { + if (index >= 0 && index < getNumOutputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; + } + + bool isOutputChannelStereoPair (int index) const override + { + if (index < 0 || index >= getNumOutputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; + } + + bool isValidChannel (int index, bool isInput) const + { + return isInput ? (index < getNumInputChannels()) + : (index < getNumOutputChannels()); + } //============================================================================== - void getStateInformation (MemoryBlock& destData); - void getCurrentProgramStateInformation (MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + int getNumParameters() { return effect != nullptr ? effect->numParams : 0; } + + float getParameter (int index) override + { + if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams)) + { + const ScopedLock sl (lock); + return effect->getParameter (effect, index); + } + + return 0.0f; + } + + void setParameter (int index, float newValue) override + { + if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams)) + { + const ScopedLock sl (lock); + + if (effect->getParameter (effect, index) != newValue) + effect->setParameter (effect, index, newValue); + } + } + + const String getParameterName (int index) override { return getTextForOpcode (index, effGetParamName); } + const String getParameterText (int index) override { return getTextForOpcode (index, effGetParamDisplay); } + String getParameterLabel (int index) const override { return getTextForOpcode (index, effGetParamLabel); } + + bool isParameterAutomatable (int index) const override + { + if (effect != nullptr) + { + jassert (index >= 0 && index < effect->numParams); + return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0; + } + + return false; + } //============================================================================== - void timerCallback(); - void handleAsyncUpdate(); - VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt); + int getNumPrograms() override { return effect != nullptr ? jmax (0, effect->numPrograms) : 0; } + + // NB: some plugs return negative numbers from this function. + int getCurrentProgram() override { return (int) dispatch (effGetProgram, 0, 0, 0, 0); } + + void setCurrentProgram (int newIndex) override + { + if (getNumPrograms() > 0 && newIndex != getCurrentProgram()) + dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0); + } + + const String getProgramName (int index) override + { + if (index >= 0) + { + if (index == getCurrentProgram()) + return getCurrentProgramName(); + + if (effect != nullptr) + { + char nm[264] = { 0 }; + + if (dispatch (effGetProgramNameIndexed, jlimit (0, getNumPrograms(), index), -1, nm, 0) != 0) + return String (CharPointer_UTF8 (nm)).trim(); + } + } + + return programNames [index]; + } + + void changeProgramName (int index, const String& newName) override + { + if (index >= 0 && index == getCurrentProgram()) + { + if (getNumPrograms() > 0 && newName != getCurrentProgramName()) + dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toRawUTF8(), 0.0f); + } + else + { + jassertfalse; // xxx not implemented! + } + } -private: //============================================================================== - friend class VSTPluginWindow; - friend class VSTPluginFormat; + void getStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, true); } + void getCurrentProgramStateInformation (MemoryBlock& mb) override { saveToFXBFile (mb, false); } + + void setStateInformation (const void* data, int size) override { loadFromFXBFile (data, size); } + void setCurrentProgramStateInformation (const void* data, int size) override { loadFromFXBFile (data, size); } + + //============================================================================== + void timerCallback() override + { + if (dispatch (effIdle, 0, 0, 0, 0) == 0) + stopTimer(); + } + + void handleAsyncUpdate() override + { + // indicates that something about the plugin has changed.. + updateHostDisplay(); + } + + VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) + { + switch (opcode) + { + case audioMasterAutomate: sendParamChangeMessageToListeners (index, opt); break; + case audioMasterProcessEvents: handleMidiFromPlugin ((const VstEvents*) ptr); break; + + #if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4311) + #endif + case audioMasterGetTime: return (VstIntPtr) &vstHostTime; + #if JUCE_MSVC + #pragma warning (pop) + #endif + + case audioMasterIdle: + if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread()) + { + const IdleCallRecursionPreventer icrp; + + #if JUCE_MAC + if (getActiveEditor() != nullptr) + dispatch (effEditIdle, 0, 0, 0, 0); + #endif + + Timer::callPendingTimersSynchronously(); + + handleUpdateNowIfNeeded(); + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + if (ComponentPeer* p = ComponentPeer::getPeer(i)) + p->performAnyPendingRepaintsNow(); + } + break; + + case audioMasterSizeWindow: + if (AudioProcessorEditor* ed = getActiveEditor()) + ed->setSize (index, (int) value); + + return 1; + + case audioMasterUpdateDisplay: triggerAsyncUpdate(); break; + case audioMasterIOChanged: setLatencySamples (effect->initialDelay); break; + case audioMasterNeedIdle: startTimer (50); break; + + case audioMasterGetSampleRate: return (VstIntPtr) (getSampleRate() > 0 ? getSampleRate() : defaultVSTSampleRateValue); + case audioMasterGetBlockSize: return (VstIntPtr) (getBlockSize() > 0 ? getBlockSize() : defaultVSTBlockSizeValue); + case audioMasterWantMidi: wantsMidiMessages = true; break; + case audioMasterGetDirectory: return getVstDirectory(); + + case audioMasterTempoAt: + if (extraFunctions != nullptr) + return (VstIntPtr) extraFunctions->getTempoAt ((int64) value); + + break; + + case audioMasterGetAutomationState: + if (extraFunctions != nullptr) + return (VstIntPtr) extraFunctions->getAutomationState(); + + break; + + case audioMasterPinConnected: + return isValidChannel (index, value == 0) ? 0 : 1; // (yes, 0 = true) + + case audioMasterGetCurrentProcessLevel: + return isNonRealtime() ? 4 : 0; + + // none of these are handled (yet).. + case audioMasterBeginEdit: + case audioMasterEndEdit: + case audioMasterSetTime: + case audioMasterGetParameterQuantization: + case audioMasterGetInputLatency: + case audioMasterGetOutputLatency: + case audioMasterGetPreviousPlug: + case audioMasterGetNextPlug: + case audioMasterWillReplaceOrAccumulate: + case audioMasterOfflineStart: + case audioMasterOfflineRead: + case audioMasterOfflineWrite: + case audioMasterOfflineGetCurrentPass: + case audioMasterOfflineGetCurrentMetaPass: + case audioMasterVendorSpecific: + case audioMasterSetIcon: + case audioMasterGetLanguage: + case audioMasterOpenWindow: + case audioMasterCloseWindow: + break; + + default: + return handleGeneralCallback (opcode, index, value, ptr, opt); + } + + return 0; + } + + // handles non plugin-specific callbacks.. + static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 /*index*/, VstIntPtr /*value*/, void *ptr, float /*opt*/) + { + switch (opcode) + { + case audioMasterCanDo: + { + static const char* canDos[] = { "supplyIdle", + "sendVstEvents", + "sendVstMidiEvent", + "sendVstTimeInfo", + "receiveVstEvents", + "receiveVstMidiEvent", + "supportShell", + "shellCategory" }; + + for (int i = 0; i < numElementsInArray (canDos); ++i) + if (strcmp (canDos[i], (const char*) ptr) == 0) + return 1; + + return 0; + } + + case audioMasterVersion: return 2400; + case audioMasterCurrentId: return shellUIDToCreate; + case audioMasterGetNumAutomatableParameters: return 0; + case audioMasterGetAutomationState: return 1; + case audioMasterGetVendorVersion: return 0x0101; + + case audioMasterGetVendorString: + case audioMasterGetProductString: + { + String hostName ("Juce VST Host"); + + if (JUCEApplicationBase* app = JUCEApplicationBase::getInstance()) + hostName = app->getApplicationName(); + + hostName.copyToUTF8 ((char*) ptr, (size_t) jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); + break; + } + + case audioMasterGetSampleRate: return (VstIntPtr) defaultVSTSampleRateValue; + case audioMasterGetBlockSize: return (VstIntPtr) defaultVSTBlockSizeValue; + case audioMasterSetOutputSampleRate: return 0; + + default: + DBG ("*** Unhandled VST Callback: " + String ((int) opcode)); + break; + } + + return 0; + } + + //============================================================================== + VstIntPtr dispatch (const int opcode, const int index, const VstIntPtr value, void* const ptr, float opt) const + { + VstIntPtr result = 0; + + if (effect != nullptr) + { + const ScopedLock sl (lock); + const IdleCallRecursionPreventer icrp; + + try + { + #if JUCE_MAC + const ResFileRefNum oldResFile = CurResFile(); + + if (module->resFileId != 0) + UseResFile (module->resFileId); + #endif + + result = effect->dispatcher (effect, opcode, index, value, ptr, opt); + + #if JUCE_MAC + const ResFileRefNum newResFile = CurResFile(); + if (newResFile != oldResFile) // avoid confusing the parent app's resource file with the plug-in's + { + module->resFileId = newResFile; + UseResFile (oldResFile); + } + #endif + } + catch (...) + {} + } + + return result; + } + + bool loadFromFXBFile (const void* const data, const size_t dataSize) + { + if (dataSize < 28) + return false; + + const fxSet* const set = (const fxSet*) data; + + if ((vst_swap (set->chunkMagic) != 'CcnK' && vst_swap (set->chunkMagic) != 'KncC') + || vst_swap (set->version) > fxbVersionNum) + return false; + + if (vst_swap (set->fxMagic) == 'FxBk') + { + // bank of programs + if (vst_swap (set->numPrograms) >= 0) + { + const int oldProg = getCurrentProgram(); + const int numParams = vst_swap (((const fxProgram*) (set->programs))->numParams); + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + + for (int i = 0; i < vst_swap (set->numPrograms); ++i) + { + if (i != oldProg) + { + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen); + if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize) + return false; + + if (vst_swap (set->numPrograms) > 0) + setCurrentProgram (i); + + if (! restoreProgramSettings (prog)) + return false; + } + } + + if (vst_swap (set->numPrograms) > 0) + setCurrentProgram (oldProg); + + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); + if (((const char*) prog) - ((const char*) set) >= (ssize_t) dataSize) + return false; + + if (! restoreProgramSettings (prog)) + return false; + } + } + else if (vst_swap (set->fxMagic) == 'FxCk') + { + // single program + const fxProgram* const prog = (const fxProgram*) data; + + if (vst_swap (prog->chunkMagic) != 'CcnK') + return false; + + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < vst_swap (prog->numParams); ++i) + setParameter (i, vst_swapFloat (prog->params[i])); + } + else if (vst_swap (set->fxMagic) == 'FBCh' || vst_swap (set->fxMagic) == 'hCBF') + { + // non-preset chunk + const fxChunkSet* const cset = (const fxChunkSet*) data; + + if (vst_swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, vst_swap (cset->chunkSize), false); + } + else if (vst_swap (set->fxMagic) == 'FPCh' || vst_swap (set->fxMagic) == 'hCPF') + { + // preset chunk + const fxProgramSet* const cset = (const fxProgramSet*) data; + + if (vst_swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, vst_swap (cset->chunkSize), true); + + changeProgramName (getCurrentProgram(), cset->name); + } + else + { + return false; + } + + return true; + } + + bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB = 128) + { + const int numPrograms = getNumPrograms(); + const int numParams = getNumParameters(); + + if (usesChunks()) + { + MemoryBlock chunk; + getChunkData (chunk, ! isFXB, maxSizeMB); + + if (isFXB) + { + const size_t totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxChunkSet* const set = (fxChunkSet*) dest.getData(); + set->chunkMagic = vst_swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = vst_swap ('FBCh'); + set->version = vst_swap (fxbVersionNum); + set->fxID = vst_swap (getUID()); + set->fxVersion = vst_swap (getVersionNumber()); + set->numPrograms = vst_swap (numPrograms); + set->chunkSize = vst_swap ((VstInt32) chunk.getSize()); + + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + else + { + const size_t totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxProgramSet* const set = (fxProgramSet*) dest.getData(); + set->chunkMagic = vst_swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = vst_swap ('FPCh'); + set->version = vst_swap (fxbVersionNum); + set->fxID = vst_swap (getUID()); + set->fxVersion = vst_swap (getVersionNumber()); + set->numPrograms = vst_swap (numPrograms); + set->chunkSize = vst_swap ((VstInt32) chunk.getSize()); + + getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1); + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + } + else + { + if (isFXB) + { + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); + dest.setSize (len, true); + + fxSet* const set = (fxSet*) dest.getData(); + set->chunkMagic = vst_swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = vst_swap ('FxBk'); + set->version = vst_swap (fxbVersionNum); + set->fxID = vst_swap (getUID()); + set->fxVersion = vst_swap (getVersionNumber()); + set->numPrograms = vst_swap (numPrograms); + + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); + + for (int i = 0; i < numPrograms; ++i) + { + if (i != oldProgram) + { + setCurrentProgram (i); + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen)); + } + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + else + { + const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + dest.setSize (totalLen, true); + + setParamsInProgramBlock ((fxProgram*) dest.getData()); + } + } + + return true; + } + + bool usesChunks() const noexcept { return effect != nullptr && (effect->flags & effFlagsProgramChunks) != 0; } + + bool getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const + { + if (usesChunks()) + { + void* data = nullptr; + const VstIntPtr bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); + + if (data != nullptr && bytes <= maxSizeMB * 1024 * 1024) + { + mb.setSize (bytes); + mb.copyFrom (data, 0, bytes); + + return true; + } + } + + return false; + } + + bool setChunkData (const void* data, const int size, bool isPreset) + { + if (size > 0 && usesChunks()) + { + dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f); + + if (! isPreset) + updateStoredProgramNames(); + + return true; + } + + return false; + } AEffect* effect; + ModuleHandle::Ptr module; + + ScopedPointer extraFunctions; + bool usesCocoaNSView; + +private: String name; CriticalSection lock; bool wantsMidiMessages, initialised, isPowerOn; @@ -775,331 +1678,221 @@ private: VSTMidiEventList midiEventsToSend; VstTimeInfo vstHostTime; - ReferenceCountedObjectPtr module; + //============================================================================== + void setHostTimeFrameRate (long frameRateIndex, double frameRate, double currentTime) noexcept + { + vstHostTime.flags |= kVstSmpteValid; + vstHostTime.smpteFrameRate = (VstInt32) frameRateIndex; + vstHostTime.smpteOffset = (VstInt32) (currentTime * 80.0 * frameRate + 0.5); + } + + bool restoreProgramSettings (const fxProgram* const prog) + { + if (vst_swap (prog->chunkMagic) == 'CcnK' && vst_swap (prog->fxMagic) == 'FxCk') + { + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < vst_swap (prog->numParams); ++i) + setParameter (i, vst_swapFloat (prog->params[i])); + + return true; + } + + return false; + } + + String getTextForOpcode (const int index, const AEffectOpcodes opcode) const + { + if (effect == nullptr) + return String::empty; + + jassert (index >= 0 && index < effect->numParams); + char nm [256] = { 0 }; + dispatch (opcode, index, 0, nm, 0); + return String (CharPointer_UTF8 (nm)).trim(); + } + + String getCurrentProgramName() + { + String progName; + + if (effect != nullptr) + { + { + char nm[256] = { 0 }; + dispatch (effGetProgramName, 0, 0, nm, 0); + progName = String (CharPointer_UTF8 (nm)).trim(); + } + + const int index = getCurrentProgram(); + + if (index >= 0 && programNames[index].isEmpty()) + { + while (programNames.size() < index) + programNames.add (String::empty); + + programNames.set (index, progName); + } + } + + return progName; + } + + void setParamsInProgramBlock (fxProgram* const prog) + { + const int numParams = getNumParameters(); + + prog->chunkMagic = vst_swap ('CcnK'); + prog->byteSize = 0; + prog->fxMagic = vst_swap ('FxCk'); + prog->version = vst_swap (fxbVersionNum); + prog->fxID = vst_swap (getUID()); + prog->fxVersion = vst_swap (getVersionNumber()); + prog->numParams = vst_swap (numParams); + + getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1); + + for (int i = 0; i < numParams; ++i) + prog->params[i] = vst_swapFloat (getParameter (i)); + } + + void updateStoredProgramNames() + { + if (effect != nullptr && getNumPrograms() > 0) + { + char nm[256] = { 0 }; + + // only do this if the plugin can't use indexed names.. + if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0) + { + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + for (int i = 0; i < getNumPrograms(); ++i) + { + setCurrentProgram (i); + getCurrentProgramName(); // (this updates the list) + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + } + } + + void handleMidiFromPlugin (const VstEvents* const events) + { + if (events != nullptr) + { + const ScopedLock sl (midiInLock); + VSTMidiEventList::addEventsToMidiBuffer (events, incomingMidi); + } + } //============================================================================== - int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const; - bool restoreProgramSettings (const fxProgram* const prog); - const String getCurrentProgramName(); - void setParamsInProgramBlock (fxProgram* const prog); - void updateStoredProgramNames(); - void initialise(); - void handleMidiFromPlugin (const VstEvents* const events); - void createTempParameterStore (MemoryBlock& dest); - void restoreFromTempParameterStore (const MemoryBlock& mb); - const String getParameterLabel (int index) const; - - bool usesChunks() const noexcept { return effect != nullptr && (effect->flags & effFlagsProgramChunks) != 0; } - void getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const; - void setChunkData (const char* data, int size, bool isPreset); - bool loadFromFXBFile (const void* data, int numBytes); - bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB); - - int getVersionNumber() const noexcept { return effect != nullptr ? effect->version : 0; } - String getVersion() const; - String getCategory() const; - - void setPower (const bool on); - - VSTPluginInstance (const ReferenceCountedObjectPtr & module); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance); -}; - -//============================================================================== -VSTPluginInstance::VSTPluginInstance (const ReferenceCountedObjectPtr & module_) - : effect (nullptr), - name (module_->pluginName), - wantsMidiMessages (false), - initialised (false), - isPowerOn (false), - tempBuffer (1, 1), - module (module_) -{ - try + void createTempParameterStore (MemoryBlock& dest) { - const IdleCallRecursionPreventer icrp; - _fpreset(); + dest.setSize (64 + 4 * getNumParameters()); + dest.fillWith (0); - log ("Creating VST instance: " + name); + getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); - #if JUCE_MAC - if (module->resFileId != 0) - UseResFile (module->resFileId); + float* const p = (float*) (((char*) dest.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + p[i] = getParameter(i); + } - #if JUCE_PPC - if (module->fragId != 0) - { - static void* audioMasterCoerced = nullptr; - if (audioMasterCoerced == nullptr) - audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster); + void restoreFromTempParameterStore (const MemoryBlock& m) + { + changeProgramName (getCurrentProgram(), (const char*) m.getData()); - effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); - } - else + float* p = (float*) (((char*) m.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + setParameter (i, p[i]); + } + + VstIntPtr getVstDirectory() const + { + #if JUCE_MAC + return (VstIntPtr) (void*) &module->parentDirFSSpec; + #else + return (VstIntPtr) (pointer_sized_uint) module->fullParentDirectoryPathName.toRawUTF8(); #endif - #endif - { - effect = module->moduleMain (&audioMaster); - } - - if (effect != nullptr && effect->magic == kEffectMagic) - { - #if JUCE_PPC - module->coerceAEffectFunctionCalls (effect); - #endif - - jassert (effect->resvd2 == 0); - jassert (effect->object != 0); - - _fpreset(); // some dodgy plugs fuck around with this - } - else - { - effect = nullptr; - } - } - catch (...) - {} -} - -VSTPluginInstance::~VSTPluginInstance() -{ - const ScopedLock sl (lock); - - if (effect != nullptr && effect->magic == kEffectMagic) - { - try - { - #if JUCE_MAC - if (module->resFileId != 0) - UseResFile (module->resFileId); - #endif - - // Must delete any editors before deleting the plugin instance! - jassert (getActiveEditor() == 0); - - _fpreset(); // some dodgy plugs fuck around with this - - module->closeEffect (effect); - } - catch (...) - {} } - module = nullptr; - effect = nullptr; -} + //============================================================================== + int getVersionNumber() const noexcept { return effect != nullptr ? effect->version : 0; } -//============================================================================== -void VSTPluginInstance::initialise() -{ - if (initialised || effect == 0) - return; - - log ("Initialising VST: " + module->pluginName); - initialised = true; - - dispatch (effIdentify, 0, 0, 0, 0); - - if (getSampleRate() > 0) - dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); - - if (getBlockSize() > 0) - dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); - - dispatch (effOpen, 0, 0, 0, 0); - - setPlayConfigDetails (effect->numInputs, effect->numOutputs, - getSampleRate(), getBlockSize()); - - if (getNumPrograms() > 1) - setCurrentProgram (0); - else - dispatch (effSetProgram, 0, 0, 0, 0); - - int i; - for (i = effect->numInputs; --i >= 0;) - dispatch (effConnectInput, i, 1, 0, 0); - - for (i = effect->numOutputs; --i >= 0;) - dispatch (effConnectOutput, i, 1, 0, 0); - - updateStoredProgramNames(); - - wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; - - setLatencySamples (effect->initialDelay); -} - - -//============================================================================== -void VSTPluginInstance::prepareToPlay (double sampleRate_, - int samplesPerBlockExpected) -{ - setPlayConfigDetails (effect->numInputs, effect->numOutputs, - sampleRate_, samplesPerBlockExpected); - - setLatencySamples (effect->initialDelay); - - vstHostTime.tempo = 120.0; - vstHostTime.timeSigNumerator = 4; - vstHostTime.timeSigDenominator = 4; - vstHostTime.sampleRate = sampleRate_; - vstHostTime.samplePos = 0; - vstHostTime.flags = kVstNanosValid; /*| kVstTransportPlaying | kVstTempoValid | kVstTimeSigValid*/; - - initialise(); - - if (initialised) + String getVersion() const { - wantsMidiMessages = wantsMidiMessages - || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0); + unsigned int v = (unsigned int) dispatch (effGetVendorVersion, 0, 0, 0, 0); - if (wantsMidiMessages) - midiEventsToSend.ensureSize (256); - else - midiEventsToSend.freeEvents(); + String s; - incomingMidi.clear(); + if (v == 0 || (int) v == -1) + v = getVersionNumber(); - dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); - dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); - - tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); - - if (! isPowerOn) - setPower (true); - - // dodgy hack to force some plugins to initialise the sample rate.. - if ((! hasEditor()) && getNumParameters() > 0) + if (v != 0) { - const float old = getParameter (0); - setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); - setParameter (0, old); - } + int versionBits[32]; + int n = 0; - dispatch (effStartProcess, 0, 0, 0, 0); - } -} + for (int vv = v; vv != 0; vv /= 10) + versionBits [n++] = vv % 10; -void VSTPluginInstance::releaseResources() -{ - if (initialised) - { - dispatch (effStopProcess, 0, 0, 0, 0); - setPower (false); - } - - tempBuffer.setSize (1, 1); - incomingMidi.clear(); - - midiEventsToSend.freeEvents(); -} - -void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages) -{ - const int numSamples = buffer.getNumSamples(); - - if (initialised) - { - AudioPlayHead* playHead = getPlayHead(); - - if (playHead != nullptr) - { - AudioPlayHead::CurrentPositionInfo position; - playHead->getCurrentPosition (position); - - vstHostTime.tempo = position.bpm; - vstHostTime.timeSigNumerator = position.timeSigNumerator; - vstHostTime.timeSigDenominator = position.timeSigDenominator; - vstHostTime.ppqPos = position.ppqPosition; - vstHostTime.barStartPos = position.ppqPositionOfLastBarStart; - vstHostTime.flags |= kVstTempoValid | kVstTimeSigValid | kVstPpqPosValid | kVstBarsValid; - - if (position.isPlaying) - vstHostTime.flags |= kVstTransportPlaying; - else - vstHostTime.flags &= ~kVstTransportPlaying; - } - - vstHostTime.nanoSeconds = getVSTHostTimeNanoseconds(); - - if (wantsMidiMessages) - { - midiEventsToSend.clear(); - midiEventsToSend.ensureSize (1); - - MidiBuffer::Iterator iter (midiMessages); - const uint8* midiData; - int numBytesOfMidiData, samplePosition; - - while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition)) + if (n > 4) // if the number ends up silly, it's probably encoded as hex instead of decimal.. { - midiEventsToSend.addEvent (midiData, numBytesOfMidiData, - jlimit (0, numSamples - 1, samplePosition)); + n = 0; + + for (int vv = v; vv != 0; vv >>= 8) + versionBits [n++] = vv & 255; } - try + while (n > 1 && versionBits [n - 1] == 0) + --n; + + s << 'V'; + + while (n > 0) { - effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend.events, 0); + s << versionBits [--n]; + + if (n > 0) + s << '.'; } - catch (...) - {} } - _clearfp(); + return s; + } - if ((effect->flags & effFlagsCanReplacing) != 0) + const char* getCategory() const + { + switch (getVstCategory()) { - try - { - effect->processReplacing (effect, buffer.getArrayOfChannels(), buffer.getArrayOfChannels(), numSamples); - } - catch (...) - {} + case kPlugCategEffect: return "Effect"; + case kPlugCategSynth: return "Synth"; + case kPlugCategAnalysis: return "Analysis"; + case kPlugCategMastering: return "Mastering"; + case kPlugCategSpacializer: return "Spacial"; + case kPlugCategRoomFx: return "Reverb"; + case kPlugSurroundFx: return "Surround"; + case kPlugCategRestoration: return "Restoration"; + case kPlugCategGenerator: return "Tone generation"; + default: break; } - else - { - tempBuffer.setSize (effect->numOutputs, numSamples); - tempBuffer.clear(); - try - { - effect->process (effect, buffer.getArrayOfChannels(), tempBuffer.getArrayOfChannels(), numSamples); - } - catch (...) - {} - - for (int i = effect->numOutputs; --i >= 0;) - buffer.copyFrom (i, 0, tempBuffer.getSampleData (i), numSamples); - } + return nullptr; } - else + + void setPower (const bool on) { - // Not initialised, so just bypass.. - for (int i = 0; i < getNumOutputChannels(); ++i) - buffer.clear (i, 0, buffer.getNumSamples()); + dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0); + isPowerOn = on; } - { - // copy any incoming midi.. - const ScopedLock sl (midiInLock); - - midiMessages.swapWith (incomingMidi); - incomingMidi.clear(); - } -} - -//============================================================================== -void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events) -{ - if (events != nullptr) - { - const ScopedLock sl (midiInLock); - VSTMidiEventList::addEventsToMidiBuffer (events, incomingMidi); - } -} + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginInstance) +}; //============================================================================== static Array activeVSTWindows; @@ -1112,13 +1905,12 @@ class VSTPluginWindow : public AudioProcessorEditor, public Timer { public: - //============================================================================== - VSTPluginWindow (VSTPluginInstance& plugin_) - : AudioProcessorEditor (&plugin_), + VSTPluginWindow (VSTPluginInstance& plug) + : AudioProcessorEditor (&plug), #if ! JUCE_MAC ComponentMovementWatcher (this), #endif - plugin (plugin_), + plugin (plug), isOpen (false), recursiveResize (false), pluginWantsKeys (false), @@ -1126,13 +1918,20 @@ public: alreadyInside (false) { #if JUCE_WINDOWS - sizeCheckCount = 0; pluginHWND = 0; + sizeCheckCount = 0; + #elif JUCE_LINUX pluginWindow = None; pluginProc = None; - #else - addAndMakeVisible (innerWrapper = new InnerWrapperComponent (*this)); + + #elif JUCE_MAC + #if JUCE_SUPPORT_CARBON + if (! plug.usesCocoaNSView) + addAndMakeVisible (carbonWrapper = new CarbonWrapperComponent (*this)); + else + #endif + addAndMakeVisible (cocoaWrapper = new AutoResizingNSViewComponentWithParent()); #endif activeVSTWindows.add (this); @@ -1144,19 +1943,22 @@ public: ~VSTPluginWindow() { - #if JUCE_MAC - innerWrapper = nullptr; - #else closePluginWindow(); + + #if JUCE_MAC + #if JUCE_SUPPORT_CARBON + carbonWrapper = nullptr; + #endif + cocoaWrapper = nullptr; #endif - activeVSTWindows.removeValue (this); + activeVSTWindows.removeFirstMatchingValue (this); plugin.editorBeingDeleted (this); } //============================================================================== -#if ! JUCE_MAC - void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + #if ! JUCE_MAC + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override { if (recursiveResize) return; @@ -1185,91 +1987,101 @@ public: } } - void componentVisibilityChanged() + void componentVisibilityChanged() override { if (isShowing()) openPluginWindow(); - else + else if (! shouldAvoidDeletingWindow()) closePluginWindow(); componentMovedOrResized (true, true); } - void componentPeerChanged() + void componentPeerChanged() override { closePluginWindow(); openPluginWindow(); } -#endif + #endif - //============================================================================== - bool keyStateChanged (bool) + #if JUCE_MAC + void visibilityChanged() override { - return pluginWantsKeys; + if (cocoaWrapper != nullptr) + { + if (isVisible()) + openPluginWindow ((NSView*) cocoaWrapper->getView()); + else + closePluginWindow(); + } } - bool keyPressed (const KeyPress&) + void childBoundsChanged (Component*) override { - return pluginWantsKeys; + if (cocoaWrapper != nullptr) + { + int w = cocoaWrapper->getWidth(); + int h = cocoaWrapper->getHeight(); + + if (w != getWidth() || h != getHeight()) + setSize (w, h); + } } + #endif //============================================================================== -#if JUCE_MAC - void paint (Graphics& g) + bool keyStateChanged (bool) override { return pluginWantsKeys; } + bool keyPressed (const juce::KeyPress&) override { return pluginWantsKeys; } + + //============================================================================== + #if JUCE_MAC + void paint (Graphics& g) override { g.fillAll (Colours::black); } -#else - void paint (Graphics& g) + #else + void paint (Graphics& g) override { if (isOpen) { - ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) + #if JUCE_LINUX + if (pluginWindow != 0) { - peer->addMaskedRegion (getScreenBounds() - peer->getScreenPosition()); + const Rectangle clip (g.getClipBounds()); - #if JUCE_LINUX - if (pluginWindow != 0) - { - const Rectangle clip (g.getClipBounds()); + XEvent ev = { 0 }; + ev.xexpose.type = Expose; + ev.xexpose.display = display; + ev.xexpose.window = pluginWindow; + ev.xexpose.x = clip.getX(); + ev.xexpose.y = clip.getY(); + ev.xexpose.width = clip.getWidth(); + ev.xexpose.height = clip.getHeight(); - XEvent ev = { 0 }; - ev.xexpose.type = Expose; - ev.xexpose.display = display; - ev.xexpose.window = pluginWindow; - ev.xexpose.x = clip.getX(); - ev.xexpose.y = clip.getY(); - ev.xexpose.width = clip.getWidth(); - ev.xexpose.height = clip.getHeight(); - - sendEventToChild (&ev); - } - #endif + sendEventToChild (ev); } + #endif } else { g.fillAll (Colours::black); } } -#endif + #endif //============================================================================== - void timerCallback() + void timerCallback() override { - #if JUCE_WINDOWS - if (--sizeCheckCount <= 0) + if (isShowing()) { - sizeCheckCount = 10; + #if JUCE_WINDOWS + if (--sizeCheckCount <= 0) + { + sizeCheckCount = 10; + checkPluginWindowSize(); + } + #endif - checkPluginWindowSize(); - } - #endif - - try - { static bool reentrant = false; if (! reentrant) @@ -1279,44 +2091,33 @@ public: reentrant = false; } } - catch (...) - {} } //============================================================================== - void mouseDown (const MouseEvent& e) + void mouseDown (const MouseEvent& e) override { + (void) e; + #if JUCE_LINUX if (pluginWindow == 0) return; toFront (true); - XEvent ev = { 0 }; - ev.xbutton.display = display; + XEvent ev; + prepareXEvent (ev, e); ev.xbutton.type = ButtonPress; - ev.xbutton.window = pluginWindow; - ev.xbutton.root = RootWindow (display, DefaultScreen (display)); - ev.xbutton.time = CurrentTime; - ev.xbutton.x = e.x; - ev.xbutton.y = e.y; - ev.xbutton.x_root = e.getScreenX(); - ev.xbutton.y_root = e.getScreenY(); - translateJuceToXButtonModifiers (e, ev); - - sendEventToChild (&ev); + sendEventToChild (ev); #elif JUCE_WINDOWS - (void) e; - toFront (true); #endif } - void broughtToFront() + void broughtToFront() override { - activeVSTWindows.removeValue (this); + activeVSTWindows.removeFirstMatchingValue (this); activeVSTWindows.add (this); #if JUCE_MAC @@ -1339,9 +2140,24 @@ private: EventProcPtr pluginProc; #endif + // This is a workaround for old Mackie plugins that crash if their + // window is deleted more than once. + bool shouldAvoidDeletingWindow() const + { + return plugin.getPluginDescription() + .manufacturerName.containsIgnoreCase ("Loud Technologies"); + } + + // This is an old workaround for some plugins that need a repaint when their + // windows are first created, but it breaks some Izotope plugins.. + bool shouldRepaintCarbonWindowWhenCreated() + { + return ! plugin.getName().containsIgnoreCase ("izotope"); + } + //============================================================================== #if JUCE_MAC - void openPluginWindow (WindowRef parentWindow) + void openPluginWindow (void* parentWindow) { if (isOpen || parentWindow == 0) return; @@ -1389,7 +2205,7 @@ private: if (isOpen || getWindowHandle() == 0) return; - log ("Opening VST UI: " + plugin.name); + JUCE_VST_LOG ("Opening VST UI: " + plugin.getName()); isOpen = true; ERect* rect = nullptr; @@ -1424,11 +2240,10 @@ private: #pragma warning (pop) - int w, h; RECT r; GetWindowRect (pluginHWND, &r); - w = r.right - r.left; - h = r.bottom - r.top; + int w = r.right - r.left; + int h = r.bottom - r.top; if (rect != nullptr) { @@ -1499,42 +2314,32 @@ private: #endif //============================================================================== -#if ! JUCE_MAC void closePluginWindow() { if (isOpen) { - log ("Closing VST UI: " + plugin.getName()); + JUCE_VST_LOG ("Closing VST UI: " + plugin.getName()); isOpen = false; - dispatch (effEditClose, 0, 0, 0, 0); + stopTimer(); #if JUCE_WINDOWS #pragma warning (push) #pragma warning (disable: 4244) - if (pluginHWND != 0 && IsWindow (pluginHWND)) SetWindowLongPtr (pluginHWND, GWLP_WNDPROC, (LONG_PTR) originalWndProc); - #pragma warning (pop) - stopTimer(); - - if (pluginHWND != 0 && IsWindow (pluginHWND)) - DestroyWindow (pluginHWND); - pluginHWND = 0; #elif JUCE_LINUX - stopTimer(); pluginWindow = 0; pluginProc = 0; #endif } } -#endif //============================================================================== - int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) + VstIntPtr dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) { return plugin.dispatch (opcode, index, value, ptr, opt); } @@ -1562,9 +2367,9 @@ private: { for (int i = activeVSTWindows.size(); --i >= 0;) { - const VSTPluginWindow* const w = activeVSTWindows.getUnchecked (i); + Component::SafePointer w (activeVSTWindows[i]); - if (w->pluginHWND == hW) + if (w != nullptr && w->pluginHWND == hW) { if (message == WM_CHAR || message == WM_KEYDOWN @@ -1577,11 +2382,10 @@ private: message, wParam, lParam); } - return CallWindowProc ((WNDPROC) (w->originalWndProc), - (HWND) w->pluginHWND, - message, - wParam, - lParam); + if (w != nullptr) // (may have been deleted in SendMessage callback) + return CallWindowProc ((WNDPROC) w->originalWndProc, + (HWND) w->pluginHWND, + message, wParam, lParam); } } @@ -1592,185 +2396,142 @@ private: #if JUCE_LINUX //============================================================================== // overload mouse/keyboard events to forward them to the plugin's inner window.. - void sendEventToChild (XEvent* event) + void sendEventToChild (XEvent& event) { if (pluginProc != 0) { // if the plugin publishes an event procedure, pass the event directly.. - pluginProc (event); + pluginProc (&event); } else if (pluginWindow != 0) { // if the plugin has a window, then send the event to the window so that // its message thread will pick it up.. - XSendEvent (display, pluginWindow, False, 0L, event); + XSendEvent (display, pluginWindow, False, NoEventMask, &event); XFlush (display); } } - void mouseEnter (const MouseEvent& e) + void prepareXEvent (XEvent& ev, const MouseEvent& e) const noexcept + { + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + } + + void mouseEnter (const MouseEvent& e) override { if (pluginWindow != 0) { - XEvent ev = { 0 }; - ev.xcrossing.display = display; + XEvent ev; + prepareXEvent (ev, e); ev.xcrossing.type = EnterNotify; - ev.xcrossing.window = pluginWindow; - ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); - ev.xcrossing.time = CurrentTime; - ev.xcrossing.x = e.x; - ev.xcrossing.y = e.y; - ev.xcrossing.x_root = e.getScreenX(); - ev.xcrossing.y_root = e.getScreenY(); - ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab - ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual - + ev.xcrossing.mode = NotifyNormal; + ev.xcrossing.detail = NotifyAncestor; translateJuceToXCrossingModifiers (e, ev); - - sendEventToChild (&ev); + sendEventToChild (ev); } } - void mouseExit (const MouseEvent& e) + void mouseExit (const MouseEvent& e) override { if (pluginWindow != 0) { - XEvent ev = { 0 }; - ev.xcrossing.display = display; + XEvent ev; + prepareXEvent (ev, e); ev.xcrossing.type = LeaveNotify; - ev.xcrossing.window = pluginWindow; - ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); - ev.xcrossing.time = CurrentTime; - ev.xcrossing.x = e.x; - ev.xcrossing.y = e.y; - ev.xcrossing.x_root = e.getScreenX(); - ev.xcrossing.y_root = e.getScreenY(); - ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab - ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual - ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ? - + ev.xcrossing.mode = NotifyNormal; + ev.xcrossing.detail = NotifyAncestor; + ev.xcrossing.focus = hasKeyboardFocus (true); translateJuceToXCrossingModifiers (e, ev); - - sendEventToChild (&ev); + sendEventToChild (ev); } } - void mouseMove (const MouseEvent& e) + void mouseMove (const MouseEvent& e) override { if (pluginWindow != 0) { - XEvent ev = { 0 }; - ev.xmotion.display = display; + XEvent ev; + prepareXEvent (ev, e); ev.xmotion.type = MotionNotify; - ev.xmotion.window = pluginWindow; - ev.xmotion.root = RootWindow (display, DefaultScreen (display)); - ev.xmotion.time = CurrentTime; ev.xmotion.is_hint = NotifyNormal; - ev.xmotion.x = e.x; - ev.xmotion.y = e.y; - ev.xmotion.x_root = e.getScreenX(); - ev.xmotion.y_root = e.getScreenY(); - - sendEventToChild (&ev); + sendEventToChild (ev); } } - void mouseDrag (const MouseEvent& e) + void mouseDrag (const MouseEvent& e) override { if (pluginWindow != 0) { - XEvent ev = { 0 }; - ev.xmotion.display = display; + XEvent ev; + prepareXEvent (ev, e); ev.xmotion.type = MotionNotify; - ev.xmotion.window = pluginWindow; - ev.xmotion.root = RootWindow (display, DefaultScreen (display)); - ev.xmotion.time = CurrentTime; - ev.xmotion.x = e.x ; - ev.xmotion.y = e.y; - ev.xmotion.x_root = e.getScreenX(); - ev.xmotion.y_root = e.getScreenY(); ev.xmotion.is_hint = NotifyNormal; - translateJuceToXMotionModifiers (e, ev); - sendEventToChild (&ev); + sendEventToChild (ev); } } - void mouseUp (const MouseEvent& e) + void mouseUp (const MouseEvent& e) override { if (pluginWindow != 0) { - XEvent ev = { 0 }; - ev.xbutton.display = display; + XEvent ev; + prepareXEvent (ev, e); ev.xbutton.type = ButtonRelease; - ev.xbutton.window = pluginWindow; - ev.xbutton.root = RootWindow (display, DefaultScreen (display)); - ev.xbutton.time = CurrentTime; - ev.xbutton.x = e.x; - ev.xbutton.y = e.y; - ev.xbutton.x_root = e.getScreenX(); - ev.xbutton.y_root = e.getScreenY(); - translateJuceToXButtonModifiers (e, ev); - sendEventToChild (&ev); + sendEventToChild (ev); } } - void mouseWheelMove (const MouseEvent& e, - float incrementX, - float incrementY) + void mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel) override { if (pluginWindow != 0) { - XEvent ev = { 0 }; - ev.xbutton.display = display; + XEvent ev; + prepareXEvent (ev, e); ev.xbutton.type = ButtonPress; - ev.xbutton.window = pluginWindow; - ev.xbutton.root = RootWindow (display, DefaultScreen (display)); - ev.xbutton.time = CurrentTime; - ev.xbutton.x = e.x; - ev.xbutton.y = e.y; - ev.xbutton.x_root = e.getScreenX(); - ev.xbutton.y_root = e.getScreenY(); - - translateJuceToXMouseWheelModifiers (e, incrementY, ev); - sendEventToChild (&ev); - - // TODO - put a usleep here ? + translateJuceToXMouseWheelModifiers (e, wheel.deltaY, ev); + sendEventToChild (ev); ev.xbutton.type = ButtonRelease; - sendEventToChild (&ev); + sendEventToChild (ev); } } #endif -#if JUCE_MAC //============================================================================== - #if ! JUCE_SUPPORT_CARBON - #error "To build VSTs, you need to enable the JUCE_SUPPORT_CARBON flag in your config!" - #endif - - class InnerWrapperComponent : public CarbonViewWrapperComponent +#if JUCE_MAC + #if JUCE_SUPPORT_CARBON + class CarbonWrapperComponent : public CarbonViewWrapperComponent { public: - InnerWrapperComponent (VSTPluginWindow& owner_) - : owner (owner_), - alreadyInside (false) + CarbonWrapperComponent (VSTPluginWindow& w) + : owner (w), alreadyInside (false) { + keepPluginWindowWhenHidden = w.shouldAvoidDeletingWindow(); + setRepaintsChildHIViewWhenCreated (w.shouldRepaintCarbonWindowWhenCreated()); } - ~InnerWrapperComponent() + ~CarbonWrapperComponent() { deleteWindow(); } - HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) + HIViewRef attachView (WindowRef windowRef, HIViewRef /*rootView*/) override { owner.openPluginWindow (windowRef); return 0; } - void removeView (HIViewRef) + void removeView (HIViewRef) override { if (owner.isOpen) { @@ -1780,7 +2541,7 @@ private: } } - bool getEmbeddedViewSize (int& w, int& h) + bool getEmbeddedViewSize (int& w, int& h) override { ERect* rect = nullptr; owner.dispatch (effEditGetRect, 0, 0, &rect, 0); @@ -1789,7 +2550,7 @@ private: return true; } - void mouseDown (int x, int y) + void handleMouseDown (int x, int y) override { if (! alreadyInside) { @@ -1804,18 +2565,16 @@ private: } } - void paint() + void handlePaint() override { - ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) + if (ComponentPeer* const peer = getPeer()) { - const Point pos (getScreenPosition() - peer->getScreenPosition()); + const Point pos (peer->globalToLocal (getScreenPosition())); ERect r; - r.left = pos.getX(); - r.right = r.left + getWidth(); - r.top = pos.getY(); - r.bottom = r.top + getHeight(); + r.left = (VstInt16) pos.getX(); + r.top = (VstInt16) pos.getY(); + r.right = (VstInt16) (r.left + getWidth()); + r.bottom = (VstInt16) (r.top + getHeight()); owner.dispatch (effEditDraw, 0, 0, &r, 0); } @@ -1825,900 +2584,74 @@ private: VSTPluginWindow& owner; bool alreadyInside; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InnerWrapperComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CarbonWrapperComponent) }; - friend class InnerWrapperComponent; - ScopedPointer innerWrapper; + friend class CarbonWrapperComponent; + ScopedPointer carbonWrapper; + #endif - void resized() + ScopedPointer cocoaWrapper; + + void resized() override { - if (innerWrapper != nullptr) - innerWrapper->setSize (getWidth(), getHeight()); + #if JUCE_SUPPORT_CARBON + if (carbonWrapper != nullptr) + carbonWrapper->setSize (getWidth(), getHeight()); + #endif + + if (cocoaWrapper != nullptr) + cocoaWrapper->setSize (getWidth(), getHeight()); } #endif -private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginWindow); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginWindow) }; //============================================================================== AudioProcessorEditor* VSTPluginInstance::createEditor() { - if (hasEditor()) - return new VSTPluginWindow (*this); + return hasEditor() ? new VSTPluginWindow (*this) + : nullptr; +} + +//============================================================================== +// entry point for all callbacks from the plugin +static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) +{ + if (effect != nullptr) + if (VSTPluginInstance* instance = (VSTPluginInstance*) (effect->resvd2)) + return instance->handleCallback (opcode, index, value, ptr, opt); + + return VSTPluginInstance::handleGeneralCallback (opcode, index, value, ptr, opt); +} + +//============================================================================== +VSTPluginFormat::VSTPluginFormat() {} +VSTPluginFormat::~VSTPluginFormat() {} + +static VSTPluginInstance* createAndUpdateDesc (VSTPluginFormat& format, PluginDescription& desc) +{ + if (AudioPluginInstance* p = format.createInstanceFromDescription (desc, 44100.0, 512)) + { + if (VSTPluginInstance* instance = dynamic_cast (p)) + { + #if JUCE_MAC + if (instance->module->resFileId != 0) + UseResFile (instance->module->resFileId); + #endif + + instance->fillInPluginDescription (desc); + return instance; + } + + jassertfalse; + } return nullptr; } - -//============================================================================== -void VSTPluginInstance::handleAsyncUpdate() -{ - // indicates that something about the plugin has changed.. - updateHostDisplay(); -} - -//============================================================================== -bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog) -{ - if (vst_swap (prog->chunkMagic) == 'CcnK' && vst_swap (prog->fxMagic) == 'FxCk') - { - changeProgramName (getCurrentProgram(), prog->prgName); - - for (int i = 0; i < vst_swap (prog->numParams); ++i) - setParameter (i, vst_swapFloat (prog->params[i])); - - return true; - } - - return false; -} - -bool VSTPluginInstance::loadFromFXBFile (const void* const data, - const int dataSize) -{ - if (dataSize < 28) - return false; - - const fxSet* const set = (const fxSet*) data; - - if ((vst_swap (set->chunkMagic) != 'CcnK' && vst_swap (set->chunkMagic) != 'KncC') - || vst_swap (set->version) > fxbVersionNum) - return false; - - if (vst_swap (set->fxMagic) == 'FxBk') - { - // bank of programs - if (vst_swap (set->numPrograms) >= 0) - { - const int oldProg = getCurrentProgram(); - const int numParams = vst_swap (((const fxProgram*) (set->programs))->numParams); - const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - - for (int i = 0; i < vst_swap (set->numPrograms); ++i) - { - if (i != oldProg) - { - const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen); - if (((const char*) prog) - ((const char*) set) >= dataSize) - return false; - - if (vst_swap (set->numPrograms) > 0) - setCurrentProgram (i); - - if (! restoreProgramSettings (prog)) - return false; - } - } - - if (vst_swap (set->numPrograms) > 0) - setCurrentProgram (oldProg); - - const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); - if (((const char*) prog) - ((const char*) set) >= dataSize) - return false; - - if (! restoreProgramSettings (prog)) - return false; - } - } - else if (vst_swap (set->fxMagic) == 'FxCk') - { - // single program - const fxProgram* const prog = (const fxProgram*) data; - - if (vst_swap (prog->chunkMagic) != 'CcnK') - return false; - - changeProgramName (getCurrentProgram(), prog->prgName); - - for (int i = 0; i < vst_swap (prog->numParams); ++i) - setParameter (i, vst_swapFloat (prog->params[i])); - } - else if (vst_swap (set->fxMagic) == 'FBCh' || vst_swap (set->fxMagic) == 'hCBF') - { - // non-preset chunk - const fxChunkSet* const cset = (const fxChunkSet*) data; - - if (vst_swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) - return false; - - setChunkData (cset->chunk, vst_swap (cset->chunkSize), false); - } - else if (vst_swap (set->fxMagic) == 'FPCh' || vst_swap (set->fxMagic) == 'hCPF') - { - // preset chunk - const fxProgramSet* const cset = (const fxProgramSet*) data; - - if (vst_swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) - return false; - - setChunkData (cset->chunk, vst_swap (cset->chunkSize), true); - - changeProgramName (getCurrentProgram(), cset->name); - } - else - { - return false; - } - - return true; -} - -//============================================================================== -void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog) -{ - const int numParams = getNumParameters(); - - prog->chunkMagic = vst_swap ('CcnK'); - prog->byteSize = 0; - prog->fxMagic = vst_swap ('FxCk'); - prog->version = vst_swap (fxbVersionNum); - prog->fxID = vst_swap (getUID()); - prog->fxVersion = vst_swap (getVersionNumber()); - prog->numParams = vst_swap (numParams); - - getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1); - - for (int i = 0; i < numParams; ++i) - prog->params[i] = vst_swapFloat (getParameter (i)); -} - -bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB) -{ - const int numPrograms = getNumPrograms(); - const int numParams = getNumParameters(); - - if (usesChunks()) - { - MemoryBlock chunk; - getChunkData (chunk, ! isFXB, maxSizeMB); - - if (isFXB) - { - const size_t totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8; - dest.setSize (totalLen, true); - - fxChunkSet* const set = (fxChunkSet*) dest.getData(); - set->chunkMagic = vst_swap ('CcnK'); - set->byteSize = 0; - set->fxMagic = vst_swap ('FBCh'); - set->version = vst_swap (fxbVersionNum); - set->fxID = vst_swap (getUID()); - set->fxVersion = vst_swap (getVersionNumber()); - set->numPrograms = vst_swap (numPrograms); - set->chunkSize = vst_swap ((long) chunk.getSize()); - - chunk.copyTo (set->chunk, 0, chunk.getSize()); - } - else - { - const size_t totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8; - dest.setSize (totalLen, true); - - fxProgramSet* const set = (fxProgramSet*) dest.getData(); - set->chunkMagic = vst_swap ('CcnK'); - set->byteSize = 0; - set->fxMagic = vst_swap ('FPCh'); - set->version = vst_swap (fxbVersionNum); - set->fxID = vst_swap (getUID()); - set->fxVersion = vst_swap (getVersionNumber()); - set->numPrograms = vst_swap (numPrograms); - set->chunkSize = vst_swap ((long) chunk.getSize()); - - getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1); - chunk.copyTo (set->chunk, 0, chunk.getSize()); - } - } - else - { - if (isFXB) - { - const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); - dest.setSize (len, true); - - fxSet* const set = (fxSet*) dest.getData(); - set->chunkMagic = vst_swap ('CcnK'); - set->byteSize = 0; - set->fxMagic = vst_swap ('FxBk'); - set->version = vst_swap (fxbVersionNum); - set->fxID = vst_swap (getUID()); - set->fxVersion = vst_swap (getVersionNumber()); - set->numPrograms = vst_swap (numPrograms); - - const int oldProgram = getCurrentProgram(); - MemoryBlock oldSettings; - createTempParameterStore (oldSettings); - - setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); - - for (int i = 0; i < numPrograms; ++i) - { - if (i != oldProgram) - { - setCurrentProgram (i); - setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen)); - } - } - - setCurrentProgram (oldProgram); - restoreFromTempParameterStore (oldSettings); - } - else - { - const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - dest.setSize (totalLen, true); - - setParamsInProgramBlock ((fxProgram*) dest.getData()); - } - } - - return true; -} - -void VSTPluginInstance::getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const -{ - if (usesChunks()) - { - void* data = nullptr; - const int bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); - - if (data != nullptr && bytes <= maxSizeMB * 1024 * 1024) - { - mb.setSize (bytes); - mb.copyFrom (data, 0, bytes); - } - } -} - -void VSTPluginInstance::setChunkData (const char* data, int size, bool isPreset) -{ - if (size > 0 && usesChunks()) - { - dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f); - - if (! isPreset) - updateStoredProgramNames(); - } -} - -//============================================================================== -void VSTPluginInstance::timerCallback() -{ - if (dispatch (effIdle, 0, 0, 0, 0) == 0) - stopTimer(); -} - -int VSTPluginInstance::dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const -{ - int result = 0; - - if (effect != nullptr) - { - const ScopedLock sl (lock); - const IdleCallRecursionPreventer icrp; - - try - { - #if JUCE_MAC - const int oldResFile = CurResFile(); - - if (module->resFileId != 0) - UseResFile (module->resFileId); - #endif - - result = effect->dispatcher (effect, opcode, index, value, ptr, opt); - - #if JUCE_MAC - const int newResFile = CurResFile(); - if (newResFile != oldResFile) // avoid confusing the parent app's resource file with the plug-in's - { - module->resFileId = newResFile; - UseResFile (oldResFile); - } - #endif - } - catch (...) - {} - } - - return result; -} - -//============================================================================== -namespace -{ - static const int defaultVSTSampleRateValue = 44100; - static const int defaultVSTBlockSizeValue = 512; - - // handles non plugin-specific callbacks.. - VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) - { - (void) index; - (void) value; - (void) opt; - - switch (opcode) - { - case audioMasterCanDo: - { - static const char* canDos[] = { "supplyIdle", - "sendVstEvents", - "sendVstMidiEvent", - "sendVstTimeInfo", - "receiveVstEvents", - "receiveVstMidiEvent", - "supportShell", - "shellCategory" }; - - for (int i = 0; i < numElementsInArray (canDos); ++i) - if (strcmp (canDos[i], (const char*) ptr) == 0) - return 1; - - return 0; - } - - case audioMasterVersion: return 0x2400; - case audioMasterCurrentId: return shellUIDToCreate; - case audioMasterGetNumAutomatableParameters: return 0; - case audioMasterGetAutomationState: return 1; - case audioMasterGetVendorVersion: return 0x0101; - - case audioMasterGetVendorString: - case audioMasterGetProductString: - { - String hostName ("Juce VST Host"); - - if (JUCEApplication::getInstance() != nullptr) - hostName = JUCEApplication::getInstance()->getApplicationName(); - - hostName.copyToUTF8 ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); - break; - } - - case audioMasterGetSampleRate: return (VstIntPtr) defaultVSTSampleRateValue; - case audioMasterGetBlockSize: return (VstIntPtr) defaultVSTBlockSizeValue; - case audioMasterSetOutputSampleRate: return 0; - - default: - DBG ("*** Unhandled VST Callback: " + String ((int) opcode)); - break; - } - - return 0; - } -} - -// handles callbacks for a specific plugin -VstIntPtr VSTPluginInstance::handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) -{ - switch (opcode) - { - case audioMasterAutomate: - sendParamChangeMessageToListeners (index, opt); - break; - - case audioMasterProcessEvents: - handleMidiFromPlugin ((const VstEvents*) ptr); - break; - - case audioMasterGetTime: - #if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4311) - #endif - - return (VstIntPtr) &vstHostTime; - - #if JUCE_MSVC - #pragma warning (pop) - #endif - break; - - case audioMasterIdle: - if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread()) - { - const IdleCallRecursionPreventer icrp; - - #if JUCE_MAC - if (getActiveEditor() != nullptr) - dispatch (effEditIdle, 0, 0, 0, 0); - #endif - - Timer::callPendingTimersSynchronously(); - - handleUpdateNowIfNeeded(); - - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); - } - break; - - case audioMasterUpdateDisplay: - triggerAsyncUpdate(); - break; - - case audioMasterTempoAt: - // returns (10000 * bpm) - break; - - case audioMasterNeedIdle: - startTimer (50); - break; - - case audioMasterSizeWindow: - if (getActiveEditor() != nullptr) - getActiveEditor()->setSize (index, value); - - return 1; - - case audioMasterGetSampleRate: - return (VstIntPtr) (getSampleRate() > 0 ? getSampleRate() : defaultVSTSampleRateValue); - - case audioMasterGetBlockSize: - return (VstIntPtr) (getBlockSize() > 0 ? getBlockSize() : defaultVSTBlockSizeValue); - - case audioMasterWantMidi: - wantsMidiMessages = true; - break; - - case audioMasterGetDirectory: - #if JUCE_MAC - return (VstIntPtr) (void*) &module->parentDirFSSpec; - #else - return (VstIntPtr) (pointer_sized_uint) module->fullParentDirectoryPathName.toUTF8().getAddress(); - #endif - - case audioMasterGetAutomationState: - // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write - break; - - case audioMasterPinConnected: - if (value == 0) // input - { - if (index < getNumInputChannels()) - return 0; - } - else // output - { - if (index < getNumOutputChannels()) - return 0; - } - - return 1; // (not connected) - - case audioMasterIOChanged: - setLatencySamples (effect->initialDelay); - break; - - // none of these are handled (yet).. - case audioMasterBeginEdit: - case audioMasterEndEdit: - case audioMasterSetTime: - case audioMasterGetParameterQuantization: - case audioMasterGetInputLatency: - case audioMasterGetOutputLatency: - case audioMasterGetPreviousPlug: - case audioMasterGetNextPlug: - case audioMasterWillReplaceOrAccumulate: - case audioMasterGetCurrentProcessLevel: - case audioMasterOfflineStart: - case audioMasterOfflineRead: - case audioMasterOfflineWrite: - case audioMasterOfflineGetCurrentPass: - case audioMasterOfflineGetCurrentMetaPass: - case audioMasterVendorSpecific: - case audioMasterSetIcon: - case audioMasterGetLanguage: - case audioMasterOpenWindow: - case audioMasterCloseWindow: - break; - - default: - return handleGeneralCallback (opcode, index, value, ptr, opt); - } - - return 0; -} - -// entry point for all callbacks from the plugin -static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) -{ - try - { - if (effect != nullptr && effect->resvd2 != 0) - { - return ((VSTPluginInstance*)(effect->resvd2)) - ->handleCallback (opcode, index, value, ptr, opt); - } - - return handleGeneralCallback (opcode, index, value, ptr, opt); - } - catch (...) - { - return 0; - } -} - -//============================================================================== -String VSTPluginInstance::getVersion() const -{ - unsigned int v = dispatch (effGetVendorVersion, 0, 0, 0, 0); - - String s; - - if (v == 0 || v == -1) - v = getVersionNumber(); - - if (v != 0) - { - int versionBits[4]; - int n = 0; - - while (v != 0) - { - versionBits [n++] = (v & 0xff); - v >>= 8; - } - - s << 'V'; - - while (n > 0) - { - s << versionBits [--n]; - - if (n > 0) - s << '.'; - } - } - - return s; -} - -int VSTPluginInstance::getUID() const -{ - int uid = effect != nullptr ? effect->uniqueID : 0; - - if (uid == 0) - uid = module->file.hashCode(); - - return uid; -} - -String VSTPluginInstance::getCategory() const -{ - const char* result = nullptr; - - switch (dispatch (effGetPlugCategory, 0, 0, 0, 0)) - { - case kPlugCategEffect: result = "Effect"; break; - case kPlugCategSynth: result = "Synth"; break; - case kPlugCategAnalysis: result = "Analysis"; break; - case kPlugCategMastering: result = "Mastering"; break; - case kPlugCategSpacializer: result = "Spacial"; break; - case kPlugCategRoomFx: result = "Reverb"; break; - case kPlugSurroundFx: result = "Surround"; break; - case kPlugCategRestoration: result = "Restoration"; break; - case kPlugCategGenerator: result = "Tone generation"; break; - default: break; - } - - return result; -} - -//============================================================================== -float VSTPluginInstance::getParameter (int index) -{ - if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams)) - { - try - { - const ScopedLock sl (lock); - return effect->getParameter (effect, index); - } - catch (...) - { - } - } - - return 0.0f; -} - -void VSTPluginInstance::setParameter (int index, float newValue) -{ - if (effect != nullptr && isPositiveAndBelow (index, (int) effect->numParams)) - { - try - { - const ScopedLock sl (lock); - - if (effect->getParameter (effect, index) != newValue) - effect->setParameter (effect, index, newValue); - } - catch (...) - { - } - } -} - -const String VSTPluginInstance::getParameterName (int index) -{ - if (effect != nullptr) - { - jassert (index >= 0 && index < effect->numParams); - - char nm [256] = { 0 }; - dispatch (effGetParamName, index, 0, nm, 0); - return String (nm).trim(); - } - - return String::empty; -} - -const String VSTPluginInstance::getParameterLabel (int index) const -{ - if (effect != nullptr) - { - jassert (index >= 0 && index < effect->numParams); - - char nm [256] = { 0 }; - dispatch (effGetParamLabel, index, 0, nm, 0); - return String (nm).trim(); - } - - return String::empty; -} - -const String VSTPluginInstance::getParameterText (int index) -{ - if (effect != nullptr) - { - jassert (index >= 0 && index < effect->numParams); - - char nm [256] = { 0 }; - dispatch (effGetParamDisplay, index, 0, nm, 0); - return String (nm).trim(); - } - - return String::empty; -} - -bool VSTPluginInstance::isParameterAutomatable (int index) const -{ - if (effect != nullptr) - { - jassert (index >= 0 && index < effect->numParams); - return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0; - } - - return false; -} - -void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest) -{ - dest.setSize (64 + 4 * getNumParameters()); - dest.fillWith (0); - - getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); - - float* const p = (float*) (((char*) dest.getData()) + 64); - for (int i = 0; i < getNumParameters(); ++i) - p[i] = getParameter(i); -} - -void VSTPluginInstance::restoreFromTempParameterStore (const MemoryBlock& m) -{ - changeProgramName (getCurrentProgram(), (const char*) m.getData()); - - float* p = (float*) (((char*) m.getData()) + 64); - for (int i = 0; i < getNumParameters(); ++i) - setParameter (i, p[i]); -} - -//============================================================================== -void VSTPluginInstance::setCurrentProgram (int newIndex) -{ - if (getNumPrograms() > 0 && newIndex != getCurrentProgram()) - dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0); -} - -const String VSTPluginInstance::getProgramName (int index) -{ - if (index == getCurrentProgram()) - { - return getCurrentProgramName(); - } - else if (effect != nullptr) - { - char nm [256] = { 0 }; - - if (dispatch (effGetProgramNameIndexed, - jlimit (0, getNumPrograms(), index), - -1, nm, 0) != 0) - { - return String (CharPointer_UTF8 (nm)).trim(); - } - } - - return programNames [index]; -} - -void VSTPluginInstance::changeProgramName (int index, const String& newName) -{ - if (index == getCurrentProgram()) - { - if (getNumPrograms() > 0 && newName != getCurrentProgramName()) - dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toUTF8().getAddress(), 0.0f); - } - else - { - jassertfalse; // xxx not implemented! - } -} - -void VSTPluginInstance::updateStoredProgramNames() -{ - if (effect != nullptr && getNumPrograms() > 0) - { - char nm[256] = { 0 }; - - // only do this if the plugin can't use indexed names.. - if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0) - { - const int oldProgram = getCurrentProgram(); - MemoryBlock oldSettings; - createTempParameterStore (oldSettings); - - for (int i = 0; i < getNumPrograms(); ++i) - { - setCurrentProgram (i); - getCurrentProgramName(); // (this updates the list) - } - - setCurrentProgram (oldProgram); - restoreFromTempParameterStore (oldSettings); - } - } -} - -const String VSTPluginInstance::getCurrentProgramName() -{ - String name; - - if (effect != nullptr) - { - { - char nm[256] = { 0 }; - dispatch (effGetProgramName, 0, 0, nm, 0); - name = String (CharPointer_UTF8 (nm)).trim(); - } - - const int index = getCurrentProgram(); - - if (programNames[index].isEmpty()) - { - while (programNames.size() < index) - programNames.add (String::empty); - - programNames.set (index, name); - } - } - - return name; -} - -//============================================================================== -const String VSTPluginInstance::getInputChannelName (int index) const -{ - if (index >= 0 && index < getNumInputChannels()) - { - VstPinProperties pinProps; - if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) - return String (pinProps.label, sizeof (pinProps.label)); - } - - return String::empty; -} - -bool VSTPluginInstance::isInputChannelStereoPair (int index) const -{ - if (index < 0 || index >= getNumInputChannels()) - return false; - - VstPinProperties pinProps; - if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) - return (pinProps.flags & kVstPinIsStereo) != 0; - - return true; -} - -const String VSTPluginInstance::getOutputChannelName (int index) const -{ - if (index >= 0 && index < getNumOutputChannels()) - { - VstPinProperties pinProps; - if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) - return String (pinProps.label, sizeof (pinProps.label)); - } - - return String::empty; -} - -bool VSTPluginInstance::isOutputChannelStereoPair (int index) const -{ - if (index < 0 || index >= getNumOutputChannels()) - return false; - - VstPinProperties pinProps; - if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) - return (pinProps.flags & kVstPinIsStereo) != 0; - - return true; -} - -//============================================================================== -void VSTPluginInstance::setPower (const bool on) -{ - dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0); - isPowerOn = on; -} - -//============================================================================== -const int defaultMaxSizeMB = 64; - -void VSTPluginInstance::getStateInformation (MemoryBlock& destData) -{ - saveToFXBFile (destData, true, defaultMaxSizeMB); -} - -void VSTPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) -{ - saveToFXBFile (destData, false, defaultMaxSizeMB); -} - -void VSTPluginInstance::setStateInformation (const void* data, int sizeInBytes) -{ - loadFromFXBFile (data, sizeInBytes); -} - -void VSTPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) -{ - loadFromFXBFile (data, sizeInBytes); -} - -//============================================================================== -//============================================================================== -VSTPluginFormat::VSTPluginFormat() -{ -} - -VSTPluginFormat::~VSTPluginFormat() -{ -} - -void VSTPluginFormat::findAllTypesForFile (OwnedArray & results, +void VSTPluginFormat::findAllTypesForFile (OwnedArray& results, const String& fileOrIdentifier) { if (! fileMightContainThisPluginType (fileOrIdentifier)) @@ -2728,75 +2661,53 @@ void VSTPluginFormat::findAllTypesForFile (OwnedArray & resul desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; - ScopedPointer instance (dynamic_cast (createInstanceFromDescription (desc))); + ScopedPointer instance (createAndUpdateDesc (*this, desc)); - if (instance == 0) + if (instance == nullptr) return; - try + if (instance->getVstCategory() != kPlugCategShell) { - #if JUCE_MAC - if (instance->module->resFileId != 0) - UseResFile (instance->module->resFileId); - #endif + // Normal plugin... + results.add (new PluginDescription (desc)); - instance->fillInPluginDescription (desc); - - VstPlugCategory category = (VstPlugCategory) instance->dispatch (effGetPlugCategory, 0, 0, 0, 0); - - if (category != kPlugCategShell) + instance->dispatch (effOpen, 0, 0, 0, 0); + } + else + { + // It's a shell plugin, so iterate all the subtypes... + for (;;) { - // Normal plugin... - results.add (new PluginDescription (desc)); + char shellEffectName [256] = { 0 }; + const int uid = (int) instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0); - instance->dispatch (effOpen, 0, 0, 0, 0); - } - else - { - // It's a shell plugin, so iterate all the subtypes... - for (;;) + if (uid == 0) + break; + + desc.uid = uid; + desc.name = shellEffectName; + + aboutToScanVSTShellPlugin (desc); + + ScopedPointer shellInstance (createAndUpdateDesc (*this, desc)); + + if (shellInstance != nullptr) { - char shellEffectName [64] = { 0 }; - const int uid = instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0); + jassert (desc.uid == uid); + desc.hasSharedContainer = true; + desc.name = shellEffectName; - if (uid == 0) - { - break; - } - else - { - desc.uid = uid; - desc.name = shellEffectName; - desc.descriptiveName = shellEffectName; - - bool alreadyThere = false; - - for (int i = results.size(); --i >= 0;) - { - PluginDescription* const d = results.getUnchecked(i); - - if (d->isDuplicateOf (desc)) - { - alreadyThere = true; - break; - } - } - - if (! alreadyThere) - results.add (new PluginDescription (desc)); - } + if (! arrayContainsPlugin (results, desc)) + results.add (new PluginDescription (desc)); } } } - catch (...) - { - // crashed while loading... - } } -AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc, + double sampleRate, int blockSize) { - ScopedPointer result; + ScopedPointer result; if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { @@ -2805,9 +2716,7 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); file.getParentDirectory().setAsCurrentWorkingDirectory(); - const ReferenceCountedObjectPtr module (ModuleHandle::findOrCreateModule (file)); - - if (module != nullptr) + if (ModuleHandle::Ptr module = ModuleHandle::findOrCreateModule (file)) { shellUIDToCreate = desc.uid; @@ -2816,7 +2725,7 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi if (result->effect != nullptr) { result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) (VSTPluginInstance*) result; - result->initialise(); + result->initialise (sampleRate, blockSize); } else { @@ -2832,7 +2741,7 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi bool VSTPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { - const File f (fileOrIdentifier); + const File f (File::createFileWithoutCheckingPath (fileOrIdentifier)); #if JUCE_MAC if (f.isDirectory() && f.hasFileExtension (".vst")) @@ -2868,6 +2777,11 @@ String VSTPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdent return fileOrIdentifier; } +bool VSTPluginFormat::pluginNeedsRescanning (const PluginDescription& desc) +{ + return File (desc.fileOrIdentifier).getLastModificationTime() != desc.lastFileModTime; +} + bool VSTPluginFormat::doesPluginStillExist (const PluginDescription& desc) { return File (desc.fileOrIdentifier).exists(); @@ -2909,16 +2823,81 @@ FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() { #if JUCE_MAC return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST"); + #elif JUCE_LINUX + return FileSearchPath (SystemStats::getEnvironmentVariable ("VST_PATH", + "/usr/lib/vst;/usr/local/lib/vst;~/.vst") + .replace (":", ";")); #elif JUCE_WINDOWS const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); - return FileSearchPath (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", - programFiles + "\\Steinberg\\VstPlugins")); - #elif JUCE_LINUX - return FileSearchPath ("/usr/lib/vst"); + FileSearchPath paths; + paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", + programFiles + "\\Steinberg\\VstPlugins")); + paths.removeNonExistentPaths(); + + paths.add (WindowsRegistry::getValue ("HKLM\\Software\\VST\\VSTPluginsPath", + programFiles + "\\VstPlugins")); + return paths; #endif } -#endif -#undef log +const XmlElement* VSTPluginFormat::getVSTXML (AudioPluginInstance* plugin) +{ + if (VSTPluginInstance* const vst = dynamic_cast (plugin)) + if (vst->module != nullptr) + return vst->module->vstXml.get(); + + return nullptr; +} + +bool VSTPluginFormat::loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize) +{ + if (VSTPluginInstance* vst = dynamic_cast (plugin)) + return vst->loadFromFXBFile (data, dataSize); + + return false; +} + +bool VSTPluginFormat::saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& dest, bool asFXB) +{ + if (VSTPluginInstance* vst = dynamic_cast (plugin)) + return vst->saveToFXBFile (dest, asFXB); + + return false; +} + +bool VSTPluginFormat::getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset) +{ + if (VSTPluginInstance* vst = dynamic_cast (plugin)) + return vst->getChunkData (result, isPreset, 128); + + return false; +} + +bool VSTPluginFormat::setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset) +{ + if (VSTPluginInstance* vst = dynamic_cast (plugin)) + return vst->setChunkData (data, size, isPreset); + + return false; +} + +void VSTPluginFormat::setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions) +{ + ScopedPointer f (functions); + + if (VSTPluginInstance* vst = dynamic_cast (plugin)) + vst->extraFunctions = f; +} + +VSTPluginFormat::VstIntPtr JUCE_CALLTYPE VSTPluginFormat::dispatcher (AudioPluginInstance* plugin, int32 opcode, int32 index, VstIntPtr value, void* ptr, float opt) +{ + if (VSTPluginInstance* vst = dynamic_cast (plugin)) + return vst->dispatch (opcode, index, value, ptr, opt); + + return 0; +} + +void VSTPluginFormat::aboutToScanVSTShellPlugin (const PluginDescription&) {} + #endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.h index 53a3c54..4e4b744 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.h @@ -1,35 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ -#define __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ - -#include "../format/juce_AudioPluginFormat.h" - - -#if JUCE_PLUGINHOST_VST +#if JUCE_PLUGINHOST_VST || DOXYGEN //============================================================================== /** @@ -43,22 +36,78 @@ public: ~VSTPluginFormat(); //============================================================================== - String getName() const { return "VST"; } - void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); - AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const String& fileOrIdentifier); - String getNameOfPluginFromIdentifier (const String& fileOrIdentifier); - StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, bool recursive); - bool doesPluginStillExist (const PluginDescription& desc); - FileSearchPath getDefaultLocationsToSearch(); + /** Attempts to retreive the VSTXML data from a plugin. + Will return nullptr if the plugin isn't a VST, or if it doesn't have any VSTXML. + */ + static const XmlElement* getVSTXML (AudioPluginInstance* plugin); + + /** Attempts to reload a VST plugin's state from some FXB or FXP data. */ + static bool loadFromFXBFile (AudioPluginInstance* plugin, const void* data, size_t dataSize); + + /** Attempts to save a VST's state to some FXP or FXB data. */ + static bool saveToFXBFile (AudioPluginInstance* plugin, MemoryBlock& result, bool asFXB); + + /** Attempts to get a VST's state as a chunk of memory. */ + static bool getChunkData (AudioPluginInstance* plugin, MemoryBlock& result, bool isPreset); + + /** Attempts to set a VST's state from a chunk of memory. */ + static bool setChunkData (AudioPluginInstance* plugin, const void* data, int size, bool isPreset); + + //============================================================================== + /** Base class for some extra functions that can be attached to a VST plugin instance. */ + class ExtraFunctions + { + public: + virtual ~ExtraFunctions() {} + + /** This should return 10000 * the BPM at this position in the current edit. */ + virtual int64 getTempoAt (int64 samplePos) = 0; + + /** This should return the host's automation state. + @returns 0 = not supported, 1 = off, 2 = read, 3 = write, 4 = read/write + */ + virtual int getAutomationState() = 0; + }; + + /** Provides an ExtraFunctions callback object for a plugin to use. + The plugin will take ownership of the object and will delete it automatically. + */ + static void setExtraFunctions (AudioPluginInstance* plugin, ExtraFunctions* functions); + + //============================================================================== + #if JUCE_64BIT + typedef int64 VstIntPtr; + #else + typedef int32 VstIntPtr; + #endif + + /** This simply calls directly to the VST's AEffect::dispatcher() function. */ + static VstIntPtr JUCE_CALLTYPE dispatcher (AudioPluginInstance*, int32, int32, VstIntPtr, void*, float); + + //============================================================================== + String getName() const override { return "VST"; } + void findAllTypesForFile (OwnedArray&, const String& fileOrIdentifier) override; + AudioPluginInstance* createInstanceFromDescription (const PluginDescription&, double, int) override; + bool fileMightContainThisPluginType (const String& fileOrIdentifier) override; + String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) override; + bool pluginNeedsRescanning (const PluginDescription&) override; + StringArray searchPathsForPlugins (const FileSearchPath&, bool recursive) override; + bool doesPluginStillExist (const PluginDescription&) override; + FileSearchPath getDefaultLocationsToSearch() override; + bool canScanForPlugins() const override { return true; } + + /** Can be overridden to receive a callback when each member of a shell plugin is about to be + tested during a call to findAllTypesForFile(). + Only the name and uid members of the PluginDescription are guaranteed to be valid when + this is called. + */ + virtual void aboutToScanVSTShellPlugin (const PluginDescription&); private: - //============================================================================== - void recursiveFileSearch (StringArray& results, const File& dir, const bool recursive); + void recursiveFileSearch (StringArray&, const File&, bool recursive); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginFormat); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VSTPluginFormat) }; #endif -#endif // __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ diff --git a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp index fdf43b7..78d3387 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_AUDIO_PROCESSORS_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_AUDIO_PROCESSORS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -54,14 +53,95 @@ #if JUCE_PLUGINHOST_VST && JUCE_LINUX #include + #include + #undef KeyPress +#endif + +#if ! JUCE_WINDOWS && ! JUCE_MAC + #undef JUCE_PLUGINHOST_VST3 + #define JUCE_PLUGINHOST_VST3 0 #endif //============================================================================== namespace juce { -// START_AUTOINCLUDE format/*.cpp, processors/*.cpp, format_types/*.cpp, -// format_types/*.mm, scanning/*.cpp +static inline bool arrayContainsPlugin (const OwnedArray& list, + const PluginDescription& desc) +{ + for (int i = list.size(); --i >= 0;) + if (list.getUnchecked(i)->isDuplicateOf (desc)) + return true; + + return false; +} + +#if JUCE_MAC +//============================================================================== +struct AutoResizingNSViewComponent : public NSViewComponent, + private AsyncUpdater +{ + AutoResizingNSViewComponent() : recursive (false) {} + + void childBoundsChanged (Component*) override + { + if (recursive) + { + triggerAsyncUpdate(); + } + else + { + recursive = true; + resizeToFitView(); + recursive = true; + } + } + + void handleAsyncUpdate() override { resizeToFitView(); } + + bool recursive; +}; + +//============================================================================== +struct AutoResizingNSViewComponentWithParent : public AutoResizingNSViewComponent, + private Timer +{ + AutoResizingNSViewComponentWithParent() + { + NSView* v = [[NSView alloc] init]; + setView (v); + [v release]; + + startTimer (100); + } + + void timerCallback() override + { + if (NSView* parent = (NSView*) getView()) + { + if ([[parent subviews] count] > 0) + { + if (NSView* child = [[parent subviews] objectAtIndex: 0]) + { + NSRect f = [parent frame]; + NSSize newSize = [child frame].size; + + if (f.size.width != newSize.width || f.size.height != newSize.height) + { + f.size = newSize; + [parent setFrame: f]; + } + } + } + } + } +}; +#endif + +#if JUCE_CLANG + #pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + #include "format/juce_AudioPluginFormat.cpp" #include "format/juce_AudioPluginFormatManager.cpp" #include "processors/juce_AudioProcessor.cpp" @@ -69,11 +149,12 @@ namespace juce #include "processors/juce_AudioProcessorGraph.cpp" #include "processors/juce_GenericAudioProcessorEditor.cpp" #include "processors/juce_PluginDescription.cpp" +#include "format_types/juce_LADSPAPluginFormat.cpp" #include "format_types/juce_VSTPluginFormat.cpp" +#include "format_types/juce_VST3PluginFormat.cpp" #include "format_types/juce_AudioUnitPluginFormat.mm" #include "scanning/juce_KnownPluginList.cpp" #include "scanning/juce_PluginDirectoryScanner.cpp" #include "scanning/juce_PluginListComponent.cpp" -// END_AUTOINCLUDE } diff --git a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h index f21cda1..3854d2a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h +++ b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIO_PROCESSORS_JUCEHEADER__ -#define __JUCE_AUDIO_PROCESSORS_JUCEHEADER__ +#ifndef JUCE_AUDIO_PROCESSORS_H_INCLUDED +#define JUCE_AUDIO_PROCESSORS_H_INCLUDED #include "../juce_gui_basics/juce_gui_basics.h" #include "../juce_audio_basics/juce_audio_basics.h" @@ -35,23 +34,33 @@ Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be installed on your machine. - @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU + @see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU, JUCE_PLUGINHOST_VST3 */ #ifndef JUCE_PLUGINHOST_VST #define JUCE_PLUGINHOST_VST 0 #endif +/** Config: JUCE_PLUGINHOST_VST3 + Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be + installed on your machine. + + @see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU +*/ +#ifndef JUCE_PLUGINHOST_VST3 + #define JUCE_PLUGINHOST_VST3 0 +#endif + /** Config: JUCE_PLUGINHOST_AU Enables the AudioUnit plugin hosting classes. This is Mac-only, of course. - @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST + @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_VST3 */ #ifndef JUCE_PLUGINHOST_AU #define JUCE_PLUGINHOST_AU 0 #endif -#if ! (JUCE_PLUGINHOST_AU || JUCE_PLUGINHOST_VST) -// #error "You need to set either the JUCE_PLUGINHOST_AU anr/or JUCE_PLUGINHOST_VST flags if you're using this module!" +#if ! (JUCE_PLUGINHOST_AU || JUCE_PLUGINHOST_VST || JUCE_PLUGINHOST_VST3) +// #error "You need to set either the JUCE_PLUGINHOST_AU and/or JUCE_PLUGINHOST_VST and/or JUCE_PLUGINHOST_VST3 flags if you're using this module!" #endif #if ! (defined (JUCE_SUPPORT_CARBON) || JUCE_64BIT) @@ -60,66 +69,29 @@ //============================================================================= //============================================================================= -#include "../juce_core/system/juce_StandardHeader.h" - namespace juce { -// START_AUTOINCLUDE processors, format, format_types, scanning -#ifndef __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ - #include "processors/juce_AudioPlayHead.h" -#endif -#ifndef __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ - #include "processors/juce_AudioPluginInstance.h" -#endif -#ifndef __JUCE_AUDIOPROCESSOR_JUCEHEADER__ - #include "processors/juce_AudioProcessor.h" -#endif -#ifndef __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ - #include "processors/juce_AudioProcessorEditor.h" -#endif -#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ - #include "processors/juce_AudioProcessorGraph.h" -#endif -#ifndef __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ - #include "processors/juce_AudioProcessorListener.h" -#endif -#ifndef __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ - #include "processors/juce_GenericAudioProcessorEditor.h" -#endif -#ifndef __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ - #include "processors/juce_PluginDescription.h" -#endif -#ifndef __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ - #include "format/juce_AudioPluginFormat.h" -#endif -#ifndef __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ - #include "format/juce_AudioPluginFormatManager.h" -#endif -#ifndef __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ - #include "format_types/juce_AudioUnitPluginFormat.h" -#endif -#ifndef __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ - #include "format_types/juce_DirectXPluginFormat.h" -#endif -#ifndef __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ - #include "format_types/juce_LADSPAPluginFormat.h" -#endif +class AudioProcessor; +#include "processors/juce_AudioPlayHead.h" +#include "processors/juce_AudioProcessorEditor.h" +#include "processors/juce_AudioProcessorListener.h" +#include "processors/juce_AudioProcessor.h" +#include "processors/juce_PluginDescription.h" +#include "processors/juce_AudioPluginInstance.h" +#include "processors/juce_AudioProcessorGraph.h" +#include "processors/juce_GenericAudioProcessorEditor.h" +#include "format/juce_AudioPluginFormat.h" +#include "format/juce_AudioPluginFormatManager.h" +#include "scanning/juce_KnownPluginList.h" +#include "format_types/juce_AudioUnitPluginFormat.h" +#include "format_types/juce_LADSPAPluginFormat.h" #include "format_types/juce_VSTMidiEventList.h" -#ifndef __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ - #include "format_types/juce_VSTPluginFormat.h" -#endif -#ifndef __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ - #include "scanning/juce_KnownPluginList.h" -#endif -#ifndef __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ - #include "scanning/juce_PluginDirectoryScanner.h" -#endif -#ifndef __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ - #include "scanning/juce_PluginListComponent.h" -#endif -// END_AUTOINCLUDE +#include "format_types/juce_VSTPluginFormat.h" +#include "format_types/juce_VST3PluginFormat.h" +#include "scanning/juce_PluginDirectoryScanner.h" +#include "scanning/juce_PluginListComponent.h" } -#endif // __JUCE_AUDIO_PROCESSORS_JUCEHEADER__ +#endif // JUCE_AUDIO_PROCESSORS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.mm b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.mm index 7c9c33f..918e848 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.mm +++ b/JuceLibraryCode/modules/juce_audio_processors/juce_audio_processors.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_audio_processors/juce_module_info b/JuceLibraryCode/modules/juce_audio_processors/juce_module_info index aa10d2a..c46eb74 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_processors/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_processors", "name": "JUCE audio plugin hosting classes", - "version": "2.0.21", + "version": "3.0.6", "description": "Classes for loading and playing VST, AU, or internally-generated audio processors.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPlayHead.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPlayHead.h index 67d96c3..b92b802 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPlayHead.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPlayHead.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ -#define __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ +#ifndef JUCE_AUDIOPLAYHEAD_H_INCLUDED +#define JUCE_AUDIOPLAYHEAD_H_INCLUDED //============================================================================== @@ -72,22 +71,21 @@ public: /** Time signature denominator, e.g. the 4 of a 3/4 time sig */ int timeSigDenominator; + /** The current play position, in samples from the start of the edit. */ + int64 timeInSamples; /** 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. - */ + /** The current play position, in pulses-per-quarter-note. */ 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. + This is the time from the start of the edit to the start of the current + bar, in ppq units. Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If it's not available, the value will be 0. @@ -131,9 +129,12 @@ public: //============================================================================== /** Fills-in the given structure with details about the transport's position at the start of the current processing block. + + This method must ONLY be called from within your AudioProcessor::processBlock() + method. Calling it at any other time will probably cause a nasty crash. */ virtual bool getCurrentPosition (CurrentPositionInfo& result) = 0; }; -#endif // __JUCE_AUDIOPLAYHEAD_JUCEHEADER__ +#endif // JUCE_AUDIOPLAYHEAD_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h index 28a5675..b7c7b7d 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioPluginInstance.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ -#define __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ - -#include "../processors/juce_AudioProcessor.h" -#include "juce_PluginDescription.h" +#ifndef JUCE_AUDIOPLUGININSTANCE_H_INCLUDED +#define JUCE_AUDIOPLUGININSTANCE_H_INCLUDED //============================================================================== @@ -37,6 +33,10 @@ This derives from the AudioProcessor class, and adds some extra functionality that helps when wrapping dynamically loaded plugins. + This class is not needed when writing plugins, and you should never need to derive + your own sub-classes from it. The plugin hosting classes use it internally and will + return AudioPluginInstance objects which wrap external plugins. + @see AudioProcessor, AudioPluginFormat */ class JUCE_API AudioPluginInstance : public AudioProcessor @@ -51,23 +51,36 @@ public: virtual ~AudioPluginInstance() {} //============================================================================== - /** Fills-in the appropriate parts of this plugin description object. - */ + /** Fills-in the appropriate parts of this plugin description object. */ virtual void fillInPluginDescription (PluginDescription& description) const = 0; - /** Returns a pointer to some kind of platform-specific data about the plugin. + /** Returns a PluginDescription for this plugin. + This is just a convenience method to avoid calling fillInPluginDescription. + */ + PluginDescription getPluginDescription() const + { + PluginDescription desc; + fillInPluginDescription (desc); + return desc; + } + /** Returns a pointer to some kind of platform-specific data about the plugin. E.g. For a VST, this value can be cast to an AEffect*. For an AudioUnit, it can be cast to an AudioUnit handle. */ - virtual void* getPlatformSpecificData() { return nullptr; } + virtual void* getPlatformSpecificData() { return nullptr; } + + /** For some formats (currently AudioUnit), this forces a reload of the list of + available parameters. + */ + virtual void refreshParameterList() {} protected: //============================================================================== AudioPluginInstance() {} - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioPluginInstance) }; -#endif // __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ +#endif // JUCE_AUDIOPLUGININSTANCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 91a8286..86f2e81 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -1,30 +1,37 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +static ThreadLocalValue wrapperTypeBeingCreated; + +void JUCE_CALLTYPE AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::WrapperType type) +{ + wrapperTypeBeingCreated = type; +} + AudioProcessor::AudioProcessor() - : playHead (nullptr), + : wrapperType (wrapperTypeBeingCreated.get()), + playHead (nullptr), sampleRate (0), blockSize (0), numInputChannels (0), @@ -48,7 +55,7 @@ AudioProcessor::~AudioProcessor() #endif } -void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead) noexcept +void AudioProcessor::setPlayHead (AudioPlayHead* const newPlayHead) { playHead = newPlayHead; } @@ -62,23 +69,37 @@ void AudioProcessor::addListener (AudioProcessorListener* const newListener) void AudioProcessor::removeListener (AudioProcessorListener* const listenerToRemove) { const ScopedLock sl (listenerLock); - listeners.removeValue (listenerToRemove); + listeners.removeFirstMatchingValue (listenerToRemove); } -void AudioProcessor::setPlayConfigDetails (const int numIns, - const int numOuts, - const double sampleRate_, - const int blockSize_) noexcept +void AudioProcessor::setPlayConfigDetails (const int newNumIns, + const int newNumOuts, + const double newSampleRate, + const int newBlockSize) noexcept { - numInputChannels = numIns; - numOutputChannels = numOuts; - sampleRate = sampleRate_; - blockSize = blockSize_; + sampleRate = newSampleRate; + blockSize = newBlockSize; + + if (numInputChannels != newNumIns || numOutputChannels != newNumOuts) + { + numInputChannels = newNumIns; + numOutputChannels = newNumOuts; + + numChannelsChanged(); + } } -void AudioProcessor::setNonRealtime (const bool nonRealtime_) noexcept +void AudioProcessor::numChannelsChanged() {} + +void AudioProcessor::setSpeakerArrangement (const String& inputs, const String& outputs) { - nonRealtime = nonRealtime_; + inputSpeakerArrangement = inputs; + outputSpeakerArrangement = outputs; +} + +void AudioProcessor::setNonRealtime (const bool newNonRealtime) noexcept +{ + nonRealtime = newNonRealtime; } void AudioProcessor::setLatencySamples (const int newLatency) @@ -97,100 +118,93 @@ void AudioProcessor::setParameterNotifyingHost (const int parameterIndex, sendParamChangeMessageToListeners (parameterIndex, newValue); } +String AudioProcessor::getParameterName (int parameterIndex, int maximumStringLength) +{ + return getParameterName (parameterIndex).substring (0, maximumStringLength); +} + +String AudioProcessor::getParameterText (int parameterIndex, int maximumStringLength) +{ + return getParameterText (parameterIndex).substring (0, maximumStringLength); +} + +int AudioProcessor::getParameterNumSteps (int /*parameterIndex*/) { return 0x7fffffff; } +float AudioProcessor::getParameterDefaultValue (int /*parameterIndex*/) { return 0.0f; } + +AudioProcessorListener* AudioProcessor::getListenerLocked (const int index) const noexcept +{ + const ScopedLock sl (listenerLock); + return listeners [index]; +} + void AudioProcessor::sendParamChangeMessageToListeners (const int parameterIndex, const float newValue) { - jassert (isPositiveAndBelow (parameterIndex, getNumParameters())); - - for (int i = listeners.size(); --i >= 0;) + if (isPositiveAndBelow (parameterIndex, getNumParameters())) { - AudioProcessorListener* l; - - { - const ScopedLock sl (listenerLock); - l = listeners [i]; - } - - if (l != nullptr) - l->audioProcessorParameterChanged (this, parameterIndex, newValue); + for (int i = listeners.size(); --i >= 0;) + if (AudioProcessorListener* l = getListenerLocked (i)) + l->audioProcessorParameterChanged (this, parameterIndex, newValue); + } + else + { + jassertfalse; // called with an out-of-range parameter index! } } void AudioProcessor::beginParameterChangeGesture (int parameterIndex) { - jassert (isPositiveAndBelow (parameterIndex, getNumParameters())); - - #if JUCE_DEBUG - // This means you've called beginParameterChangeGesture twice in succession without a matching - // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. - jassert (! changingParams [parameterIndex]); - changingParams.setBit (parameterIndex); - #endif - - for (int i = listeners.size(); --i >= 0;) + if (isPositiveAndBelow (parameterIndex, getNumParameters())) { - AudioProcessorListener* l; + #if JUCE_DEBUG + // This means you've called beginParameterChangeGesture twice in succession without a matching + // call to endParameterChangeGesture. That might be fine in most hosts, but better to avoid doing it. + jassert (! changingParams [parameterIndex]); + changingParams.setBit (parameterIndex); + #endif - { - const ScopedLock sl (listenerLock); - l = listeners [i]; - } - - if (l != nullptr) - l->audioProcessorParameterChangeGestureBegin (this, parameterIndex); + for (int i = listeners.size(); --i >= 0;) + if (AudioProcessorListener* l = getListenerLocked (i)) + l->audioProcessorParameterChangeGestureBegin (this, parameterIndex); + } + else + { + jassertfalse; // called with an out-of-range parameter index! } } void AudioProcessor::endParameterChangeGesture (int parameterIndex) { - jassert (isPositiveAndBelow (parameterIndex, getNumParameters())); - - #if JUCE_DEBUG - // This means you've called endParameterChangeGesture without having previously called - // endParameterChangeGesture. That might be fine in most hosts, but better to keep the - // calls matched correctly. - jassert (changingParams [parameterIndex]); - changingParams.clearBit (parameterIndex); - #endif - - for (int i = listeners.size(); --i >= 0;) + if (isPositiveAndBelow (parameterIndex, getNumParameters())) { - AudioProcessorListener* l; + #if JUCE_DEBUG + // This means you've called endParameterChangeGesture without having previously called + // endParameterChangeGesture. That might be fine in most hosts, but better to keep the + // calls matched correctly. + jassert (changingParams [parameterIndex]); + changingParams.clearBit (parameterIndex); + #endif - { - const ScopedLock sl (listenerLock); - l = listeners [i]; - } - - if (l != nullptr) - l->audioProcessorParameterChangeGestureEnd (this, parameterIndex); + for (int i = listeners.size(); --i >= 0;) + if (AudioProcessorListener* l = getListenerLocked (i)) + l->audioProcessorParameterChangeGestureEnd (this, parameterIndex); + } + else + { + jassertfalse; // called with an out-of-range parameter index! } } void AudioProcessor::updateHostDisplay() { for (int i = listeners.size(); --i >= 0;) - { - AudioProcessorListener* l; - - { - const ScopedLock sl (listenerLock); - l = listeners [i]; - } - - if (l != nullptr) + if (AudioProcessorListener* l = getListenerLocked (i)) l->audioProcessorChanged (this); - } } -bool AudioProcessor::isParameterAutomatable (int /*parameterIndex*/) const -{ - return true; -} - -bool AudioProcessor::isMetaParameter (int /*parameterIndex*/) const -{ - return false; -} +String AudioProcessor::getParameterLabel (int) const { return String(); } +bool AudioProcessor::isParameterOrientationInverted (int) const { return false; } +bool AudioProcessor::isParameterAutomatable (int) const { return true; } +bool AudioProcessor::isMetaParameter (int) const { return false; } void AudioProcessor::suspendProcessing (const bool shouldBeSuspended) { @@ -198,9 +212,8 @@ void AudioProcessor::suspendProcessing (const bool shouldBeSuspended) suspended = shouldBeSuspended; } -void AudioProcessor::reset() -{ -} +void AudioProcessor::reset() {} +void AudioProcessor::processBlockBypassed (AudioSampleBuffer&, MidiBuffer&) {} //============================================================================== void AudioProcessor::editorBeingDeleted (AudioProcessorEditor* const editor) noexcept @@ -250,20 +263,20 @@ const uint32 magicXmlNumber = 0x21324356; void AudioProcessor::copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& destData) { - const String xmlString (xml.createDocument (String::empty, true, false)); - const int stringLength = xmlString.getNumBytesAsUTF8(); + { + MemoryOutputStream out (destData, false); + out.writeInt (magicXmlNumber); + out.writeInt (0); + xml.writeToStream (out, String(), true, false); + out.writeByte (0); + } - destData.setSize ((size_t) stringLength + 10); - - char* const d = static_cast (destData.getData()); - *(uint32*) d = ByteOrder::swapIfBigEndian ((const uint32) magicXmlNumber); - *(uint32*) (d + 4) = ByteOrder::swapIfBigEndian ((const uint32) stringLength); - - xmlString.copyToUTF8 (d + 8, stringLength + 1); + // go back and write the string length.. + static_cast (destData.getData())[1] + = ByteOrder::swapIfBigEndian ((uint32) destData.getSize() - 9); } -XmlElement* AudioProcessor::getXmlFromBinary (const void* data, - const int sizeInBytes) +XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeInBytes) { if (sizeInBytes > 8 && ByteOrder::littleEndianInt (data) == magicXmlNumber) @@ -280,12 +293,12 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, //============================================================================== void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {} -void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} +void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {} //============================================================================== bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& other) const noexcept { - return timeInSeconds == other.timeInSeconds + return timeInSamples == other.timeInSamples && ppqPosition == other.ppqPosition && editOriginTime == other.editOriginTime && ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart @@ -294,7 +307,10 @@ bool AudioPlayHead::CurrentPositionInfo::operator== (const CurrentPositionInfo& && isRecording == other.isRecording && bpm == other.bpm && timeSigNumerator == other.timeSigNumerator - && timeSigDenominator == other.timeSigDenominator; + && timeSigDenominator == other.timeSigDenominator + && ppqLoopStart == other.ppqLoopStart + && ppqLoopEnd == other.ppqLoopEnd + && isLooping == other.isLooping; } bool AudioPlayHead::CurrentPositionInfo::operator!= (const CurrentPositionInfo& other) const noexcept diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h index f5c224c..cec25c0 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -1,34 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPROCESSOR_JUCEHEADER__ -#define __JUCE_AUDIOPROCESSOR_JUCEHEADER__ - -#include "juce_AudioProcessorEditor.h" -#include "juce_AudioProcessorListener.h" -#include "juce_AudioPlayHead.h" +#ifndef JUCE_AUDIOPROCESSOR_H_INCLUDED +#define JUCE_AUDIOPROCESSOR_H_INCLUDED //============================================================================== @@ -49,11 +44,7 @@ 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. - */ + /** Constructor. */ AudioProcessor(); public: @@ -61,8 +52,7 @@ public: virtual ~AudioProcessor(); //============================================================================== - /** Returns the name of this processor. - */ + /** Returns the name of this processor. */ virtual const String getName() const = 0; //============================================================================== @@ -108,7 +98,7 @@ public: 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 + but you 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 @@ -135,6 +125,17 @@ public: virtual void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) = 0; + /** Renders the next block when the processor is being bypassed. + The default implementation of this method will pass-through any incoming audio, but + you may override this method e.g. to add latency compensation to the data to match + the processor's latency characteristics. This will avoid situations where bypassing + will shift the signal forward in time, possibly creating pre-echo effects and odd timings. + Another use for this method would be to cross-fade or morph between the wet (not bypassed) + and dry (bypassed) signals. + */ + virtual void processBlockBypassed (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages); + //============================================================================== /** Returns the current AudioPlayHead object that should be used to find out the state and position of the playhead. @@ -143,9 +144,9 @@ public: 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. + If the host hasn't supplied a playhead object, this will return nullptr. */ - AudioPlayHead* getPlayHead() const noexcept { return playHead; } + AudioPlayHead* getPlayHead() const noexcept { return playHead; } //============================================================================== @@ -154,7 +155,7 @@ public: 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 noexcept { return sampleRate; } + double getSampleRate() const noexcept { return sampleRate; } /** Returns the current typical block size that is being used. @@ -165,7 +166,7 @@ public: 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 noexcept { return blockSize; } + int getBlockSize() const noexcept { return blockSize; } //============================================================================== /** Returns the number of input channels that the host will be sending the filter. @@ -177,7 +178,7 @@ public: 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 noexcept { return numInputChannels; } + int getNumInputChannels() const noexcept { return numInputChannels; } /** Returns the number of output channels that the host will be sending the filter. @@ -188,18 +189,33 @@ public: 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 noexcept { return numOutputChannels; } + int getNumOutputChannels() const noexcept { return numOutputChannels; } - /** Returns the name of one of the input channels, as returned by the host. + /** Returns a string containing a whitespace-separated list of speaker types + corresponding to each input channel. + For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs" + If the speaker arrangement is unknown, the returned string will be empty. + */ + const String& getInputSpeakerArrangement() const noexcept { return inputSpeakerArrangement; } - The host might not supply very useful names for channels, and this might be + /** Returns a string containing a whitespace-separated list of speaker types + corresponding to each output channel. + For example in a 5.1 arrangement, the string may be "L R C Lfe Ls Rs" + If the speaker arrangement is unknown, the returned string will be empty. + */ + const String& getOutputSpeakerArrangement() const noexcept { return outputSpeakerArrangement; } + + //============================================================================== + /** Returns the name of one of the processor's input channels. + + The processor might not supply very useful names for channels, and this might be something like "1", "2", "left", "right", etc. */ virtual const String getInputChannelName (int channelIndex) const = 0; - /** Returns the name of one of the output channels, as returned by the host. + /** Returns the name of one of the processor's output channels. - The host might not supply very useful names for channels, and this might be + The processor might not supply very useful names for channels, and this might be something like "1", "2", "left", "right", etc. */ virtual const String getOutputChannelName (int channelIndex) const = 0; @@ -216,7 +232,7 @@ public: 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 noexcept { return latencySamples; } + int getLatencySamples() const noexcept { return latencySamples; } /** The filter should call this to set the number of samples delay that it introduces. @@ -225,6 +241,12 @@ public: */ void setLatencySamples (int newLatency); + /** Returns true if a silent input always produces a silent output. */ + virtual bool silenceInProducesSilenceOut() const = 0; + + /** Returns the length of the filter's tail, in seconds. */ + virtual double getTailLengthSeconds() const = 0; + /** Returns true if the processor wants midi messages. */ virtual bool acceptsMidi() const = 0; @@ -243,7 +265,7 @@ public: @see suspendProcessing */ - const CriticalSection& getCallbackLock() const noexcept { return callbackLock; } + const CriticalSection& getCallbackLock() const noexcept { return callbackLock; } /** Enables and disables the processing callback. @@ -300,17 +322,15 @@ public: */ bool isNonRealtime() const noexcept { return nonRealtime; } - /** Called by the host to tell this processor whether it's being used in a non-realime + /** Called by the host to tell this processor whether it's being used in a non-realtime capacity for offline rendering or bouncing. - - Whatever value is passed-in will be */ void setNonRealtime (bool isNonRealtime) noexcept; //============================================================================== /** Creates the filter's UI. - This can return 0 if you want a UI-less filter, in which case the host may create + This can return nullptr 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 @@ -344,14 +364,11 @@ public: //============================================================================== /** Returns the active editor, if there is one. - - Bear in mind this can return 0, even if an editor has previously been - opened. + Bear in mind this can return nullptr, even if an editor has previously been opened. */ AudioProcessorEditor* getActiveEditor() const noexcept { 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(); @@ -378,6 +395,47 @@ public: /** Returns the value of a parameter as a text string. */ virtual const String getParameterText (int parameterIndex) = 0; + /** Returns the name of a parameter as a text string with a preferred maximum length. + If you want to provide customised short versions of your parameter names that + will look better in constrained spaces (e.g. the displays on hardware controller + devices or mixing desks) then you should implement this method. + If you don't override it, the default implementation will call getParameterText(int), + and truncate the result. + */ + virtual String getParameterName (int parameterIndex, int maximumStringLength); + + /** Returns the value of a parameter as a text string with a preferred maximum length. + If you want to provide customised short versions of your parameter values that + will look better in constrained spaces (e.g. the displays on hardware controller + devices or mixing desks) then you should implement this method. + If you don't override it, the default implementation will call getParameterText(int), + and truncate the result. + */ + virtual String getParameterText (int parameterIndex, int maximumStringLength); + + /** Returns the number of discrete steps that this parameter can represent. + The default return value if you don't implement this method is 0x7fffffff. + If your parameter is boolean, then you may want to make this return 2. + The value that is returned may or may not be used, depending on the host. + */ + virtual int getParameterNumSteps (int parameterIndex); + + /** Returns the default value for the parameter. + By default, this just returns 0. + The value that is returned may or may not be used, depending on the host. + */ + virtual float getParameterDefaultValue (int parameterIndex); + + /** Some plugin types may be able to return a label string for a + parameter's units. + */ + virtual String getParameterLabel (int index) const; + + /** This can be overridden to tell the host that particular parameters operate in the + reverse direction. (Not all plugin formats or hosts will actually use this information). + */ + virtual bool isParameterOrientationInverted (int index) const; + /** 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 @@ -391,8 +449,7 @@ public: The value passed will be between 0 and 1.0. */ - virtual void setParameter (int parameterIndex, - float newValue) = 0; + virtual void setParameter (int parameterIndex, float newValue) = 0; /** Your filter can call this when it needs to change one of its parameters. @@ -404,20 +461,16 @@ public: 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); + 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; @@ -458,19 +511,16 @@ public: */ virtual int getNumPrograms() = 0; - /** Returns the number of the currently active program. - */ + /** Returns the number of the currently active program. */ virtual int getCurrentProgram() = 0; - /** Called by the host to change the current program. - */ + /** 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. - */ + /** Called by the host to rename a program. */ virtual void changeProgramName (int index, const String& newName) = 0; //============================================================================== @@ -524,31 +574,51 @@ public: */ virtual void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + /** This method is called when the number of input or output channels is changed. */ + virtual void numChannelsChanged(); //============================================================================== /** Adds a listener that will be called when an aspect of this processor changes. */ - void addListener (AudioProcessorListener* newListener); + virtual void addListener (AudioProcessorListener* newListener); /** Removes a previously added listener. */ - void removeListener (AudioProcessorListener* listenerToRemove); + virtual void removeListener (AudioProcessorListener* listenerToRemove); //============================================================================== /** Tells the processor to use this playhead object. The processor will not take ownership of the object, so the caller must delete it when it is no longer being used. */ - void setPlayHead (AudioPlayHead* newPlayHead) noexcept; + virtual void setPlayHead (AudioPlayHead* newPlayHead); + + //============================================================================== + /** This is called by the processor to specify its details before being played. */ + void setPlayConfigDetails (int numIns, int numOuts, double sampleRate, int blockSize) noexcept; //============================================================================== /** Not for public use - this is called before deleting an editor component. */ - void editorBeingDeleted (AudioProcessorEditor* editor) noexcept; + void editorBeingDeleted (AudioProcessorEditor*) noexcept; /** Not for public use - this is called to initialise the processor before playing. */ - void setPlayConfigDetails (int numIns, int numOuts, - double sampleRate, - int blockSize) noexcept; + void setSpeakerArrangement (const String& inputs, const String& outputs); + + /** Flags to indicate the type of plugin context in which a processor is being used. */ + enum WrapperType + { + wrapperType_Undefined = 0, + wrapperType_VST, + wrapperType_VST3, + wrapperType_AudioUnit, + wrapperType_RTAS, + wrapperType_AAX, + wrapperType_Standalone + }; + + /** When loaded by a plugin wrapper, this flag will be set to indicate the type + of plugin within which the processor is running. + */ + WrapperType wrapperType; -protected: //============================================================================== /** Helper function that just converts an xml element into a binary blob. @@ -563,11 +633,15 @@ protected: /** 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 + This might return nullptr 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, int sizeInBytes); + /** @internal */ + static void JUCE_CALLTYPE setTypeOfNextNewPlugin (WrapperType); + +protected: /** @internal */ AudioPlayHead* playHead; @@ -575,19 +649,22 @@ protected: void sendParamChangeMessageToListeners (int parameterIndex, float newValue); private: - Array listeners; + Array listeners; Component::SafePointer activeEditor; double sampleRate; int blockSize, numInputChannels, numOutputChannels, latencySamples; bool suspended, nonRealtime; CriticalSection callbackLock, listenerLock; + String inputSpeakerArrangement, outputSpeakerArrangement; #if JUCE_DEBUG BigInteger changingParams; #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor); + AudioProcessorListener* getListenerLocked (int) const noexcept; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessor) }; -#endif // __JUCE_AUDIOPROCESSOR_JUCEHEADER__ +#endif // JUCE_AUDIOPROCESSOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp index 7af9590..a4c9d29 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.cpp @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* const owner_) - : owner (owner_) +AudioProcessorEditor::AudioProcessorEditor (AudioProcessor* const p) + : owner (p) { // the filter must be valid.. jassert (owner != nullptr); diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h index 5c25232..3c811a2 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorEditor.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ -#define __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ - -class AudioProcessor; +#ifndef JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED +#define JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED //============================================================================== @@ -60,8 +57,8 @@ private: //============================================================================== AudioProcessor* const owner; - JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor); + JUCE_DECLARE_NON_COPYABLE (AudioProcessorEditor) }; -#endif // __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ +#endif // JUCE_AUDIOPROCESSOREDITOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index 8c6578b..9ca46d2 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -40,7 +39,7 @@ public: const OwnedArray & sharedMidiBuffers, const int numSamples) = 0; - JUCE_LEAK_DETECTOR (AudioGraphRenderingOp); + JUCE_LEAK_DETECTOR (AudioGraphRenderingOp) }; //============================================================================== @@ -59,7 +58,7 @@ public: private: const int channelNum; - JUCE_DECLARE_NON_COPYABLE (ClearChannelOp); + JUCE_DECLARE_NON_COPYABLE (ClearChannelOp) }; //============================================================================== @@ -79,7 +78,7 @@ public: private: const int srcChannelNum, dstChannelNum; - JUCE_DECLARE_NON_COPYABLE (CopyChannelOp); + JUCE_DECLARE_NON_COPYABLE (CopyChannelOp) }; //============================================================================== @@ -99,7 +98,7 @@ public: private: const int srcChannelNum, dstChannelNum; - JUCE_DECLARE_NON_COPYABLE (AddChannelOp); + JUCE_DECLARE_NON_COPYABLE (AddChannelOp) }; //============================================================================== @@ -118,7 +117,7 @@ public: private: const int bufferNum; - JUCE_DECLARE_NON_COPYABLE (ClearMidiBufferOp); + JUCE_DECLARE_NON_COPYABLE (ClearMidiBufferOp) }; //============================================================================== @@ -138,7 +137,7 @@ public: private: const int srcBufferNum, dstBufferNum; - JUCE_DECLARE_NON_COPYABLE (CopyMidiBufferOp); + JUCE_DECLARE_NON_COPYABLE (CopyMidiBufferOp) }; //============================================================================== @@ -159,7 +158,7 @@ public: private: const int srcBufferNum, dstBufferNum; - JUCE_DECLARE_NON_COPYABLE (AddMidiBufferOp); + JUCE_DECLARE_NON_COPYABLE (AddMidiBufferOp) }; //============================================================================== @@ -176,7 +175,7 @@ public: void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) { - float* data = sharedBufferChans.getSampleData (channel, 0); + float* data = sharedBufferChans.getWritePointer (channel, 0); for (int i = numSamples; --i >= 0;) { @@ -193,7 +192,7 @@ private: const int channel, bufferSize; int readIndex, writeIndex; - JUCE_DECLARE_NON_COPYABLE (DelayChannelOp); + JUCE_DECLARE_NON_COPYABLE (DelayChannelOp) }; @@ -220,7 +219,7 @@ public: void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray & sharedMidiBuffers, const int numSamples) { for (int i = totalChans; --i >= 0;) - channels[i] = sharedBufferChans.getSampleData (audioChannelsToUse.getUnchecked (i), 0); + channels[i] = sharedBufferChans.getWritePointer (audioChannelsToUse.getUnchecked (i), 0); AudioSampleBuffer buffer (channels, totalChans, numSamples); @@ -231,12 +230,12 @@ public: AudioProcessor* const processor; private: - Array audioChannelsToUse; - HeapBlock channels; + Array audioChannelsToUse; + HeapBlock channels; int totalChans; int midiBufferToUse; - JUCE_DECLARE_NON_COPYABLE (ProcessBufferOp); + JUCE_DECLARE_NON_COPYABLE (ProcessBufferOp) }; //============================================================================== @@ -665,8 +664,7 @@ private: void markAnyUnusedBuffersAsFree (const int stepIndex) { - int i; - for (i = 0; i < nodeIds.size(); ++i) + for (int i = 0; i < nodeIds.size(); ++i) { if (isNodeBusy (nodeIds.getUnchecked(i)) && ! isBufferNeededLater (stepIndex, -1, @@ -677,7 +675,7 @@ private: } } - for (i = 0; i < midiNodeIds.size(); ++i) + for (int i = 0; i < midiNodeIds.size(); ++i) { if (isNodeBusy (midiNodeIds.getUnchecked(i)) && ! isBufferNeededLater (stepIndex, -1, @@ -738,7 +736,7 @@ private: } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RenderingOpSequenceCalculator); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RenderingOpSequenceCalculator) }; //============================================================================== @@ -780,7 +778,7 @@ private: const uint32 destNodeId; SortedSet srcNodes; - JUCE_DECLARE_NON_COPYABLE (Entry); + JUCE_DECLARE_NON_COPYABLE (Entry) }; OwnedArray entries; @@ -790,9 +788,8 @@ private: int recursionCheck) const noexcept { int index; - const Entry* const entry = findEntry (possibleDestinationId, index); - if (entry != nullptr) + if (const Entry* const entry = findEntry (possibleDestinationId, index)) { const SortedSet& srcNodes = entry->srcNodes; @@ -850,7 +847,7 @@ private: return result; } - JUCE_DECLARE_NON_COPYABLE (ConnectionLookupTable); + JUCE_DECLARE_NON_COPYABLE (ConnectionLookupTable) }; //============================================================================== @@ -859,14 +856,14 @@ struct ConnectionSorter static int compareElements (const AudioProcessorGraph::Connection* const first, const AudioProcessorGraph::Connection* const second) noexcept { - if (first->sourceNodeId < second->sourceNodeId) return -1; - else if (first->sourceNodeId > second->sourceNodeId) return 1; - else if (first->destNodeId < second->destNodeId) return -1; - else if (first->destNodeId > second->destNodeId) return 1; - else if (first->sourceChannelIndex < second->sourceChannelIndex) return -1; - else if (first->sourceChannelIndex > second->sourceChannelIndex) return 1; - else if (first->destChannelIndex < second->destChannelIndex) return -1; - else if (first->destChannelIndex > second->destChannelIndex) return 1; + if (first->sourceNodeId < second->sourceNodeId) return -1; + if (first->sourceNodeId > second->sourceNodeId) return 1; + if (first->destNodeId < second->destNodeId) return -1; + if (first->destNodeId > second->destNodeId) return 1; + if (first->sourceChannelIndex < second->sourceChannelIndex) return -1; + if (first->sourceChannelIndex > second->sourceChannelIndex) return 1; + if (first->destChannelIndex < second->destChannelIndex) return -1; + if (first->destChannelIndex > second->destChannelIndex) return 1; return 0; } @@ -888,7 +885,7 @@ AudioProcessorGraph::Node::Node (const uint32 nodeId_, AudioProcessor* const pro processor (processor_), isPrepared (false) { - jassert (processor_ != nullptr); + jassert (processor != nullptr); } void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, @@ -918,18 +915,16 @@ void AudioProcessorGraph::Node::unprepare() void AudioProcessorGraph::Node::setParentGraph (AudioProcessorGraph* const graph) const { - AudioProcessorGraph::AudioGraphIOProcessor* const ioProc - = dynamic_cast (processor.get()); - - if (ioProc != nullptr) + if (AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (processor.get())) ioProc->setParentGraph (graph); } //============================================================================== AudioProcessorGraph::AudioProcessorGraph() : lastNodeId (0), - renderingBuffers (1, 1), - currentAudioOutputBuffer (1, 1) + currentAudioInputBuffer (nullptr), + currentMidiInputBuffer (nullptr) { } @@ -963,12 +958,21 @@ AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeI AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, uint32 nodeId) { - if (newProcessor == nullptr) + if (newProcessor == nullptr || newProcessor == this) { jassertfalse; return nullptr; } + for (int i = nodes.size(); --i >= 0;) + { + if (nodes.getUnchecked(i)->getProcessor() == newProcessor) + { + jassertfalse; // Cannot add the same object to the graph twice! + return nullptr; + } + } + if (nodeId == 0) { nodeId = ++lastNodeId; @@ -983,6 +987,8 @@ AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const n lastNodeId = nodeId; } + newProcessor->setPlayHead (getPlayHead()); + Node* const n = new Node (nodeId, newProcessor); nodes.add (n); triggerAsyncUpdate(); @@ -1171,8 +1177,8 @@ void AudioProcessorGraph::clearRenderingSequence() Array oldOps; { - const ScopedLock sl (renderLock); - renderingOps.swapWithArray (oldOps); + const ScopedLock sl (getCallbackLock()); + renderingOps.swapWith (oldOps); } deleteRenderOpArray (oldOps); @@ -1235,7 +1241,7 @@ void AudioProcessorGraph::buildRenderingSequence() { // swap over to the new rendering sequence.. - const ScopedLock sl (renderLock); + const ScopedLock sl (getCallbackLock()); renderingBuffers.setSize (numRenderingBuffersNeeded, getBlockSize()); renderingBuffers.clear(); @@ -1246,7 +1252,7 @@ void AudioProcessorGraph::buildRenderingSequence() while (midiBuffers.size() < numMidiBuffersNeeded) midiBuffers.add (new MidiBuffer()); - renderingOps.swapWithArray (newRenderingOps); + renderingOps.swapWith (newRenderingOps); } // delete the old ones.. @@ -1284,20 +1290,25 @@ void AudioProcessorGraph::releaseResources() currentMidiOutputBuffer.clear(); } +void AudioProcessorGraph::reset() +{ + const ScopedLock sl (getCallbackLock()); + + for (int i = 0; i < nodes.size(); ++i) + nodes.getUnchecked(i)->getProcessor()->reset(); +} + void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { const int numSamples = buffer.getNumSamples(); - const ScopedLock sl (renderLock); - currentAudioInputBuffer = &buffer; currentAudioOutputBuffer.setSize (jmax (1, buffer.getNumChannels()), numSamples); currentAudioOutputBuffer.clear(); currentMidiInputBuffer = &midiMessages; currentMidiOutputBuffer.clear(); - int i; - for (i = 0; i < renderingOps.size(); ++i) + for (int i = 0; i < renderingOps.size(); ++i) { GraphRenderingOps::AudioGraphRenderingOp* const op = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); @@ -1305,7 +1316,7 @@ void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& m op->perform (renderingBuffers, midiBuffers, numSamples); } - for (i = 0; i < buffer.getNumChannels(); ++i) + for (int i = 0; i < buffer.getNumChannels(); ++i) buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples); midiMessages.clear(); @@ -1324,6 +1335,8 @@ const String AudioProcessorGraph::getOutputChannelName (int channelIndex) const bool AudioProcessorGraph::isInputChannelStereoPair (int /*index*/) const { return true; } bool AudioProcessorGraph::isOutputChannelStereoPair (int /*index*/) const { return true; } +bool AudioProcessorGraph::silenceInProducesSilenceOut() const { return false; } +double AudioProcessorGraph::getTailLengthSeconds() const { return 0; } bool AudioProcessorGraph::acceptsMidi() const { return true; } bool AudioProcessorGraph::producesMidi() const { return true; } void AudioProcessorGraph::getStateInformation (juce::MemoryBlock& /*destData*/) {} @@ -1352,7 +1365,7 @@ const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const default: break; } - return String::empty; + return String(); } void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const @@ -1425,6 +1438,16 @@ void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer } } +bool AudioProcessorGraph::AudioGraphIOProcessor::silenceInProducesSilenceOut() const +{ + return isOutput(); +} + +double AudioProcessorGraph::AudioGraphIOProcessor::getTailLengthSeconds() const +{ + return 0; +} + bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const { return type == midiOutputNode; @@ -1444,7 +1467,7 @@ const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (in default: break; } - return String::empty; + return String(); } const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (int channelIndex) const @@ -1456,7 +1479,7 @@ const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (i default: break; } - return String::empty; + return String(); } bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const @@ -1476,17 +1499,17 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::hasEditor() const AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() { return nullptr; } int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String(); } float AudioProcessorGraph::AudioGraphIOProcessor::getParameter (int) { return 0.0f; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String::empty; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String(); } void AudioProcessorGraph::AudioGraphIOProcessor::setParameter (int, float) { } int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; } int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; } void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { } -const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String::empty; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String(); } void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) {} void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (juce::MemoryBlock&) {} diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h index b224c2f..d944682 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h @@ -1,34 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ -#define __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ - -#include "juce_AudioProcessor.h" -#include "../format/juce_AudioPluginFormatManager.h" -#include "../scanning/juce_KnownPluginList.h" +#ifndef JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED +#define JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED //============================================================================== @@ -46,12 +41,11 @@ AudioProcessorPlayer object. */ class JUCE_API AudioProcessorGraph : public AudioProcessor, - public AsyncUpdater + private AsyncUpdater { public: //============================================================================== - /** Creates an empty graph. - */ + /** Creates an empty graph. */ AudioProcessorGraph(); /** Destructor. @@ -101,7 +95,7 @@ public: void prepare (double sampleRate, int blockSize, AudioProcessorGraph*); void unprepare(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Node) }; //============================================================================== @@ -147,12 +141,11 @@ public: private: //============================================================================== - JUCE_LEAK_DETECTOR (Connection); + JUCE_LEAK_DETECTOR (Connection) }; //============================================================================== /** Deletes all nodes and connections from this graph. - Any processor objects in the graph will be deleted. */ void clear(); @@ -199,7 +192,6 @@ public: 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 nullptr. */ const Connection* getConnectionBetween (uint32 sourceNodeId, @@ -213,8 +205,7 @@ public: bool isConnected (uint32 possibleSourceNodeId, uint32 possibleDestNodeId) const; - /** Returns true if it would be legal to connect the specified points. - */ + /** Returns true if it would be legal to connect the specified points. */ bool canConnect (uint32 sourceNodeId, int sourceChannelIndex, uint32 destNodeId, int destChannelIndex) const; @@ -226,21 +217,16 @@ public: 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. - */ + /** Deletes the connection with the specified index. */ 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. - */ + /** Removes all connections from the specified node. */ bool disconnectNode (uint32 nodeId); /** Returns true if the given connection's channel numbers map on to valid @@ -329,6 +315,8 @@ public: const String getOutputChannelName (int channelIndex) const; bool isInputChannelStereoPair (int index) const; bool isOutputChannelStereoPair (int index) const; + bool silenceInProducesSilenceOut() const; + double getTailLengthSeconds() const; bool acceptsMidi() const; bool producesMidi() const; @@ -357,7 +345,7 @@ public: const IODeviceType type; AudioProcessorGraph* graph; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioGraphIOProcessor) }; //============================================================================== @@ -368,11 +356,14 @@ public: void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); void releaseResources(); void processBlock (AudioSampleBuffer&, MidiBuffer&); + void reset(); const String getInputChannelName (int channelIndex) const; const String getOutputChannelName (int channelIndex) const; bool isInputChannelStereoPair (int index) const; bool isOutputChannelStereoPair (int index) const; + bool silenceInProducesSilenceOut() const; + double getTailLengthSeconds() const; bool acceptsMidi() const; bool producesMidi() const; @@ -381,32 +372,27 @@ public: AudioProcessorEditor* createEditor() { return nullptr; } int getNumParameters() { return 0; } - const String getParameterName (int) { return String::empty; } + const String getParameterName (int) { return String(); } float getParameter (int) { return 0; } - const String getParameterText (int) { return String::empty; } + const String getParameterText (int) { return String(); } void setParameter (int, float) { } int getNumPrograms() { return 0; } int getCurrentProgram() { return 0; } void setCurrentProgram (int) { } - const String getProgramName (int) { return String::empty; } + const String getProgramName (int) { return String(); } void changeProgramName (int, const String&) { } - void getStateInformation (juce::MemoryBlock& destData); + void getStateInformation (juce::MemoryBlock&); void setStateInformation (const void* data, int sizeInBytes); - /** @internal */ - void handleAsyncUpdate(); - private: //============================================================================== - ReferenceCountedArray nodes; - OwnedArray connections; + ReferenceCountedArray nodes; + OwnedArray connections; uint32 lastNodeId; AudioSampleBuffer renderingBuffers; - OwnedArray midiBuffers; - - CriticalSection renderLock; + OwnedArray midiBuffers; Array renderingOps; friend class AudioGraphIOProcessor; @@ -415,13 +401,13 @@ private: MidiBuffer* currentMidiInputBuffer; MidiBuffer currentMidiOutputBuffer; + void handleAsyncUpdate() override; void clearRenderingSequence(); void buildRenderingSequence(); - bool isAnInputTo (uint32 possibleInputId, uint32 possibleDestinationId, int recursionCheck) const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioProcessorGraph) }; -#endif // __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ +#endif // JUCE_AUDIOPROCESSORGRAPH_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h index 55a39ff..d8ea93c 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorListener.h @@ -1,32 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ -#define __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ +#ifndef JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED +#define JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED -class AudioProcessor; //============================================================================== /** @@ -105,4 +103,4 @@ public: int parameterIndex); }; -#endif // __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ +#endif // JUCE_AUDIOPROCESSORLISTENER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp index 2cea534..c94411d 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp @@ -1,43 +1,42 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ class ProcessorParameterPropertyComp : public PropertyComponent, - public AudioProcessorListener, - public Timer + private AudioProcessorListener, + private Timer { public: - ProcessorParameterPropertyComp (const String& name, AudioProcessor& owner_, const int index_) + ProcessorParameterPropertyComp (const String& name, AudioProcessor& p, int paramIndex) : PropertyComponent (name), - owner (owner_), - index (index_), + owner (p), + index (paramIndex), paramHasChanged (false), - slider (owner_, index_) + slider (p, paramIndex) { startTimer (100); - addAndMakeVisible (&slider); - owner_.addListener (this); + addAndMakeVisible (slider); + owner.addListener (this); } ~ProcessorParameterPropertyComp() @@ -45,21 +44,25 @@ public: owner.removeListener (this); } - void refresh() + void refresh() override { paramHasChanged = false; - slider.setValue (owner.getParameter (index), false); + + if (slider.getThumbBeingDragged() < 0) + slider.setValue (owner.getParameter (index), dontSendNotification); + + slider.updateText(); } - void audioProcessorChanged (AudioProcessor*) {} + void audioProcessorChanged (AudioProcessor*) override {} - void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) + void audioProcessorParameterChanged (AudioProcessor*, int parameterIndex, float) override { if (parameterIndex == index) paramHasChanged = true; } - void timerCallback() + void timerCallback() override { if (paramHasChanged) { @@ -77,27 +80,34 @@ private: class ParamSlider : public Slider { public: - ParamSlider (AudioProcessor& owner_, const int index_) - : owner (owner_), - index (index_) + ParamSlider (AudioProcessor& p, int paramIndex) : owner (p), index (paramIndex) { - setRange (0.0, 1.0, 0.0); + const int steps = owner.getParameterNumSteps (index); + + if (steps > 1 && steps < 0x7fffffff) + setRange (0.0, 1.0, 1.0 / (steps - 1.0)); + else + setRange (0.0, 1.0); + setSliderStyle (Slider::LinearBar); setTextBoxIsEditable (false); - setScrollWheelEnabled (false); + setScrollWheelEnabled (true); } - void valueChanged() + void valueChanged() override { const float newVal = (float) getValue(); if (owner.getParameter (index) != newVal) - owner.setParameter (index, newVal); + { + owner.setParameterNotifyingHost (index, newVal); + updateText(); + } } - String getTextFromValue (double /*value*/) + String getTextFromValue (double /*value*/) override { - return owner.getParameterText (index); + return owner.getParameterText (index) + " " + owner.getParameterLabel (index).trimEnd(); } private: @@ -105,7 +115,7 @@ private: AudioProcessor& owner; const int index; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamSlider); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ParamSlider) }; AudioProcessor& owner; @@ -113,31 +123,31 @@ private: bool volatile paramHasChanged; ParamSlider slider; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorParameterPropertyComp); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProcessorParameterPropertyComp) }; //============================================================================== -GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const owner_) - : AudioProcessorEditor (owner_) +GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const p) + : AudioProcessorEditor (p) { - jassert (owner_ != nullptr); + jassert (p != nullptr); setOpaque (true); - addAndMakeVisible (&panel); + addAndMakeVisible (panel); Array params; - const int numParams = owner_->getNumParameters(); + const int numParams = p->getNumParameters(); int totalHeight = 0; for (int i = 0; i < numParams; ++i) { - String name (owner_->getParameterName (i)); + String name (p->getParameterName (i)); if (name.trim().isEmpty()) name = "Unnamed"; - ProcessorParameterPropertyComp* const pc = new ProcessorParameterPropertyComp (name, *owner_, i); + ProcessorParameterPropertyComp* const pc = new ProcessorParameterPropertyComp (name, *p, i); params.add (pc); totalHeight += pc->getPreferredHeight(); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h index 65b9699..1189f88 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ -#define __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ - -#include "juce_AudioProcessorEditor.h" +#ifndef JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED +#define JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED //============================================================================== @@ -47,15 +44,15 @@ public: ~GenericAudioProcessorEditor(); //============================================================================== - void paint (Graphics& g); - void resized(); + void paint (Graphics&) override; + void resized() override; private: //============================================================================== PropertyPanel panel; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GenericAudioProcessorEditor) }; -#endif // __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ +#endif // JUCE_GENERICAUDIOPROCESSOREDITOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.cpp index a21b316..8e1010f 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -27,7 +26,8 @@ PluginDescription::PluginDescription() : uid (0), isInstrument (false), numInputChannels (0), - numOutputChannels (0) + numOutputChannels (0), + hasSharedContainer (false) { } @@ -47,7 +47,8 @@ PluginDescription::PluginDescription (const PluginDescription& other) uid (other.uid), isInstrument (other.isInstrument), numInputChannels (other.numInputChannels), - numOutputChannels (other.numOutputChannels) + numOutputChannels (other.numOutputChannels), + hasSharedContainer (other.hasSharedContainer) { } @@ -65,6 +66,7 @@ PluginDescription& PluginDescription::operator= (const PluginDescription& other) lastFileModTime = other.lastFileModTime; numInputChannels = other.numInputChannels; numOutputChannels = other.numOutputChannels; + hasSharedContainer = other.hasSharedContainer; return *this; } @@ -100,6 +102,7 @@ XmlElement* PluginDescription::createXml() const e->setAttribute ("fileTime", String::toHexString (lastFileModTime.toMilliseconds())); e->setAttribute ("numInputs", numInputChannels); e->setAttribute ("numOutputs", numOutputChannels); + e->setAttribute ("isShell", hasSharedContainer); return e; } @@ -120,6 +123,7 @@ bool PluginDescription::loadFromXml (const XmlElement& xml) lastFileModTime = Time (xml.getStringAttribute ("fileTime").getHexValue64()); numInputChannels = xml.getIntAttribute ("numInputs"); numOutputChannels = xml.getIntAttribute ("numOutputs"); + hasSharedContainer = xml.getBoolAttribute ("isShell", false); return true; } diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.h index dd2f657..6361775 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_PluginDescription.h @@ -1,37 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ -#define __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ +#ifndef JUCE_PLUGINDESCRIPTION_H_INCLUDED +#define JUCE_PLUGINDESCRIPTION_H_INCLUDED //============================================================================== /** - A small class to represent some facts about a particular type of plugin. + A small class to represent some facts about a particular type of plug-in. - This class is for storing and managing the details about a plugin without + This class is for storing and managing the details about a plug-in without actually having to load an instance of it. A KnownPluginList contains a list of PluginDescription objects. @@ -48,21 +47,19 @@ public: ~PluginDescription(); //============================================================================== - /** The name of the plugin. */ + /** The name of the plug-in. */ String name; - /** A more descriptive name for the plugin. - This may be the same as the 'name' field, but some plugins may provide an + /** A more descriptive name for the plug-in. + This may be the same as the 'name' field, but some plug-ins may provide an alternative name. */ String descriptiveName; - /** The plugin format, e.g. "VST", "AudioUnit", etc. - */ + /** The plug-in format, e.g. "VST", "AudioUnit", etc. */ String pluginFormatName; - /** A category, such as "Dynamics", "Reverbs", etc. - */ + /** A category, such as "Dynamics", "Reverbs", etc. */ String category; /** The manufacturer. */ @@ -71,20 +68,20 @@ public: /** The version. This string doesn't have any particular format. */ String version; - /** Either the file containing the plugin module, or some other unique way + /** Either the file containing the plug-in 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. + could use to retrieve the plug-in. 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. + /** The last time the plug-in file was changed. + This is handy when scanning for new or changed plug-ins. */ Time lastFileModTime; - /** A unique ID for the plugin. + /** A unique ID for the plug-in. Note that this might not be unique between formats, e.g. a VST and some other format might actually have the same id. @@ -93,7 +90,7 @@ public: */ int uid; - /** True if the plugin identifies itself as a synthesiser. */ + /** True if the plug-in identifies itself as a synthesiser. */ bool isInstrument; /** The number of inputs. */ @@ -102,10 +99,13 @@ public: /** The number of outputs. */ int numOutputChannels; - /** Returns true if the two descriptions refer the the same plugin. + /** True if the plug-in is part of a multi-type container, e.g. a VST Shell. */ + bool hasSharedContainer; + + /** Returns true if the two descriptions refer to the same plug-in. This isn't quite as simple as them just having the same file (because of - shell plugins). + shell plug-ins). */ bool isDuplicateOf (const PluginDescription& other) const; @@ -114,7 +114,7 @@ public: 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 + plug-in's file location, so can be used to store a plug-in ID for use across different machines. */ String createIdentifierString() const; @@ -129,15 +129,15 @@ public: /** 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. + Returns true if the XML was a valid plug-in description. */ bool loadFromXml (const XmlElement& xml); private: //============================================================================== - JUCE_LEAK_DETECTOR (PluginDescription); + JUCE_LEAK_DETECTOR (PluginDescription) }; -#endif // __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ +#endif // JUCE_PLUGINDESCRIPTION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp index 5f308e0..afbf23e 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -68,7 +67,7 @@ bool KnownPluginList::addType (const PluginDescription& type) } } - types.add (new PluginDescription (type)); + types.insert (0, new PluginDescription (type)); sendChangeMessage(); return true; } @@ -79,27 +78,10 @@ void KnownPluginList::removeType (const int index) sendChangeMessage(); } -namespace +bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier, + AudioPluginFormat& formatToUse) const { - Time getPluginFileModTime (const String& fileOrIdentifier) - { - if (fileOrIdentifier.startsWithChar ('/') || fileOrIdentifier[1] == ':') - return File (fileOrIdentifier).getLastModificationTime(); - - return Time(); - } - - bool timesAreDifferent (const Time& t1, const Time& t2) noexcept - { - return t1 != t2 || t1 == Time(); - } - - enum { menuIdBase = 0x324503f4 }; -} - -bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier) const -{ - if (getTypeForFile (fileOrIdentifier) == 0) + if (getTypeForFile (fileOrIdentifier) == nullptr) return false; for (int i = types.size(); --i >= 0;) @@ -107,21 +89,24 @@ bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier) const const PluginDescription* const d = types.getUnchecked(i); if (d->fileOrIdentifier == fileOrIdentifier - && timesAreDifferent (d->lastFileModTime, getPluginFileModTime (fileOrIdentifier))) - { + && formatToUse.pluginNeedsRescanning (*d)) return false; - } } return true; } +void KnownPluginList::setCustomScanner (CustomScanner* newScanner) +{ + scanner = newScanner; +} + bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, const bool dontRescanIfAlreadyInList, OwnedArray & typesFound, AudioPluginFormat& format) { - bool addedOne = false; + const ScopedLock sl (scanLock); if (dontRescanIfAlreadyInList && getTypeForFile (fileOrIdentifier) != nullptr) @@ -134,7 +119,7 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, if (d->fileOrIdentifier == fileOrIdentifier && d->pluginFormatName == format.getName()) { - if (timesAreDifferent (d->lastFileModTime, getPluginFileModTime (fileOrIdentifier))) + if (format.pluginNeedsRescanning (*d)) needsRescanning = true; else typesFound.add (new PluginDescription (*d)); @@ -145,59 +130,127 @@ bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, return false; } + if (blacklist.contains (fileOrIdentifier)) + return false; + OwnedArray found; - format.findAllTypesForFile (found, fileOrIdentifier); + + { + const ScopedUnlock sl2 (scanLock); + + if (scanner != nullptr) + { + if (! scanner->findPluginTypesFor (format, found, fileOrIdentifier)) + addToBlacklist (fileOrIdentifier); + } + else + { + format.findAllTypesForFile (found, fileOrIdentifier); + } + } for (int i = 0; i < found.size(); ++i) { PluginDescription* const desc = found.getUnchecked(i); jassert (desc != nullptr); - if (addType (*desc)) - addedOne = true; - + addType (*desc); typesFound.add (new PluginDescription (*desc)); } - return addedOne; + return found.size() > 0; } -void KnownPluginList::scanAndAddDragAndDroppedFiles (const StringArray& files, +void KnownPluginList::scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager, + const StringArray& files, OwnedArray & typesFound) { for (int i = 0; i < files.size(); ++i) { - for (int j = 0; j < AudioPluginFormatManager::getInstance()->getNumFormats(); ++j) + const String filenameOrID (files[i]); + bool found = false; + + for (int j = 0; j < formatManager.getNumFormats(); ++j) { - AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (j); - - if (scanAndAddFile (files[i], true, typesFound, *format)) - return; - } - - const File f (files[i]); - - if (f.isDirectory()) - { - StringArray s; + AudioPluginFormat* const format = formatManager.getFormat (j); + if (format->fileMightContainThisPluginType (filenameOrID) + && scanAndAddFile (filenameOrID, true, typesFound, *format)) { - Array subFiles; - f.findChildFiles (subFiles, File::findFilesAndDirectories, false); - - for (int j = 0; j < subFiles.size(); ++j) - s.add (subFiles.getReference(j).getFullPathName()); + found = true; + break; } - - scanAndAddDragAndDroppedFiles (s, typesFound); } + + if (! found) + { + const File f (filenameOrID); + + if (f.isDirectory()) + { + StringArray s; + + { + Array subFiles; + f.findChildFiles (subFiles, File::findFilesAndDirectories, false); + + for (int j = 0; j < subFiles.size(); ++j) + s.add (subFiles.getReference(j).getFullPathName()); + } + + scanAndAddDragAndDroppedFiles (formatManager, s, typesFound); + } + } + } + + scanFinished(); +} + +void KnownPluginList::scanFinished() +{ + if (scanner != nullptr) + scanner->scanFinished(); +} + +const StringArray& KnownPluginList::getBlacklistedFiles() const +{ + return blacklist; +} + +void KnownPluginList::addToBlacklist (const String& pluginID) +{ + if (! blacklist.contains (pluginID)) + { + blacklist.add (pluginID); + sendChangeMessage(); + } +} + +void KnownPluginList::removeFromBlacklist (const String& pluginID) +{ + const int index = blacklist.indexOf (pluginID); + + if (index >= 0) + { + blacklist.remove (index); + sendChangeMessage(); + } +} + +void KnownPluginList::clearBlacklistedFiles() +{ + if (blacklist.size() > 0) + { + blacklist.clear(); + sendChangeMessage(); } } //============================================================================== struct PluginSorter { - PluginSorter (KnownPluginList::SortMethod method_) noexcept : method (method_) {} + PluginSorter (KnownPluginList::SortMethod sortMethod, bool forwards) noexcept + : method (sortMethod), direction (forwards ? 1 : -1) {} int compareElements (const PluginDescription* const first, const PluginDescription* const second) const @@ -208,6 +261,7 @@ struct PluginSorter { case KnownPluginList::sortByCategory: diff = first->category.compareLexicographically (second->category); break; case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareLexicographically (second->manufacturerName); break; + case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break; case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break; default: break; } @@ -215,7 +269,7 @@ struct PluginSorter if (diff == 0) diff = first->name.compareLexicographically (second->name); - return diff; + return diff * direction; } private: @@ -224,17 +278,26 @@ private: return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false); } - KnownPluginList::SortMethod method; + const KnownPluginList::SortMethod method; + const int direction; + + JUCE_DECLARE_NON_COPYABLE (PluginSorter) }; -void KnownPluginList::sort (const SortMethod method) +void KnownPluginList::sort (const SortMethod method, bool forwards) { if (method != defaultOrder) { - PluginSorter sorter (method); + Array oldOrder, newOrder; + oldOrder.addArray (types); + + PluginSorter sorter (method, forwards); types.sort (sorter, true); - sendChangeMessage(); + newOrder.addArray (types); + + if (oldOrder != newOrder) + sendChangeMessage(); } } @@ -243,8 +306,11 @@ XmlElement* KnownPluginList::createXml() const { XmlElement* const e = new XmlElement ("KNOWNPLUGINS"); - for (int i = 0; i < types.size(); ++i) - e->addChildElement (types.getUnchecked(i)->createXml()); + for (int i = types.size(); --i >= 0;) + e->prependChildElement (types.getUnchecked(i)->createXml()); + + for (int i = 0; i < blacklist.size(); ++i) + e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", blacklist[i]); return e; } @@ -252,6 +318,7 @@ XmlElement* KnownPluginList::createXml() const void KnownPluginList::recreateFromXml (const XmlElement& xml) { clear(); + clearBlacklistedFiles(); if (xml.hasTagName ("KNOWNPLUGINS")) { @@ -259,178 +326,224 @@ void KnownPluginList::recreateFromXml (const XmlElement& xml) { PluginDescription info; - if (info.loadFromXml (*e)) + if (e->hasTagName ("BLACKLISTED")) + blacklist.add (e->getStringAttribute ("id")); + else if (info.loadFromXml (*e)) addType (info); } } } //============================================================================== -// This is used to turn a bunch of paths into a nested menu structure. -class PluginFilesystemTree +struct PluginTreeUtils { -public: - void buildTree (const Array & allPlugins) + enum { menuIdBase = 0x324503f4 }; + + static void buildTreeByFolder (KnownPluginList::PluginTree& tree, const Array & allPlugins) { for (int i = 0; i < allPlugins.size(); ++i) { - String path (allPlugins.getUnchecked(i) - ->fileOrIdentifier.replaceCharacter ('\\', '/') - .upToLastOccurrenceOf ("/", false, false)); + PluginDescription* const pd = allPlugins.getUnchecked (i); + + String path (pd->fileOrIdentifier.replaceCharacter ('\\', '/') + .upToLastOccurrenceOf ("/", false, false)); if (path.substring (1, 2) == ":") path = path.substring (2); - addPlugin (allPlugins.getUnchecked(i), path); + addPlugin (tree, pd, path); } - optimise(); + optimiseFolders (tree, false); } - void addToMenu (PopupMenu& m, const OwnedArray & allPlugins) const + static void optimiseFolders (KnownPluginList::PluginTree& tree, bool concatenateName) { - int i; - for (i = 0; i < subFolders.size(); ++i) + for (int i = tree.subFolders.size(); --i >= 0;) { - const PluginFilesystemTree* const sub = subFolders.getUnchecked(i); + KnownPluginList::PluginTree& sub = *tree.subFolders.getUnchecked(i); + optimiseFolders (sub, concatenateName || (tree.subFolders.size() > 1)); - PopupMenu subMenu; - sub->addToMenu (subMenu, allPlugins); + if (sub.plugins.size() == 0) + { + for (int j = 0; j < sub.subFolders.size(); ++j) + { + KnownPluginList::PluginTree* const s = sub.subFolders.getUnchecked(j); - #if JUCE_MAC - // avoid the special AU formatting nonsense on Mac.. - m.addSubMenu (sub->folder.fromFirstOccurrenceOf (":", false, false), subMenu); - #else - m.addSubMenu (sub->folder, subMenu); - #endif - } + if (concatenateName) + s->folder = sub.folder + "/" + s->folder; - for (i = 0; i < plugins.size(); ++i) - { - PluginDescription* const plugin = plugins.getUnchecked(i); + tree.subFolders.add (s); + } - m.addItem (allPlugins.indexOf (plugin) + menuIdBase, - plugin->name, true, false); + sub.subFolders.clear (false); + tree.subFolders.remove (i); + } } } -private: - String folder; - OwnedArray subFolders; - Array plugins; + static void buildTreeByCategory (KnownPluginList::PluginTree& tree, + const Array & sorted, + const KnownPluginList::SortMethod sortMethod) + { + String lastType; + ScopedPointer current (new KnownPluginList::PluginTree()); - void addPlugin (PluginDescription* const pd, const String& path) + for (int i = 0; i < sorted.size(); ++i) + { + const PluginDescription* const pd = sorted.getUnchecked(i); + String thisType (sortMethod == KnownPluginList::sortByCategory ? pd->category + : pd->manufacturerName); + + if (! thisType.containsNonWhitespaceChars()) + thisType = "Other"; + + if (thisType != lastType) + { + if (current->plugins.size() + current->subFolders.size() > 0) + { + current->folder = lastType; + tree.subFolders.add (current.release()); + current = new KnownPluginList::PluginTree(); + } + + lastType = thisType; + } + + current->plugins.add (pd); + } + + if (current->plugins.size() + current->subFolders.size() > 0) + { + current->folder = lastType; + tree.subFolders.add (current.release()); + } + } + + static void addPlugin (KnownPluginList::PluginTree& tree, PluginDescription* const pd, String path) { if (path.isEmpty()) { - plugins.add (pd); + tree.plugins.add (pd); } else { - const String firstSubFolder (path.upToFirstOccurrenceOf ("/", false, false)); - const String remainingPath (path.fromFirstOccurrenceOf ("/", false, false)); + #if JUCE_MAC + if (path.containsChar (':')) + path = path.fromFirstOccurrenceOf (":", false, false); // avoid the special AU formatting nonsense on Mac.. + #endif - for (int i = subFolders.size(); --i >= 0;) + const String firstSubFolder (path.upToFirstOccurrenceOf ("/", false, false)); + const String remainingPath (path.fromFirstOccurrenceOf ("/", false, false)); + + for (int i = tree.subFolders.size(); --i >= 0;) { - if (subFolders.getUnchecked(i)->folder.equalsIgnoreCase (firstSubFolder)) + KnownPluginList::PluginTree& subFolder = *tree.subFolders.getUnchecked(i); + + if (subFolder.folder.equalsIgnoreCase (firstSubFolder)) { - subFolders.getUnchecked(i)->addPlugin (pd, remainingPath); + addPlugin (subFolder, pd, remainingPath); return; } } - PluginFilesystemTree* const newFolder = new PluginFilesystemTree(); + KnownPluginList::PluginTree* const newFolder = new KnownPluginList::PluginTree(); newFolder->folder = firstSubFolder; - subFolders.add (newFolder); - - newFolder->addPlugin (pd, remainingPath); + tree.subFolders.add (newFolder); + addPlugin (*newFolder, pd, remainingPath); } } - // removes any deeply nested folders that don't contain any actual plugins - void optimise() + static bool containsDuplicateNames (const Array& plugins, const String& name) { - for (int i = subFolders.size(); --i >= 0;) + int matches = 0; + + for (int i = 0; i < plugins.size(); ++i) + if (plugins.getUnchecked(i)->name == name) + if (++matches > 1) + return true; + + return false; + } + + static void addToMenu (const KnownPluginList::PluginTree& tree, PopupMenu& m, const OwnedArray & allPlugins) + { + for (int i = 0; i < tree.subFolders.size(); ++i) { - PluginFilesystemTree* const sub = subFolders.getUnchecked(i); + const KnownPluginList::PluginTree& sub = *tree.subFolders.getUnchecked(i); - sub->optimise(); + PopupMenu subMenu; + addToMenu (sub, subMenu, allPlugins); + m.addSubMenu (sub.folder, subMenu); + } - if (sub->plugins.size() == 0) - { - for (int j = 0; j < sub->subFolders.size(); ++j) - subFolders.add (sub->subFolders.getUnchecked(j)); + for (int i = 0; i < tree.plugins.size(); ++i) + { + const PluginDescription* const plugin = tree.plugins.getUnchecked(i); - sub->subFolders.clear (false); - subFolders.remove (i); - } + String name (plugin->name); + + if (containsDuplicateNames (tree.plugins, name)) + name << " (" << plugin->pluginFormatName << ')'; + + m.addItem (allPlugins.indexOf (plugin) + menuIdBase, name, true, false); } } }; -//============================================================================== -void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const +KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortMethod) const { Array sorted; { - PluginSorter sorter (sortMethod); + PluginSorter sorter (sortMethod, true); for (int i = 0; i < types.size(); ++i) sorted.addSorted (sorter, types.getUnchecked(i)); } - if (sortMethod == sortByCategory - || sortMethod == sortByManufacturer) + PluginTree* tree = new PluginTree(); + + if (sortMethod == sortByCategory || sortMethod == sortByManufacturer || sortMethod == sortByFormat) { - String lastSubMenuName; - PopupMenu sub; - - for (int i = 0; i < sorted.size(); ++i) - { - const PluginDescription* const pd = sorted.getUnchecked(i); - String thisSubMenuName (sortMethod == sortByCategory ? pd->category - : pd->manufacturerName); - - if (! thisSubMenuName.containsNonWhitespaceChars()) - thisSubMenuName = "Other"; - - if (thisSubMenuName != lastSubMenuName) - { - if (sub.getNumItems() > 0) - { - menu.addSubMenu (lastSubMenuName, sub); - sub.clear(); - } - - lastSubMenuName = thisSubMenuName; - } - - sub.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false); - } - - if (sub.getNumItems() > 0) - menu.addSubMenu (lastSubMenuName, sub); + PluginTreeUtils::buildTreeByCategory (*tree, sorted, sortMethod); } else if (sortMethod == sortByFileSystemLocation) { - PluginFilesystemTree root; - root.buildTree (sorted); - root.addToMenu (menu, types); + PluginTreeUtils::buildTreeByFolder (*tree, sorted); } else { for (int i = 0; i < sorted.size(); ++i) - { - const PluginDescription* const pd = sorted.getUnchecked(i); - menu.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false); - } + tree->plugins.add (sorted.getUnchecked(i)); } + + return tree; +} + +//============================================================================== +void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const +{ + ScopedPointer tree (createTree (sortMethod)); + PluginTreeUtils::addToMenu (*tree, menu, types); } int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const { - const int i = menuResultCode - menuIdBase; - + const int i = menuResultCode - PluginTreeUtils::menuIdBase; return isPositiveAndBelow (i, types.size()) ? i : -1; } + +//============================================================================== +KnownPluginList::CustomScanner::CustomScanner() {} +KnownPluginList::CustomScanner::~CustomScanner() {} + +void KnownPluginList::CustomScanner::scanFinished() {} + +bool KnownPluginList::CustomScanner::shouldExit() const noexcept +{ + if (ThreadPoolJob* job = ThreadPoolJob::getCurrentThreadPoolJob()) + return job->shouldExit(); + + return false; +} diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h index dee4b73..3c8df25 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ -#define __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ - -#include "../processors/juce_PluginDescription.h" -#include "../format/juce_AudioPluginFormat.h" +#ifndef JUCE_KNOWNPLUGINLIST_H_INCLUDED +#define JUCE_KNOWNPLUGINLIST_H_INCLUDED //============================================================================== @@ -43,8 +39,7 @@ class JUCE_API KnownPluginList : public ChangeBroadcaster { public: //============================================================================== - /** Creates an empty list. - */ + /** Creates an empty list. */ KnownPluginList(); /** Destructor. */ @@ -64,8 +59,12 @@ public: */ PluginDescription* getType (int index) const noexcept { return types [index]; } - /** Looks for a type in the list which comes from this file. - */ + /** Type iteration. */ + PluginDescription** begin() const noexcept { return types.begin(); } + /** Type iteration. */ + PluginDescription** end() const noexcept { return types.end(); } + + /** Looks for a type in the list which comes from this file. */ PluginDescription* getTypeForFile (const String& fileOrIdentifier) const; /** Looks for a type in the list which matches a plugin type ID. @@ -98,18 +97,35 @@ public: OwnedArray & typesFound, AudioPluginFormat& formatToUse); + /** Tells a custom scanner that a scan has finished, and it can release any resources. */ + void scanFinished(); + /** 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; + bool isListingUpToDate (const String& possiblePluginFileOrIdentifier, + AudioPluginFormat& formatToUse) const; /** 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, + void scanAndAddDragAndDroppedFiles (AudioPluginFormatManager& formatManager, + const StringArray& filenames, OwnedArray & typesFound); + //============================================================================== + /** Returns the list of blacklisted files. */ + const StringArray& getBlacklistedFiles() const; + + /** Adds a plugin ID to the black-list. */ + void addToBlacklist (const String& pluginID); + + /** Removes a plugin ID from the black-list. */ + void removeFromBlacklist (const String& pluginID); + + /** Clears all the blacklisted files. */ + void clearBlacklistedFiles(); + //============================================================================== /** Sort methods used to change the order of the plugins in the list. */ @@ -119,6 +135,7 @@ public: sortAlphabetically, sortByCategory, sortByManufacturer, + sortByFormat, sortByFileSystemLocation }; @@ -130,20 +147,17 @@ public: Use getIndexChosenByMenu() to find out the type that was chosen. */ - void addToMenu (PopupMenu& menu, - const SortMethod sortMethod) const; + void addToMenu (PopupMenu& menu, 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); + void sort (SortMethod method, bool forwards); //============================================================================== /** Creates some XML that can be used to store the state of this list. */ @@ -152,13 +166,58 @@ public: /** Recreates the state of this list from its stored XML format. */ void recreateFromXml (const XmlElement& xml); + //============================================================================== + /** A structure that recursively holds a tree of plugins. + @see KnownPluginList::createTree() + */ + struct PluginTree + { + String folder; /**< The name of this folder in the tree */ + OwnedArray subFolders; + Array plugins; + }; + + /** Creates a PluginTree object containing all the known plugins. */ + PluginTree* createTree (const SortMethod sortMethod) const; + + //============================================================================== + class CustomScanner + { + public: + CustomScanner(); + virtual ~CustomScanner(); + + /** Attempts to load the given file and find a list of plugins in it. + @returns true if the plugin loaded, false if it crashed + */ + virtual bool findPluginTypesFor (AudioPluginFormat& format, + OwnedArray & result, + const String& fileOrIdentifier) = 0; + + /** Called when a scan has finished, to allow clean-up of resources. */ + virtual void scanFinished(); + + /** Returns true if the current scan should be abandoned. + Any blocking methods should check this value repeatedly and return if + if becomes true. + */ + bool shouldExit() const noexcept; + }; + + /** Supplies a custom scanner to be used in future scans. + The KnownPluginList will take ownership of the object passed in. + */ + void setCustomScanner (CustomScanner*); private: //============================================================================== - OwnedArray types; + OwnedArray types; + StringArray blacklist; + ScopedPointer scanner; + CriticalSection scanLock; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownPluginList) }; -#endif // __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ +#endif // JUCE_KNOWNPLUGINLIST_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp index ecca4f0..a7beea7 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp @@ -1,37 +1,43 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +static StringArray readDeadMansPedalFile (const File& file) +{ + StringArray lines; + file.readLines (lines); + lines.removeEmptyStrings(); + return lines; +} + PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, AudioPluginFormat& formatToLookFor, FileSearchPath directoriesToSearch, const bool recursive, - const File& deadMansPedalFile_) + const File& deadMansPedal) : list (listToAddTo), format (formatToLookFor), - deadMansPedalFile (deadMansPedalFile_), - nextIndex (0), + deadMansPedalFile (deadMansPedal), progress (0) { directoriesToSearch.removeRedundantPaths(); @@ -40,7 +46,7 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, // If any plugins have crashed recently when being loaded, move them to the // end of the list to give the others a chance to load correctly.. - const StringArray crashedPlugins (getDeadMansPedalFile()); + const StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile)); for (int i = 0; i < crashedPlugins.size(); ++i) { @@ -50,72 +56,81 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, if (f == filesOrIdentifiersToScan[j]) filesOrIdentifiersToScan.move (j, -1); } + + applyBlacklistingsFromDeadMansPedal (listToAddTo, deadMansPedalFile); + nextIndex.set (filesOrIdentifiersToScan.size()); } PluginDirectoryScanner::~PluginDirectoryScanner() { + list.scanFinished(); } //============================================================================== -const String PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const +String PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const { - return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex]); + return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex.get() - 1]); } -bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList) +void PluginDirectoryScanner::updateProgress() { - String file (filesOrIdentifiersToScan [nextIndex]); + progress = (1.0f - nextIndex.get() / (float) filesOrIdentifiersToScan.size()); +} - if (file.isNotEmpty() && ! list.isListingUpToDate (file)) +bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList, + String& nameOfPluginBeingScanned) +{ + const int index = --nextIndex; + + if (index >= 0) { - OwnedArray typesFound; + const String file (filesOrIdentifiersToScan [index]); - // Add this plugin to the end of the dead-man's pedal list in case it crashes... - StringArray crashedPlugins (getDeadMansPedalFile()); - crashedPlugins.removeString (file); - crashedPlugins.add (file); - setDeadMansPedalFile (crashedPlugins); + if (file.isNotEmpty() && ! list.isListingUpToDate (file, format)) + { + nameOfPluginBeingScanned = format.getNameOfPluginFromIdentifier (file); - list.scanAndAddFile (file, - dontRescanIfAlreadyInList, - typesFound, - format); + OwnedArray typesFound; - // Managed to load without crashing, so remove it from the dead-man's-pedal.. - crashedPlugins.removeString (file); - setDeadMansPedalFile (crashedPlugins); + // Add this plugin to the end of the dead-man's pedal list in case it crashes... + StringArray crashedPlugins (readDeadMansPedalFile (deadMansPedalFile)); + crashedPlugins.removeString (file); + crashedPlugins.add (file); + setDeadMansPedalFile (crashedPlugins); - if (typesFound.size() == 0) - failedFiles.add (file); + list.scanAndAddFile (file, dontRescanIfAlreadyInList, typesFound, format); + + // Managed to load without crashing, so remove it from the dead-man's-pedal.. + crashedPlugins.removeString (file); + setDeadMansPedalFile (crashedPlugins); + + if (typesFound.size() == 0 && ! list.getBlacklistedFiles().contains (file)) + failedFiles.add (file); + } } - return skipNextFile(); + updateProgress(); + return index > 0; } bool PluginDirectoryScanner::skipNextFile() { - if (nextIndex >= filesOrIdentifiersToScan.size()) - return false; - - progress = ++nextIndex / (float) filesOrIdentifiersToScan.size(); - return nextIndex < filesOrIdentifiersToScan.size(); -} - -StringArray PluginDirectoryScanner::getDeadMansPedalFile() -{ - StringArray lines; - - if (deadMansPedalFile != File::nonexistent) - { - deadMansPedalFile.readLines (lines); - lines.removeEmptyStrings(); - } - - return lines; + updateProgress(); + return --nextIndex > 0; } void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents) { - if (deadMansPedalFile != File::nonexistent) + if (deadMansPedalFile.getFullPathName().isNotEmpty()) deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true); } + +void PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (KnownPluginList& list, const File& file) +{ + // If any plugins have crashed recently when being loaded, move them to the + // end of the list to give the others a chance to load correctly.. + const StringArray crashedPlugins (readDeadMansPedalFile (file)); + + for (int i = 0; i < crashedPlugins.size(); ++i) + list.addToBlacklist (crashedPlugins[i]); +} diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h index 2c7e7c5..762e742 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ -#define __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ - -#include "juce_KnownPluginList.h" -#include "../format/juce_AudioPluginFormatManager.h" +#ifndef JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED +#define JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED //============================================================================== @@ -78,10 +74,13 @@ public: 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. + The nameOfPluginBeingScanned will be updated to the name of the plugin being + scanned before the scan starts. Returns false when there are no more files to try. */ - bool scanNextFile (bool dontRescanIfAlreadyInList); + bool scanNextFile (bool dontRescanIfAlreadyInList, + String& nameOfPluginBeingScanned); /** Skips over the next file without scanning it. Returns false when there are no more files to try. @@ -94,10 +93,9 @@ public: This is handy if you want to show the user which file is currently getting scanned. */ - const String getNextPluginFileThatWillBeScanned() const; + String getNextPluginFileThatWillBeScanned() const; - /** Returns the estimated progress, between 0 and 1. - */ + /** 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 @@ -105,6 +103,10 @@ public: */ const StringArray& getFailedFiles() const noexcept { return failedFiles; } + /** Reads the given dead-mans-pedal file and applies its contents to the list. */ + static void applyBlacklistingsFromDeadMansPedal (KnownPluginList& listToApplyTo, + const File& deadMansPedalFile); + private: //============================================================================== KnownPluginList& list; @@ -112,14 +114,14 @@ private: StringArray filesOrIdentifiersToScan; File deadMansPedalFile; StringArray failedFiles; - int nextIndex; + Atomic nextIndex; float progress; - StringArray getDeadMansPedalFile(); + void updateProgress(); void setDeadMansPedalFile (const StringArray& newContents); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginDirectoryScanner) }; -#endif // __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ +#endif // JUCE_PLUGINDIRECTORYSCANNER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 3076821..a0c2388 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -1,46 +1,171 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -PluginListComponent::PluginListComponent (KnownPluginList& listToEdit, - const File& deadMansPedalFile_, - PropertiesFile* const propertiesToUse_) - : list (listToEdit), - deadMansPedalFile (deadMansPedalFile_), - optionsButton ("Options..."), - propertiesToUse (propertiesToUse_) +class PluginListComponent::TableModel : public TableListBoxModel { - listBox.setModel (this); - addAndMakeVisible (&listBox); +public: + TableModel (PluginListComponent& c, KnownPluginList& l) : owner (c), list (l) {} - addAndMakeVisible (&optionsButton); + int getNumRows() override + { + return list.getNumTypes() + list.getBlacklistedFiles().size(); + } + + void paintRowBackground (Graphics& g, int /*rowNumber*/, int /*width*/, int /*height*/, bool rowIsSelected) override + { + if (rowIsSelected) + g.fillAll (owner.findColour (TextEditor::highlightColourId)); + } + + enum + { + nameCol = 1, + typeCol = 2, + categoryCol = 3, + manufacturerCol = 4, + descCol = 5 + }; + + void paintCell (Graphics& g, int row, int columnId, int width, int height, bool /*rowIsSelected*/) override + { + String text; + bool isBlacklisted = row >= list.getNumTypes(); + + if (isBlacklisted) + { + if (columnId == nameCol) + text = list.getBlacklistedFiles() [row - list.getNumTypes()]; + else if (columnId == descCol) + text = TRANS("Deactivated after failing to initialise correctly"); + } + else if (const PluginDescription* const desc = list.getType (row)) + { + switch (columnId) + { + case nameCol: text = desc->name; break; + case typeCol: text = desc->pluginFormatName; break; + case categoryCol: text = desc->category.isNotEmpty() ? desc->category : "-"; break; + case manufacturerCol: text = desc->manufacturerName; break; + case descCol: text = getPluginDescription (*desc); break; + + default: jassertfalse; break; + } + } + + if (text.isNotEmpty()) + { + g.setColour (isBlacklisted ? Colours::red + : columnId == nameCol ? Colours::black + : Colours::grey); + g.setFont (Font (height * 0.7f, Font::bold)); + g.drawFittedText (text, 4, 0, width - 6, height, Justification::centredLeft, 1, 0.9f); + } + } + + void deleteKeyPressed (int) override + { + owner.removeSelected(); + } + + void sortOrderChanged (int newSortColumnId, bool isForwards) override + { + switch (newSortColumnId) + { + case nameCol: list.sort (KnownPluginList::sortAlphabetically, isForwards); break; + case typeCol: list.sort (KnownPluginList::sortByFormat, isForwards); break; + case categoryCol: list.sort (KnownPluginList::sortByCategory, isForwards); break; + case manufacturerCol: list.sort (KnownPluginList::sortByManufacturer, isForwards); break; + case descCol: break; + + default: jassertfalse; break; + } + } + + static void removePluginItem (KnownPluginList& list, int index) + { + if (index < list.getNumTypes()) + list.removeType (index); + else + list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); + } + + static String getPluginDescription (const PluginDescription& desc) + { + StringArray items; + + if (desc.descriptiveName != desc.name) + items.add (desc.descriptiveName); + + items.add (desc.version); + + items.removeEmptyStrings(); + return items.joinIntoString (" - "); + } + + PluginListComponent& owner; + KnownPluginList& list; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableModel) +}; + +//============================================================================== +PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit, + const File& deadMansPedal, PropertiesFile* const props) + : formatManager (manager), + list (listToEdit), + deadMansPedalFile (deadMansPedal), + optionsButton ("Options..."), + propertiesToUse (props), + numThreads (0) +{ + tableModel = new TableModel (*this, listToEdit); + + TableHeaderComponent& header = table.getHeader(); + + header.addColumn (TRANS("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); + header.addColumn (TRANS("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); + header.addColumn (TRANS("Category"), TableModel::categoryCol, 100, 100, 200); + header.addColumn (TRANS("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); + header.addColumn (TRANS("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); + + table.setHeaderHeight (22); + table.setRowHeight (20); + table.setModel (tableModel); + table.setMultipleSelectionEnabled (true); + addAndMakeVisible (table); + + addAndMakeVisible (optionsButton); optionsButton.addListener (this); optionsButton.setTriggeredOnMouseDown (true); setSize (400, 600); list.addChangeListener (this); updateList(); + table.getHeader().reSortTable(); + + PluginDirectoryScanner::applyBlacklistingsFromDeadMansPedal (list, deadMansPedalFile); + deadMansPedalFile.deleteFile(); } PluginListComponent::~PluginListComponent() @@ -48,123 +173,69 @@ PluginListComponent::~PluginListComponent() list.removeChangeListener (this); } +void PluginListComponent::setOptionsButtonText (const String& newText) +{ + optionsButton.setButtonText (newText); + resized(); +} + +void PluginListComponent::setNumberOfThreadsForScanning (int num) +{ + numThreads = num; +} + void PluginListComponent::resized() { - listBox.setBounds (0, 0, getWidth(), getHeight() - 30); + Rectangle r (getLocalBounds().reduced (2)); + + optionsButton.setBounds (r.removeFromBottom (24)); optionsButton.changeWidthToFitText (24); - optionsButton.setTopLeftPosition (8, getHeight() - 28); + + r.removeFromBottom (3); + table.setBounds (r); } void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) { + table.getHeader().reSortTable(); updateList(); } void PluginListComponent::updateList() { - listBox.updateContent(); - listBox.repaint(); + table.updateContent(); + table.repaint(); } -int PluginListComponent::getNumRows() +void PluginListComponent::removeSelected() { - return list.getNumTypes(); + const SparseSet selected (table.getSelectedRows()); + + for (int i = table.getNumRows(); --i >= 0;) + if (selected.contains (i)) + TableModel::removePluginItem (list, i); } -void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) +bool PluginListComponent::canShowSelectedFolder() const { - if (rowIsSelected) - g.fillAll (findColour (TextEditor::highlightColourId)); + if (const PluginDescription* const desc = list.getType (table.getSelectedRow())) + return File::createFileWithoutCheckingPath (desc->fileOrIdentifier).exists(); - const PluginDescription* const pd = list.getType (row); - - if (pd != nullptr) - { - GlyphArrangement ga; - ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), pd->name, 8.0f, height * 0.8f, width - 10.0f, true); - - g.setColour (Colours::black); - ga.draw (g); - - const Rectangle bb (ga.getBoundingBox (0, -1, false)); - - String desc; - desc << pd->pluginFormatName - << (pd->isInstrument ? " instrument" : " effect") - << " - " - << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") - << " / " - << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); - - if (pd->manufacturerName.isNotEmpty()) - desc << " - " << pd->manufacturerName; - - if (pd->version.isNotEmpty()) - desc << " - " << pd->version; - - if (pd->category.isNotEmpty()) - desc << " - category: '" << pd->category << '\''; - - g.setColour (Colours::grey); - - ga.clear(); - ga.addCurtailedLineOfText (Font (height * 0.6f), desc, bb.getRight() + 10.0f, height * 0.8f, width - bb.getRight() - 12.0f, true); - ga.draw (g); - } + return false; } -void PluginListComponent::deleteKeyPressed (int lastRowSelected) +void PluginListComponent::showSelectedFolder() { - list.removeType (lastRowSelected); + if (canShowSelectedFolder()) + if (const PluginDescription* const desc = list.getType (table.getSelectedRow())) + File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); } -void PluginListComponent::optionsMenuCallback (int result) +void PluginListComponent::removeMissingPlugins() { - switch (result) - { - case 1: list.clear(); break; - - case 2: list.sort (KnownPluginList::sortAlphabetically); break; - case 3: list.sort (KnownPluginList::sortByCategory); break; - case 4: list.sort (KnownPluginList::sortByManufacturer); break; - - case 5: - { - const SparseSet selected (listBox.getSelectedRows()); - - for (int i = list.getNumTypes(); --i >= 0;) - if (selected.contains (i)) - list.removeType (i); - - break; - } - - case 6: - { - const PluginDescription* const desc = list.getType (listBox.getSelectedRow()); - - if (desc != nullptr && File (desc->fileOrIdentifier).existsAsFile()) - File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); - - break; - } - - case 7: - for (int i = list.getNumTypes(); --i >= 0;) - if (! AudioPluginFormatManager::getInstance()->doesPluginStillExist (*list.getType (i))) - list.removeType (i); - - break; - - default: - if (result != 0) - { - typeToScan = result - 10; - startTimer (1); - } - - break; - } + for (int i = list.getNumTypes(); --i >= 0;) + if (! formatManager.doesPluginStillExist (*list.getType (i))) + list.removeType (i); } void PluginListComponent::optionsMenuStaticCallback (int result, PluginListComponent* pluginList) @@ -173,27 +244,41 @@ void PluginListComponent::optionsMenuStaticCallback (int result, PluginListCompo pluginList->optionsMenuCallback (result); } +void PluginListComponent::optionsMenuCallback (int result) +{ + switch (result) + { + case 0: break; + case 1: list.clear(); break; + case 2: removeSelected(); break; + case 3: showSelectedFolder(); break; + case 4: removeMissingPlugins(); break; + + default: + if (AudioPluginFormat* format = formatManager.getFormat (result - 10)) + scanFor (*format); + + break; + } +} + void PluginListComponent::buttonClicked (Button* button) { if (button == &optionsButton) { PopupMenu menu; menu.addItem (1, TRANS("Clear list")); - menu.addItem (5, TRANS("Remove selected plugin from list"), listBox.getNumSelectedRows() > 0); - menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox.getNumSelectedRows() > 0); - menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); - menu.addSeparator(); - menu.addItem (2, TRANS("Sort alphabetically")); - menu.addItem (3, TRANS("Sort by category")); - menu.addItem (4, TRANS("Sort by manufacturer")); + menu.addItem (2, TRANS("Remove selected plug-in from list"), table.getNumSelectedRows() > 0); + menu.addItem (3, TRANS("Show folder containing selected plug-in"), canShowSelectedFolder()); + menu.addItem (4, TRANS("Remove any plug-ins whose files no longer exist")); menu.addSeparator(); - for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) + for (int i = 0; i < formatManager.getNumFormats(); ++i) { - AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); + AudioPluginFormat* const format = formatManager.getFormat (i); - if (format->getDefaultLocationsToSearch().getNumPaths() > 0) - menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); + if (format->canScanForPlugins()) + menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plug-ins"); } menu.showMenuAsync (PopupMenu::Options().withTargetComponent (&optionsButton), @@ -201,12 +286,6 @@ void PluginListComponent::buttonClicked (Button* button) } } -void PluginListComponent::timerCallback() -{ - stopTimer(); - scanFor (AudioPluginFormatManager::getInstance()->getFormat (typeToScan)); -} - bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) { return true; @@ -215,83 +294,261 @@ bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) void PluginListComponent::filesDropped (const StringArray& files, int, int) { OwnedArray typesFound; - list.scanAndAddDragAndDroppedFiles (files, typesFound); + list.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound); } -void PluginListComponent::scanFor (AudioPluginFormat* format) +FileSearchPath PluginListComponent::getLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format) { -#if JUCE_MODAL_LOOPS_PERMITTED - if (format == nullptr) - return; - - FileSearchPath path (format->getDefaultLocationsToSearch()); - - if (propertiesToUse != nullptr) - path = propertiesToUse->getValue ("lastPluginScanPath_" + format->getName(), path.toString()); - - { - AlertWindow aw (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon); - FileSearchPathListComponent pathList; - pathList.setSize (500, 300); - pathList.setPath (path); - - aw.addCustomComponent (&pathList); - aw.addButton (TRANS("Scan"), 1, KeyPress::returnKey); - aw.addButton (TRANS("Cancel"), 0, KeyPress::escapeKey); - - if (aw.runModalLoop() == 0) - return; - - path = pathList.getPath(); - } - - if (propertiesToUse != nullptr) - { - propertiesToUse->setValue ("lastPluginScanPath_" + format->getName(), path.toString()); - propertiesToUse->saveIfNeeded(); - } - - double progress = 0.0; - - AlertWindow aw (TRANS("Scanning for plugins..."), - TRANS("Searching for all possible plugin files..."), AlertWindow::NoIcon); - - aw.addButton (TRANS("Cancel"), 0, KeyPress::escapeKey); - aw.addProgressBarComponent (progress); - aw.enterModalState(); - - MessageManager::getInstance()->runDispatchLoopUntil (300); - - PluginDirectoryScanner scanner (list, *format, path, true, deadMansPedalFile); - - for (;;) - { - aw.setMessage (TRANS("Testing:\n\n") + scanner.getNextPluginFileThatWillBeScanned()); - - MessageManager::getInstance()->runDispatchLoopUntil (100); - - if (! scanner.scanNextFile (true)) - break; - - if (! aw.isCurrentlyModal()) - break; - - progress = scanner.getProgress(); - } - - if (scanner.getFailedFiles().size() > 0) - { - StringArray shortNames; - - for (int i = 0; i < scanner.getFailedFiles().size(); ++i) - shortNames.add (File (scanner.getFailedFiles()[i]).getFileName()); - - AlertWindow::showMessageBox (AlertWindow::InfoIcon, - TRANS("Scan complete"), - TRANS("Note that the following files appeared to be plugin files, but failed to load correctly:\n\n") - + shortNames.joinIntoString (", ")); - } -#else - jassertfalse; // this method needs refactoring to work without modal loops.. -#endif + return FileSearchPath (properties.getValue ("lastPluginScanPath_" + format.getName(), + format.getDefaultLocationsToSearch().toString())); +} + +void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format, + const FileSearchPath& newPath) +{ + properties.setValue ("lastPluginScanPath_" + format.getName(), newPath.toString()); +} + +//============================================================================== +class PluginListComponent::Scanner : private Timer +{ +public: + Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, int threads) + : owner (plc), formatToScan (format), propertiesToUse (properties), + pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon), + progressWindow (TRANS("Scanning for plug-ins..."), + TRANS("Searching for all possible plug-in files..."), AlertWindow::NoIcon), + progress (0.0), numThreads (threads), finished (false) + { + FileSearchPath path (formatToScan.getDefaultLocationsToSearch()); + + if (path.getNumPaths() > 0) // if the path is empty, then paths aren't used for this format. + { + if (propertiesToUse != nullptr) + path = getLastSearchPath (*propertiesToUse, formatToScan); + + pathList.setSize (500, 300); + pathList.setPath (path); + + pathChooserWindow.addCustomComponent (&pathList); + pathChooserWindow.addButton (TRANS("Scan"), 1, KeyPress (KeyPress::returnKey)); + pathChooserWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + + pathChooserWindow.enterModalState (true, + ModalCallbackFunction::forComponent (startScanCallback, + &pathChooserWindow, this), + false); + } + else + { + startScan(); + } + } + + ~Scanner() + { + if (pool != nullptr) + { + pool->removeAllJobs (true, 60000); + pool = nullptr; + } + } + +private: + PluginListComponent& owner; + AudioPluginFormat& formatToScan; + PropertiesFile* propertiesToUse; + ScopedPointer scanner; + AlertWindow pathChooserWindow, progressWindow; + FileSearchPathListComponent pathList; + String pluginBeingScanned; + double progress; + int numThreads; + bool finished; + ScopedPointer pool; + + static void startScanCallback (int result, AlertWindow* alert, Scanner* scanner) + { + if (alert != nullptr && scanner != nullptr) + { + if (result != 0) + scanner->warnUserAboutStupidPaths(); + else + scanner->finishedScan(); + } + } + + // Try to dissuade people from to scanning their entire C: drive, or other system folders. + void warnUserAboutStupidPaths() + { + for (int i = 0; i < pathList.getPath().getNumPaths(); ++i) + { + const File f (pathList.getPath()[i]); + + if (isStupidPath (f)) + { + AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, + TRANS("Plugin Scanning"), + TRANS("If you choose to scan folders that contain non-plugin files, " + "then scanning may take a long time, and can cause crashes when " + "attempting to load unsuitable files.") + + newLine + + TRANS ("Are you sure you want to scan the folder \"XYZ\"?") + .replace ("XYZ", f.getFullPathName()), + TRANS ("Scan"), + String::empty, + nullptr, + ModalCallbackFunction::create (warnAboutStupidPathsCallback, this)); + return; + } + } + + startScan(); + } + + static bool isStupidPath (const File& f) + { + Array roots; + File::findFileSystemRoots (roots); + + if (roots.contains (f)) + return true; + + File::SpecialLocationType pathsThatWouldBeStupidToScan[] + = { File::globalApplicationsDirectory, + File::userHomeDirectory, + File::userDocumentsDirectory, + File::userDesktopDirectory, + File::tempDirectory, + File::userMusicDirectory, + File::userMoviesDirectory, + File::userPicturesDirectory }; + + for (int i = 0; i < numElementsInArray (pathsThatWouldBeStupidToScan); ++i) + { + const File sillyFolder (File::getSpecialLocation (pathsThatWouldBeStupidToScan[i])); + + if (f == sillyFolder || sillyFolder.isAChildOf (f)) + return true; + } + + return false; + } + + static void warnAboutStupidPathsCallback (int result, Scanner* scanner) + { + if (result != 0) + scanner->startScan(); + else + scanner->finishedScan(); + } + + void startScan() + { + pathChooserWindow.setVisible (false); + + scanner = new PluginDirectoryScanner (owner.list, formatToScan, pathList.getPath(), + true, owner.deadMansPedalFile); + + if (propertiesToUse != nullptr) + { + setLastSearchPath (*propertiesToUse, formatToScan, pathList.getPath()); + propertiesToUse->saveIfNeeded(); + } + + progressWindow.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + progressWindow.addProgressBarComponent (progress); + progressWindow.enterModalState(); + + if (numThreads > 0) + { + pool = new ThreadPool (numThreads); + + for (int i = numThreads; --i >= 0;) + pool->addJob (new ScanJob (*this), true); + } + + startTimer (20); + } + + void finishedScan() + { + owner.scanFinished (scanner != nullptr ? scanner->getFailedFiles() + : StringArray()); + } + + void timerCallback() override + { + if (pool == nullptr) + { + if (doNextScan()) + startTimer (20); + } + + if (! progressWindow.isCurrentlyModal()) + finished = true; + + if (finished) + finishedScan(); + else + progressWindow.setMessage (TRANS("Testing") + ":\n\n" + pluginBeingScanned); + } + + bool doNextScan() + { + if (scanner->scanNextFile (true, pluginBeingScanned)) + { + progress = scanner->getProgress(); + return true; + } + + finished = true; + return false; + } + + struct ScanJob : public ThreadPoolJob + { + ScanJob (Scanner& s) : ThreadPoolJob ("pluginscan"), scanner (s) {} + + JobStatus runJob() + { + while (scanner.doNextScan() && ! shouldExit()) + {} + + return jobHasFinished; + } + + Scanner& scanner; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScanJob) + }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Scanner) +}; + +void PluginListComponent::scanFor (AudioPluginFormat& format) +{ + currentScanner = new Scanner (*this, format, propertiesToUse, numThreads); +} + +bool PluginListComponent::isScanning() const noexcept +{ + return currentScanner != nullptr; +} + +void PluginListComponent::scanFinished (const StringArray& failedFiles) +{ + StringArray shortNames; + + for (int i = 0; i < failedFiles.size(); ++i) + shortNames.add (File::createFileWithoutCheckingPath (failedFiles[i]).getFileName()); + + currentScanner = nullptr; // mustn't delete this before using the failed files array + + if (shortNames.size() > 0) + AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon, + TRANS("Scan complete"), + TRANS("Note that the following files appeared to be plugin files, but failed to load correctly") + + ":\n\n" + + shortNames.joinIntoString (", ")); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h index 97520a4..97751a8 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ -#define __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ - -#include "juce_KnownPluginList.h" -#include "../format/juce_AudioPluginFormat.h" +#ifndef JUCE_PLUGINLISTCOMPONENT_H_INCLUDED +#define JUCE_PLUGINLISTCOMPONENT_H_INCLUDED //============================================================================== @@ -37,10 +33,8 @@ */ class JUCE_API PluginListComponent : public Component, public FileDragAndDropTarget, - public ListBoxModel, private ChangeListener, - private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug) - private Timer + private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug) { public: //============================================================================== @@ -48,50 +42,73 @@ 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, + PluginListComponent (AudioPluginFormatManager& formatManager, + KnownPluginList& listToRepresent, const File& deadMansPedalFile, PropertiesFile* propertiesToUse); /** Destructor. */ ~PluginListComponent(); - //============================================================================== - /** @internal */ - void resized(); - /** @internal */ - bool isInterestedInFileDrag (const StringArray&); - /** @internal */ - void filesDropped (const StringArray&, int, int); - /** @internal */ - int getNumRows(); - /** @internal */ - void paintListBoxItem (int row, Graphics&, int width, int height, bool rowIsSelected); - /** @internal */ - void deleteKeyPressed (int lastRowSelected); + /** Changes the text in the panel's options button. */ + void setOptionsButtonText (const String& newText); + + /** Sets how many threads to simultaneously scan for plugins. + If this is 0, then all scanning happens on the message thread (this is the default) + */ + void setNumberOfThreadsForScanning (int numThreads); + + /** Returns the last search path stored in a given properties file for the specified format. */ + static FileSearchPath getLastSearchPath (PropertiesFile&, AudioPluginFormat&); + + /** Stores a search path in a properties file for the given format. */ + static void setLastSearchPath (PropertiesFile&, AudioPluginFormat&, const FileSearchPath&); + + /** Triggers an asynchronous scan for the given format. */ + void scanFor (AudioPluginFormat&); + + /** Returns true if there's currently a scan in progress. */ + bool isScanning() const noexcept; private: //============================================================================== + AudioPluginFormatManager& formatManager; KnownPluginList& list; File deadMansPedalFile; - ListBox listBox; + TableListBox table; TextButton optionsButton; PropertiesFile* propertiesToUse; - int typeToScan; + int numThreads; - void scanFor (AudioPluginFormat*); - static void optionsMenuStaticCallback (int result, PluginListComponent*); - void optionsMenuCallback (int result); + class TableModel; + friend class TableModel; + friend struct ContainerDeletePolicy; + ScopedPointer tableModel; + + class Scanner; + friend class Scanner; + friend struct ContainerDeletePolicy; + ScopedPointer currentScanner; + + void scanFinished (const StringArray&); + static void optionsMenuStaticCallback (int, PluginListComponent*); + void optionsMenuCallback (int); void updateList(); + void showSelectedFolder(); + bool canShowSelectedFolder() const; + void removeSelected(); + void removeMissingPlugins(); - void buttonClicked (Button*); - void changeListenerCallback (ChangeBroadcaster*); - void timerCallback(); + void resized() override; + bool isInterestedInFileDrag (const StringArray&) override; + void filesDropped (const StringArray&, int, int) override; + void buttonClicked (Button*) override; + void changeListenerCallback (ChangeBroadcaster*) override; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PluginListComponent) }; -#endif // __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ +#endif // JUCE_PLUGINLISTCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp index 96a2ac3..65b1615 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -32,7 +35,7 @@ AbstractFifo::AbstractFifo (const int capacity) noexcept AbstractFifo::~AbstractFifo() {} int AbstractFifo::getTotalSize() const noexcept { return bufferSize; } -int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady(); } +int AbstractFifo::getFreeSpace() const noexcept { return bufferSize - getNumReady() - 1; } int AbstractFifo::getNumReady() const noexcept { @@ -138,8 +141,8 @@ public: class WriteThread : public Thread { public: - WriteThread (AbstractFifo& fifo_, int* buffer_) - : Thread ("fifo writer"), fifo (fifo_), buffer (buffer_) + WriteThread (AbstractFifo& f, int* b, Random rng) + : Thread ("fifo writer"), fifo (f), buffer (b), random (rng) { startThread(); } @@ -152,11 +155,10 @@ public: void run() { int n = 0; - Random r; while (! threadShouldExit()) { - int num = r.nextInt (2000) + 1; + int num = random.nextInt (2000) + 1; int start1, size1, start2, size2; fifo.prepareToWrite (num, start1, size1, start2, size2); @@ -165,11 +167,10 @@ public: jassert (size1 == 0 || (start1 >= 0 && start1 < fifo.getTotalSize())); jassert (size2 == 0 || (start2 >= 0 && start2 < fifo.getTotalSize())); - int i; - for (i = 0; i < size1; ++i) + for (int i = 0; i < size1; ++i) buffer [start1 + i] = n++; - for (i = 0; i < size2; ++i) + for (int i = 0; i < size2; ++i) buffer [start2 + i] = n++; fifo.finishedWrite (size1 + size2); @@ -179,6 +180,7 @@ public: private: AbstractFifo& fifo; int* buffer; + Random random; }; void runTest() @@ -188,12 +190,13 @@ public: int buffer [5000]; AbstractFifo fifo (numElementsInArray (buffer)); - WriteThread writer (fifo, buffer); + WriteThread writer (fifo, buffer, getRandom()); int n = 0; - Random r; + Random r = getRandom(); + r.combineSeed (12345); - for (int count = 1000000; --count >= 0;) + for (int count = 100000; --count >= 0;) { int num = r.nextInt (6000) + 1; @@ -210,11 +213,10 @@ public: bool failed = false; - int i; - for (i = 0; i < size1; ++i) + for (int i = 0; i < size1; ++i) failed = (buffer [start1 + i] != n++) || failed; - for (i = 0; i < size2; ++i) + for (int i = 0; i < size2; ++i) failed = (buffer [start2 + i] != n++) || failed; if (failed) diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h index 3faa5b1..02feac7 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_ABSTRACTFIFO_JUCEHEADER__ -#define __JUCE_ABSTRACTFIFO_JUCEHEADER__ - -#include "../memory/juce_Atomic.h" +#ifndef JUCE_ABSTRACTFIFO_H_INCLUDED +#define JUCE_ABSTRACTFIFO_H_INCLUDED //============================================================================== @@ -156,7 +157,7 @@ public: */ void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept; - /** Called after reading from the FIFO, to indicate that this many items have been added. + /** Called after writing from the FIFO, to indicate that this many items have been added. @see prepareToWrite */ void finishedWrite (int numWritten) noexcept; @@ -212,8 +213,8 @@ private: int bufferSize; Atomic validStart, validEnd; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo) }; -#endif // __JUCE_ABSTRACTFIFO_JUCEHEADER__ +#endif // JUCE_ABSTRACTFIFO_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h index 4b0ae52..375576a 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Array.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Array.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_ARRAY_JUCEHEADER__ -#define __JUCE_ARRAY_JUCEHEADER__ - -#include "juce_ArrayAllocationBase.h" -#include "juce_ElementComparator.h" -#include "../threads/juce_CriticalSection.h" +#ifndef JUCE_ARRAY_H_INCLUDED +#define JUCE_ARRAY_H_INCLUDED //============================================================================== @@ -41,9 +40,9 @@ do so, the class must fulfil these requirements: - it must have a copy constructor and assignment operator - it must be able to be relocated in memory by a memcpy without this causing any problems - so - objects whose functionality relies on external pointers or references to themselves can be used. + objects whose functionality relies on external pointers or references to themselves can not be used. - You can of course have an array of pointers to any kind of object, e.g. Array , but if + 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. @@ -56,7 +55,8 @@ @see OwnedArray, ReferenceCountedArray, StringArray, CriticalSection */ template + typename TypeOfCriticalSectionToUse = DummyCriticalSection, + int minimumAllocatedSize = 0> class Array { private: @@ -85,7 +85,7 @@ public: #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Array (Array&& other) noexcept - : data (static_cast &&> (other.data)), + : data (static_cast&&> (other.data)), numUsed (other.numUsed) { other.numUsed = 0; @@ -133,7 +133,7 @@ public: if (this != &other) { Array otherCopy (other); - swapWithArray (otherCopy); + swapWith (otherCopy); } return *this; @@ -142,7 +142,9 @@ public: #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Array& operator= (Array&& other) noexcept { - data = static_cast &&> (other.data); + const ScopedLockType lock (getLock()); + deleteAllElements(); + data = static_cast&&> (other.data); numUsed = other.numUsed; other.numUsed = 0; return *this; @@ -199,7 +201,6 @@ public: } /** Removes all elements from the array without freeing the array's allocated storage. - @see clear */ void clearQuick() @@ -219,7 +220,7 @@ public: /** Returns one of the elements in the array. If the index passed in is beyond the range of valid elements, this - will return zero. + will return a default value. If you're certain that the index will always be a valid element, you can call getUnchecked() instead, which is faster. @@ -230,8 +231,14 @@ public: ElementType operator[] (const int index) const { const ScopedLockType lock (getLock()); - return isPositiveAndBelow (index, numUsed) ? data.elements [index] - : ElementType(); + + if (isPositiveAndBelow (index, numUsed)) + { + jassert (data.elements != nullptr); + return data.elements [index]; + } + + return ElementType(); } /** Returns one of the elements in the array, without checking the index passed in. @@ -246,7 +253,7 @@ public: inline ElementType getUnchecked (const int index) const { const ScopedLockType lock (getLock()); - jassert (isPositiveAndBelow (index, numUsed)); + jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } @@ -262,30 +269,42 @@ public: inline ElementType& getReference (const int index) const noexcept { const ScopedLockType lock (getLock()); - jassert (isPositiveAndBelow (index, numUsed)); + jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } - /** Returns the first element in the array, or 0 if the array is empty. + /** Returns the first element in the array, or a default value if the array is empty. @see operator[], getUnchecked, getLast */ inline ElementType getFirst() const { const ScopedLockType lock (getLock()); - return (numUsed > 0) ? data.elements [0] - : ElementType(); + + if (numUsed > 0) + { + jassert (data.elements != nullptr); + return data.elements[0]; + } + + return ElementType(); } - /** Returns the last element in the array, or 0 if the array is empty. + /** Returns the last element in the array, or a default value if the array is empty. @see operator[], getUnchecked, getFirst */ inline ElementType getLast() const { const ScopedLockType lock (getLock()); - return (numUsed > 0) ? data.elements [numUsed - 1] - : ElementType(); + + if (numUsed > 0) + { + jassert (data.elements != nullptr); + return data.elements[numUsed - 1]; + } + + return ElementType(); } /** Returns a pointer to the actual array data. @@ -311,6 +330,11 @@ public: */ inline ElementType* end() const noexcept { + #if JUCE_DEBUG + if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy) + return data.elements; + #endif + return data.elements + numUsed; } @@ -360,13 +384,27 @@ public: @param newElement the new object to add to the array @see set, insert, addIfNotAlreadyThere, addSorted, addUsingDefaultSort, addArray */ - void add (ParameterType newElement) + void add (const ElementType& newElement) { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); new (data.elements + numUsed++) ElementType (newElement); } + #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + /** 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 (ElementType&& newElement) + { + const ScopedLockType lock (getLock()); + data.ensureAllocatedSize (numUsed + 1); + new (data.elements + numUsed++) ElementType (static_cast (newElement)); + } + #endif + /** 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 @@ -383,6 +421,7 @@ public: { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); + jassert (data.elements != nullptr); if (isPositiveAndBelow (indexToInsertAt, numUsed)) { @@ -460,17 +499,17 @@ public: { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + numberOfElements); - ElementType* insertPos; + ElementType* insertPos = data.elements; if (isPositiveAndBelow (indexToInsertAt, numUsed)) { - insertPos = data.elements + indexToInsertAt; + insertPos += indexToInsertAt; const int numberToMove = numUsed - indexToInsertAt; memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ElementType)); } else { - insertPos = data.elements + numUsed; + insertPos += numUsed; } numUsed += numberOfElements; @@ -512,6 +551,7 @@ public: if (isPositiveAndBelow (indexToChange, numUsed)) { + jassert (data.elements != nullptr); data.elements [indexToChange] = newValue; } else if (indexToChange >= 0) @@ -539,11 +579,13 @@ public: /** Adds elements from an array to the end of this array. - @param elementsToAdd the array of elements to add + @param elementsToAdd an array of some kind of object from which elements + can be constructed. @param numElementsToAdd how many elements are in this other array @see add */ - void addArray (const ElementType* elementsToAdd, int numElementsToAdd) + template + void addArray (const Type* elementsToAdd, int numElementsToAdd) { const ScopedLockType lock (getLock()); @@ -559,18 +601,34 @@ public: } } + /** Adds elements from a null-terminated array of pointers to the end of this array. + + @param elementsToAdd an array of pointers to some kind of object from which elements + can be constructed. This array must be terminated by a nullptr + @see addArray + */ + template + void addNullTerminatedArray (const Type* const* elementsToAdd) + { + int num = 0; + for (const Type* const* e = elementsToAdd; *e != nullptr; ++e) + ++num; + + addArray (elementsToAdd, num); + } + /** 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) noexcept + template + void swapWith (OtherArrayType& otherArray) noexcept { const ScopedLockType lock1 (getLock()); - const ScopedLockType lock2 (otherArray.getLock()); - + const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); data.swapWith (otherArray.data); - swapVariables (numUsed, otherArray.numUsed); + std::swap (numUsed, otherArray.numUsed); } /** Adds elements from another array to the end of this array. @@ -672,37 +730,30 @@ public: @returns the index of the element, or -1 if it's not found @see addSorted, sort */ - template - int indexOfSorted (ElementComparator& comparator, ParameterType elementToLookFor) const + template + int indexOfSorted (ElementComparator& comparator, TargetValueType elementToLookFor) const { (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused const ScopedLockType lock (getLock()); - int start = 0; - int end_ = numUsed; - for (;;) + for (int s = 0, e = numUsed;;) { - if (start >= end_) - { + if (s >= e) return -1; - } - else if (comparator.compareElements (elementToLookFor, data.elements [start]) == 0) - { - return start; - } - else - { - const int halfway = (start + end_) >> 1; - if (halfway == start) - return -1; - else if (comparator.compareElements (elementToLookFor, data.elements [halfway]) >= 0) - start = halfway; - else - end_ = halfway; - } + if (comparator.compareElements (elementToLookFor, data.elements [s]) == 0) + return s; + + const int halfway = (s + e) / 2; + if (halfway == s) + return -1; + + if (comparator.compareElements (elementToLookFor, data.elements [halfway]) >= 0) + s = halfway; + else + e = halfway; } } @@ -715,7 +766,7 @@ public: @param indexToRemove the index of the element to remove @returns the element that has been removed - @see removeValue, removeRange + @see removeFirstMatchingValue, removeAllInstancesOf, removeRange */ ElementType remove (const int indexToRemove) { @@ -723,24 +774,35 @@ public: if (isPositiveAndBelow (indexToRemove, numUsed)) { - --numUsed; - - ElementType* const e = data.elements + indexToRemove; - ElementType removed (*e); - e->~ElementType(); - const int numberToShift = numUsed - indexToRemove; - - if (numberToShift > 0) - memmove (e, e + 1, ((size_t) numberToShift) * sizeof (ElementType)); - - if ((numUsed << 1) < data.numAllocated) - minimiseStorageOverheads(); - + jassert (data.elements != nullptr); + ElementType removed (data.elements[indexToRemove]); + removeInternal (indexToRemove); return removed; } - else + + return ElementType(); + } + + /** Removes an item from the array. + + This will remove the first occurrence of the given element from the array. + If the item isn't found, no action is taken. + + @param valueToRemove the object to try to remove + @see remove, removeRange + */ + void removeFirstMatchingValue (ParameterType valueToRemove) + { + const ScopedLockType lock (getLock()); + ElementType* const e = data.elements; + + for (int i = 0; i < numUsed; ++i) { - return ElementType(); + if (valueToRemove == e[i]) + { + removeInternal (i); + break; + } } } @@ -752,19 +814,13 @@ public: @param valueToRemove the object to try to remove @see remove, removeRange */ - void removeValue (ParameterType valueToRemove) + void removeAllInstancesOf (ParameterType valueToRemove) { const ScopedLockType lock (getLock()); - ElementType* const e = data.elements; - for (int i = 0; i < numUsed; ++i) - { - if (valueToRemove == e[i]) - { - remove (i); - break; - } - } + for (int i = numUsed; --i >= 0;) + if (valueToRemove == data.elements[i]) + removeInternal (i); } /** Removes a range of elements from the array. @@ -777,7 +833,7 @@ public: @param startIndex the index of the first element to remove @param numberToRemove how many elements should be removed - @see remove, removeValue + @see remove, removeFirstMatchingValue, removeAllInstancesOf */ void removeRange (int startIndex, int numberToRemove) { @@ -798,16 +854,14 @@ public: memmove (e, e + numberToRemove, ((size_t) numToShift) * sizeof (ElementType)); numUsed -= numberToRemove; - - if ((numUsed << 1) < data.numAllocated) - minimiseStorageOverheads(); + minimiseStorageAfterRemoval(); } } /** 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 + @see remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ void removeLast (int howManyToRemove = 1) { @@ -820,15 +874,13 @@ public: data.elements [numUsed - i].~ElementType(); numUsed -= howManyToRemove; - - if ((numUsed << 1) < data.numAllocated) - minimiseStorageOverheads(); + minimiseStorageAfterRemoval(); } /** 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 + @see removeValuesNotIn, remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ template void removeValuesIn (const OtherArrayType& otherArray) @@ -846,7 +898,7 @@ public: { for (int i = numUsed; --i >= 0;) if (otherArray.contains (data.elements [i])) - remove (i); + removeInternal (i); } } } @@ -856,7 +908,7 @@ public: 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 + @see removeValuesIn, remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ template void removeValuesNotIn (const OtherArrayType& otherArray) @@ -874,7 +926,7 @@ public: { for (int i = numUsed; --i >= 0;) if (! otherArray.contains (data.elements [i])) - remove (i); + removeInternal (i); } } } @@ -895,8 +947,8 @@ public: if (isPositiveAndBelow (index1, numUsed) && isPositiveAndBelow (index2, numUsed)) { - swapVariables (data.elements [index1], - data.elements [index2]); + std::swap (data.elements [index1], + data.elements [index2]); } } @@ -1019,17 +1071,43 @@ public: typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; + //============================================================================== + #ifndef DOXYGEN + // Note that the swapWithArray method has been replaced by a more flexible templated version, + // and renamed "swapWith" to be more consistent with the names used in other classes. + JUCE_DEPRECATED_WITH_BODY (void swapWithArray (Array& other) noexcept, { swapWith (other); }) + #endif + private: //============================================================================== ArrayAllocationBase data; int numUsed; + void removeInternal (const int indexToRemove) + { + --numUsed; + ElementType* const e = data.elements + indexToRemove; + e->~ElementType(); + const int numberToShift = numUsed - indexToRemove; + + if (numberToShift > 0) + memmove (e, e + 1, ((size_t) numberToShift) * sizeof (ElementType)); + + minimiseStorageAfterRemoval(); + } + inline void deleteAllElements() noexcept { for (int i = 0; i < numUsed; ++i) data.elements[i].~ElementType(); } + + void minimiseStorageAfterRemoval() + { + if (data.numAllocated > jmax (minimumAllocatedSize, numUsed * 2)) + data.shrinkToNoMoreThan (jmax (numUsed, jmax (minimumAllocatedSize, 64 / (int) sizeof (ElementType)))); + } }; -#endif // __JUCE_ARRAY_JUCEHEADER__ +#endif // JUCE_ARRAY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h index 7c72c93..cfbe464 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ArrayAllocationBase.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ -#define __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ - -#include "../memory/juce_HeapBlock.h" +#ifndef JUCE_ARRAYALLOCATIONBASE_H_INCLUDED +#define JUCE_ARRAYALLOCATIONBASE_H_INCLUDED //============================================================================== @@ -105,6 +106,8 @@ public: { if (minNumElements > numAllocated) setAllocatedSize ((minNumElements + minNumElements / 2 + 8) & ~7); + + jassert (numAllocated <= 0 || elements != nullptr); } /** Minimises the amount of storage allocated so that it's no more than @@ -128,8 +131,8 @@ public: int numAllocated; private: - JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase); + JUCE_DECLARE_NON_COPYABLE (ArrayAllocationBase) }; -#endif // __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ +#endif // JUCE_ARRAYALLOCATIONBASE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp index c0afb55..38f5c5f 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -27,6 +30,11 @@ DynamicObject::DynamicObject() { } +DynamicObject::DynamicObject (const DynamicObject& other) + : ReferenceCountedObject(), properties (other.properties) +{ +} + DynamicObject::~DynamicObject() { } @@ -57,20 +65,69 @@ bool DynamicObject::hasMethod (const Identifier& methodName) const return getProperty (methodName).isMethod(); } -var DynamicObject::invokeMethod (const Identifier& methodName, - const var* parameters, - int numParameters) +var DynamicObject::invokeMethod (Identifier method, const var::NativeFunctionArgs& args) { - return properties [methodName].invokeMethod (this, parameters, numParameters); + if (var::NativeFunction function = properties [method].getNativeFunction()) + return function (args); + + return var(); } -void DynamicObject::setMethod (const Identifier& name, - var::MethodFunction methodFunction) +void DynamicObject::setMethod (Identifier name, var::NativeFunction function) { - properties.set (name, var (methodFunction)); + properties.set (name, var (function)); } void DynamicObject::clear() { properties.clear(); } + +void DynamicObject::cloneAllProperties() +{ + for (int i = properties.size(); --i >= 0;) + if (var* v = properties.getVarPointerAt (i)) + *v = v->clone(); +} + +DynamicObject::Ptr DynamicObject::clone() +{ + Ptr d (new DynamicObject (*this)); + d->cloneAllProperties(); + return d; +} + +void DynamicObject::writeAsJSON (OutputStream& out, const int indentLevel, const bool allOnOneLine) +{ + out << '{'; + if (! allOnOneLine) + out << newLine; + + const int numValues = properties.size(); + + for (int i = 0; i < numValues; ++i) + { + if (! allOnOneLine) + JSONFormatter::writeSpaces (out, indentLevel + JSONFormatter::indentSize); + + out << '"'; + JSONFormatter::writeString (out, properties.getName (i)); + out << "\": "; + JSONFormatter::write (out, properties.getValueAt (i), indentLevel + JSONFormatter::indentSize, allOnOneLine); + + if (i < numValues - 1) + { + if (allOnOneLine) + out << ", "; + else + out << ',' << newLine; + } + else if (! allOnOneLine) + out << newLine; + } + + if (! allOnOneLine) + JSONFormatter::writeSpaces (out, indentLevel); + + out << '}'; +} diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h index d899cb0..5c624a4 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_DynamicObject.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_DYNAMICOBJECT_JUCEHEADER__ -#define __JUCE_DYNAMICOBJECT_JUCEHEADER__ - -#include "juce_NamedValueSet.h" -#include "../memory/juce_ReferenceCountedObject.h" +#ifndef JUCE_DYNAMICOBJECT_H_INCLUDED +#define JUCE_DYNAMICOBJECT_H_INCLUDED //============================================================================== @@ -46,9 +46,10 @@ class JUCE_API DynamicObject : public ReferenceCountedObject public: //============================================================================== DynamicObject(); + DynamicObject (const DynamicObject&); + ~DynamicObject(); - /** Destructor. */ - virtual ~DynamicObject(); + typedef ReferenceCountedObjectPtr Ptr; //============================================================================== /** Returns true if the object has a property with this name. @@ -57,8 +58,7 @@ public: virtual bool hasProperty (const Identifier& propertyName) const; /** Returns a named property. - - This returns a void if no such property exists. + This returns var::null if no such property exists. */ virtual var getProperty (const Identifier& propertyName) const; @@ -85,23 +85,16 @@ public: This method is virtual to allow more dynamic invocation to used for objects where the methods may not already be set as properies. */ - virtual var invokeMethod (const Identifier& methodName, - const var* parameters, - int numParameters); + virtual var invokeMethod (Identifier methodName, + const var::NativeFunctionArgs& args); - /** Sets up a method. + /** Adds a method to the class. - This is basically the same as calling setProperty (methodName, (var::MethodFunction) myFunction), but + This is basically the same as calling setProperty (methodName, (var::NativeFunction) 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 Identifier& methodName, - var::MethodFunction methodFunction); + void setMethod (Identifier methodName, var::NativeFunction function); //============================================================================== /** Removes all properties and methods from the object. */ @@ -110,13 +103,37 @@ public: /** Returns the NamedValueSet that holds the object's properties. */ NamedValueSet& getProperties() noexcept { return properties; } + /** Calls var::clone() on all the properties that this object contains. */ + void cloneAllProperties(); + + //============================================================================== + /** Returns a clone of this object. + The default implementation of this method just returns a new DynamicObject + with a (deep) copy of all of its properties. Subclasses can override this to + implement their own custom copy routines. + */ + virtual Ptr clone(); + + //============================================================================== + /** Writes this object to a text stream in JSON format. + This method is used by JSON::toString and JSON::writeToStream, and you should + never need to call it directly, but it's virtual so that custom object types + can stringify themselves appropriately. + */ + virtual void writeAsJSON (OutputStream&, int indentLevel, bool allOnOneLine); + private: //============================================================================== NamedValueSet properties; - JUCE_LEAK_DETECTOR (DynamicObject); + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // These methods have been deprecated - use var::invoke instead + virtual void invokeMethod (const Identifier&, const var*, int) {} + #endif + + JUCE_LEAK_DETECTOR (DynamicObject) }; -#endif // __JUCE_DYNAMICOBJECT_JUCEHEADER__ +#endif // JUCE_DYNAMICOBJECT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h b/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h index f976c40..01dc5b7 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ElementComparator.h @@ -1,30 +1,54 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ -#define __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ +#ifndef JUCE_ELEMENTCOMPARATOR_H_INCLUDED +#define JUCE_ELEMENTCOMPARATOR_H_INCLUDED + +#ifndef DOXYGEN + +/** This is an internal helper class which converts a juce ElementComparator style + class (using a "compareElements" method) into a class that's compatible with + std::sort (i.e. using an operator() to compare the elements) +*/ +template +struct SortFunctionConverter +{ + SortFunctionConverter (ElementComparator& e) : comparator (e) {} + + template + bool operator() (Type a, Type b) { return comparator.compareElements (a, b) < 0; } + +private: + ElementComparator& comparator; + SortFunctionConverter& operator= (const SortFunctionConverter&) JUCE_DELETED_FUNCTION; +}; + +#endif //============================================================================== @@ -62,117 +86,12 @@ static void sortArray (ElementComparator& comparator, int lastElement, const bool retainOrderOfEquivalentItems) { - (void) comparator; // if you pass in an object with a static compareElements() method, this - // avoids getting warning messages about the parameter being unused + SortFunctionConverter converter (comparator); - if (lastElement > firstElement) - { - if (retainOrderOfEquivalentItems) - { - for (int i = firstElement; i < lastElement; ++i) - { - if (comparator.compareElements (array[i], array [i + 1]) > 0) - { - std::swap (array[i], array[i + 1]); - - if (i > firstElement) - i -= 2; - } - } - } - else - { - int fromStack[30], toStack[30]; - int stackIndex = 0; - - for (;;) - { - const int size = (lastElement - firstElement) + 1; - - if (size <= 8) - { - int j = lastElement; - int maxIndex; - - while (j > firstElement) - { - maxIndex = firstElement; - for (int k = firstElement + 1; k <= j; ++k) - if (comparator.compareElements (array[k], array [maxIndex]) > 0) - maxIndex = k; - - std::swap (array[j], array[maxIndex]); - --j; - } - } - else - { - const int mid = firstElement + (size >> 1); - std::swap (array[mid], array[firstElement]); - - int i = firstElement; - int j = lastElement + 1; - - for (;;) - { - while (++i <= lastElement - && comparator.compareElements (array[i], array [firstElement]) <= 0) - {} - - while (--j > firstElement - && comparator.compareElements (array[j], array [firstElement]) >= 0) - {} - - if (j < i) - break; - - std::swap (array[i], array[j]); - } - - std::swap (array[j], array[firstElement]); - - if (j - 1 - firstElement >= lastElement - i) - { - if (firstElement + 1 < j) - { - fromStack [stackIndex] = firstElement; - toStack [stackIndex] = j - 1; - ++stackIndex; - } - - if (i < lastElement) - { - firstElement = i; - continue; - } - } - else - { - if (i < lastElement) - { - fromStack [stackIndex] = i; - toStack [stackIndex] = lastElement; - ++stackIndex; - } - - if (firstElement + 1 < j) - { - lastElement = j - 1; - continue; - } - } - } - - if (--stackIndex < 0) - break; - - jassert (stackIndex < numElementsInArray (fromStack)); - - firstElement = fromStack [stackIndex]; - lastElement = toStack [stackIndex]; - } - } - } + if (retainOrderOfEquivalentItems) + std::stable_sort (array + firstElement, array + lastElement + 1, converter); + else + std::sort (array + firstElement, array + lastElement + 1, converter); } @@ -273,4 +192,4 @@ public: }; -#endif // __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ +#endif // JUCE_ELEMENTCOMPARATOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h index 5ff8c59..07d02b4 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_HashMap.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_HASHMAP_JUCEHEADER__ -#define __JUCE_HASHMAP_JUCEHEADER__ - -#include "juce_OwnedArray.h" -#include "juce_LinkedListPointer.h" -#include "../memory/juce_ScopedPointer.h" +#ifndef JUCE_HASHMAP_H_INCLUDED +#define JUCE_HASHMAP_H_INCLUDED //============================================================================== @@ -37,15 +36,16 @@ use with the HashMap class. @see HashMap */ -class DefaultHashFunctions +struct DefaultHashFunctions { -public: /** Generates a simple hash from an integer. */ - static int generateHash (const int key, const int upperLimit) noexcept { return std::abs (key) % upperLimit; } + int generateHash (const int key, const int upperLimit) const noexcept { return std::abs (key) % upperLimit; } + /** Generates a simple hash from an int64. */ + int generateHash (const int64 key, const int upperLimit) const noexcept { return std::abs ((int) key) % upperLimit; } /** Generates a simple hash from a string. */ - static int generateHash (const String& key, const int upperLimit) noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); } + int generateHash (const String& key, const int upperLimit) const noexcept { return (int) (((uint32) key.hashCode()) % (uint32) upperLimit); } /** Generates a simple hash from a variant. */ - static int generateHash (const var& key, const int upperLimit) noexcept { return generateHash (key.toString(), upperLimit); } + int generateHash (const var& key, const int upperLimit) const noexcept { return generateHash (key.toString(), upperLimit); } }; @@ -60,7 +60,7 @@ public: @code struct MyHashGenerator { - static int generateHash (MyKeyType key, int upperLimit) + int generateHash (MyKeyType key, int upperLimit) const { // The function must return a value 0 <= x < upperLimit return someFunctionOfMyKeyType (key) % upperLimit; @@ -68,11 +68,11 @@ public: }; @endcode - Like the Array class, the key and value types are expected to be copy-by-value types, so - if you define them to be pointer types, this class won't delete the objects that they - point to. + Like the Array class, the key and value types are expected to be copy-by-value + types, so if you define them to be pointer types, this class won't delete the + objects that they point to. - If you don't supply a class for the HashFunctionToUse template parameter, the + If you don't supply a class for the HashFunctionType template parameter, the default one provides some simple mappings for strings and ints. @code @@ -88,11 +88,12 @@ public: DBG (i.getKey() << " -> " << i.getValue()); @endcode + @tparam HashFunctionType The class of hash function, which must be copy-constructible. @see CriticalSection, DefaultHashFunctions, NamedValueSet, SortedSet */ template class HashMap { @@ -104,14 +105,19 @@ public: //============================================================================== /** Creates an empty hash-map. - The numberOfSlots parameter specifies the number of hash entries the map will use. This - will be the "upperLimit" parameter that is passed to your generateHash() function. The number - of hash slots will grow automatically if necessary, or it can be remapped manually using remapTable(). + @param numberOfSlots Specifies the number of hash entries the map will use. This will be + the "upperLimit" parameter that is passed to your generateHash() + function. The number of hash slots will grow automatically if necessary, + or it can be remapped manually using remapTable(). + @param hashFunction An instance of HashFunctionType, which will be copied and + stored to use with the HashMap. This parameter can be omitted + if HashFunctionType has a default constructor. */ - explicit HashMap (const int numberOfSlots = defaultHashTableSize) - : totalNumItems (0) + explicit HashMap (int numberOfSlots = defaultHashTableSize, + HashFunctionType hashFunction = HashFunctionType()) + : hashFunctionToUse (hashFunction), totalNumItems (0) { - slots.insertMultiple (0, nullptr, numberOfSlots); + hashSlots.insertMultiple (0, nullptr, numberOfSlots); } /** Destructor. */ @@ -129,9 +135,9 @@ public: { const ScopedLockType sl (getLock()); - for (int i = slots.size(); --i >= 0;) + for (int i = hashSlots.size(); --i >= 0;) { - HashEntry* h = slots.getUnchecked(i); + HashEntry* h = hashSlots.getUnchecked(i); while (h != nullptr) { @@ -139,7 +145,7 @@ public: h = h->nextEntry; } - slots.set (i, nullptr); + hashSlots.set (i, nullptr); } totalNumItems = 0; @@ -156,11 +162,11 @@ public: If the map doesn't contain the key, a default instance of the value type is returned. @param keyToLookFor the key of the item being requested */ - inline const ValueType operator[] (KeyTypeParameter keyToLookFor) const + inline ValueType operator[] (KeyTypeParameter keyToLookFor) const { const ScopedLockType sl (getLock()); - for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = hashSlots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return entry->value; @@ -173,7 +179,7 @@ public: { const ScopedLockType sl (getLock()); - for (const HashEntry* entry = slots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = hashSlots.getUnchecked (generateHashFor (keyToLookFor)); entry != nullptr; entry = entry->nextEntry) if (entry->key == keyToLookFor) return true; @@ -186,7 +192,7 @@ public: const ScopedLockType sl (getLock()); for (int i = getNumSlots(); --i >= 0;) - for (const HashEntry* entry = slots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) if (entry->value == valueToLookFor) return true; @@ -203,7 +209,7 @@ public: const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (newKey); - HashEntry* const firstEntry = slots.getUnchecked (hashIndex); + HashEntry* const firstEntry = hashSlots.getUnchecked (hashIndex); for (HashEntry* entry = firstEntry; entry != nullptr; entry = entry->nextEntry) { @@ -214,7 +220,7 @@ public: } } - slots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); + hashSlots.set (hashIndex, new HashEntry (newKey, newValue, firstEntry)); ++totalNumItems; if (totalNumItems > (getNumSlots() * 3) / 2) @@ -226,7 +232,7 @@ public: { const ScopedLockType sl (getLock()); const int hashIndex = generateHashFor (keyToRemove); - HashEntry* entry = slots.getUnchecked (hashIndex); + HashEntry* entry = hashSlots.getUnchecked (hashIndex); HashEntry* previous = nullptr; while (entry != nullptr) @@ -240,7 +246,7 @@ public: if (previous != nullptr) previous->nextEntry = entry; else - slots.set (hashIndex, entry); + hashSlots.set (hashIndex, entry); --totalNumItems; } @@ -259,7 +265,7 @@ public: for (int i = getNumSlots(); --i >= 0;) { - HashEntry* entry = slots.getUnchecked(i); + HashEntry* entry = hashSlots.getUnchecked(i); HashEntry* previous = nullptr; while (entry != nullptr) @@ -273,7 +279,7 @@ public: if (previous != nullptr) previous->nextEntry = entry; else - slots.set (i, entry); + hashSlots.set (i, entry); --totalNumItems; } @@ -295,7 +301,7 @@ public: HashMap newTable (newNumberOfSlots); for (int i = getNumSlots(); --i >= 0;) - for (const HashEntry* entry = slots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) + for (const HashEntry* entry = hashSlots.getUnchecked(i); entry != nullptr; entry = entry->nextEntry) newTable.set (entry->key, entry->value); swapWith (newTable); @@ -307,17 +313,18 @@ public: */ inline int getNumSlots() const noexcept { - return slots.size(); + return hashSlots.size(); } //============================================================================== /** Efficiently swaps the contents of two hash-maps. */ - void swapWith (HashMap& otherHashMap) noexcept + template + void swapWith (OtherHashMapType& otherHashMap) noexcept { const ScopedLockType lock1 (getLock()); - const ScopedLockType lock2 (otherHashMap.getLock()); + const typename OtherHashMapType::ScopedLockType lock2 (otherHashMap.getLock()); - slots.swapWithArray (otherHashMap.slots); + hashSlots.swapWith (otherHashMap.hashSlots); std::swap (totalNumItems, otherHashMap.totalNumItems); } @@ -336,15 +343,15 @@ private: class HashEntry { public: - HashEntry (KeyTypeParameter key_, ValueTypeParameter value_, HashEntry* const nextEntry_) - : key (key_), value (value_), nextEntry (nextEntry_) + HashEntry (KeyTypeParameter k, ValueTypeParameter val, HashEntry* const next) + : key (k), value (val), nextEntry (next) {} const KeyType key; ValueType value; HashEntry* nextEntry; - JUCE_DECLARE_NON_COPYABLE (HashEntry); + JUCE_DECLARE_NON_COPYABLE (HashEntry) }; public: @@ -393,7 +400,7 @@ public: if (index >= hashMap.getNumSlots()) return false; - entry = hashMap.slots.getUnchecked (index++); + entry = hashMap.hashSlots.getUnchecked (index++); } return true; @@ -421,7 +428,7 @@ public: HashEntry* entry; int index; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Iterator) }; private: @@ -429,19 +436,20 @@ private: enum { defaultHashTableSize = 101 }; friend class Iterator; - Array slots; + HashFunctionType hashFunctionToUse; + Array hashSlots; int totalNumItems; TypeOfCriticalSectionToUse lock; int generateHashFor (KeyTypeParameter key) const { - const int hash = HashFunctionToUse::generateHash (key, getNumSlots()); + const int hash = hashFunctionToUse.generateHash (key, getNumSlots()); jassert (isPositiveAndBelow (hash, getNumSlots())); // your hash function is generating out-of-range numbers! return hash; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HashMap) }; -#endif // __JUCE_HASHMAP_JUCEHEADER__ +#endif // JUCE_HASHMAP_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h b/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h index f0f7892..a27e521 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_LINKEDLISTPOINTER_JUCEHEADER__ -#define __JUCE_LINKEDLISTPOINTER_JUCEHEADER__ +#ifndef JUCE_LINKEDLISTPOINTER_H_INCLUDED +#define JUCE_LINKEDLISTPOINTER_H_INCLUDED //============================================================================== @@ -218,7 +221,7 @@ public: ObjectType* const oldItem = item; item = newItem; item->nextListItem = oldItem->nextListItem.item; - oldItem->nextListItem = (ObjectType*) 0; + oldItem->nextListItem.item = nullptr; return oldItem; } @@ -259,7 +262,7 @@ public: if (oldItem != nullptr) { item = oldItem->nextListItem; - oldItem->nextListItem = (ObjectType*) 0; + oldItem->nextListItem.item = nullptr; } return oldItem; @@ -270,9 +273,7 @@ public: */ void remove (ObjectType* const itemToRemove) { - LinkedListPointer* const l = findPointerTo (itemToRemove); - - if (l != nullptr) + if (LinkedListPointer* const l = findPointerTo (itemToRemove)) l->removeNext(); } @@ -356,15 +357,15 @@ public: private: LinkedListPointer* endOfList; - JUCE_DECLARE_NON_COPYABLE (Appender); + JUCE_DECLARE_NON_COPYABLE (Appender) }; private: //============================================================================== ObjectType* item; - JUCE_DECLARE_NON_COPYABLE (LinkedListPointer); + JUCE_DECLARE_NON_COPYABLE (LinkedListPointer) }; -#endif // __JUCE_LINKEDLISTPOINTER_JUCEHEADER__ +#endif // JUCE_LINKEDLISTPOINTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp index 27a5232..c4e198d 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.cpp @@ -1,75 +1,62 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -NamedValueSet::NamedValue::NamedValue() noexcept +struct NamedValueSet::NamedValue { -} + NamedValue() noexcept {} + NamedValue (Identifier n, const var& v) : name (n), value (v) {} + NamedValue (const NamedValue& other) : name (other.name), value (other.value) {} -inline NamedValueSet::NamedValue::NamedValue (const Identifier& name_, const var& value_) - : name (name_), value (value_) -{ -} + #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + NamedValue (NamedValue&& other) noexcept + : name (static_cast (other.name)), + value (static_cast (other.value)) + { + } -NamedValueSet::NamedValue::NamedValue (const NamedValue& other) - : name (other.name), value (other.value) -{ -} + NamedValue (Identifier n, var&& v) : name (n), value (static_cast (v)) + { + } -NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (const NamedValueSet::NamedValue& other) -{ - name = other.name; - value = other.value; - return *this; -} + NamedValue& operator= (NamedValue&& other) noexcept + { + name = static_cast (other.name); + value = static_cast (other.value); + return *this; + } + #endif -#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS -NamedValueSet::NamedValue::NamedValue (NamedValue&& other) noexcept - : nextListItem (static_cast &&> (other.nextListItem)), - name (static_cast (other.name)), - value (static_cast (other.value)) -{ -} + bool operator== (const NamedValue& other) const noexcept { return name == other.name && value == other.value; } + bool operator!= (const NamedValue& other) const noexcept { return ! operator== (other); } -inline NamedValueSet::NamedValue::NamedValue (const Identifier& name_, var&& value_) - : name (name_), value (static_cast (value_)) -{ -} - -NamedValueSet::NamedValue& NamedValueSet::NamedValue::operator= (NamedValue&& other) noexcept -{ - nextListItem = static_cast &&> (other.nextListItem); - name = static_cast (other.name); - value = static_cast (other.value); - return *this; -} -#endif - -bool NamedValueSet::NamedValue::operator== (const NamedValueSet::NamedValue& other) const noexcept -{ - return name == other.name && value == other.value; -} + Identifier name; + var value; +}; //============================================================================== NamedValueSet::NamedValueSet() noexcept @@ -77,20 +64,20 @@ NamedValueSet::NamedValueSet() noexcept } NamedValueSet::NamedValueSet (const NamedValueSet& other) + : values (other.values) { - values.addCopyOfList (other.values); } NamedValueSet& NamedValueSet::operator= (const NamedValueSet& other) { clear(); - values.addCopyOfList (other.values); + values = other.values; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS NamedValueSet::NamedValueSet (NamedValueSet&& other) noexcept - : values (static_cast &&> (other.values)) + : values (static_cast &&> (other.values)) { } @@ -108,24 +95,12 @@ NamedValueSet::~NamedValueSet() void NamedValueSet::clear() { - values.deleteAll(); + values.clear(); } bool NamedValueSet::operator== (const NamedValueSet& other) const { - const NamedValue* i1 = values; - const NamedValue* i2 = other.values; - - while (i1 != nullptr && i2 != nullptr) - { - if (! (*i1 == *i2)) - return false; - - i1 = i1->nextListItem; - i2 = i2->nextListItem; - } - - return true; + return values == other.values; } bool NamedValueSet::operator!= (const NamedValueSet& other) const @@ -140,22 +115,23 @@ int NamedValueSet::size() const noexcept const var& NamedValueSet::operator[] (const Identifier& name) const { - for (NamedValue* i = values; i != nullptr; i = i->nextListItem) - if (i->name == name) - return i->value; + if (const var* v = getVarPointer (name)) + return *v; return var::null; } var NamedValueSet::getWithDefault (const Identifier& name, const var& defaultReturnValue) const { - const var* const v = getVarPointer (name); - return v != nullptr ? *v : defaultReturnValue; + if (const var* const v = getVarPointer (name)) + return *v; + + return defaultReturnValue; } var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept { - for (NamedValue* i = values; i != nullptr; i = i->nextListItem) + for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) if (i->name == name) return &(i->value); @@ -163,52 +139,34 @@ var* NamedValueSet::getVarPointer (const Identifier& name) const noexcept } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS -bool NamedValueSet::set (const Identifier& name, var&& newValue) +bool NamedValueSet::set (Identifier name, var&& newValue) { - LinkedListPointer* i = &values; - - while (i->get() != nullptr) + if (var* const v = getVarPointer (name)) { - NamedValue* const v = i->get(); + if (v->equalsWithSameType (newValue)) + return false; - if (v->name == name) - { - if (v->value.equalsWithSameType (newValue)) - return false; - - v->value = static_cast (newValue); - return true; - } - - i = &(v->nextListItem); + *v = static_cast (newValue); + return true; } - i->insertNext (new NamedValue (name, static_cast (newValue))); + values.add (NamedValue (name, static_cast (newValue))); return true; } #endif -bool NamedValueSet::set (const Identifier& name, const var& newValue) +bool NamedValueSet::set (Identifier name, const var& newValue) { - LinkedListPointer* i = &values; - - while (i->get() != nullptr) + if (var* const v = getVarPointer (name)) { - NamedValue* const v = i->get(); + if (v->equalsWithSameType (newValue)) + return false; - if (v->name == name) - { - if (v->value.equalsWithSameType (newValue)) - return false; - - v->value = newValue; - return true; - } - - i = &(v->nextListItem); + *v = newValue; + return true; } - i->insertNext (new NamedValue (name, newValue)); + values.add (NamedValue (name, newValue)); return true; } @@ -217,61 +175,97 @@ bool NamedValueSet::contains (const Identifier& name) const return getVarPointer (name) != nullptr; } +int NamedValueSet::indexOf (const Identifier& name) const noexcept +{ + const int numValues = values.size(); + + for (int i = 0; i < numValues; ++i) + if (values.getReference(i).name == name) + return i; + + return -1; +} + bool NamedValueSet::remove (const Identifier& name) { - LinkedListPointer* i = &values; + const int numValues = values.size(); - for (;;) + for (int i = 0; i < numValues; ++i) { - NamedValue* const v = i->get(); - - if (v == nullptr) - break; - - if (v->name == name) + if (values.getReference(i).name == name) { - delete i->removeNext(); + values.remove (i); return true; } - - i = &(v->nextListItem); } return false; } -const Identifier NamedValueSet::getName (const int index) const +Identifier NamedValueSet::getName (const int index) const noexcept { - const NamedValue* const v = values[index]; - jassert (v != nullptr); - return v->name; + if (isPositiveAndBelow (index, values.size())) + return values.getReference (index).name; + + jassertfalse; + return Identifier(); } -const var& NamedValueSet::getValueAt (const int index) const +const var& NamedValueSet::getValueAt (const int index) const noexcept { - const NamedValue* const v = values[index]; - jassert (v != nullptr); - return v->value; + if (isPositiveAndBelow (index, values.size())) + return values.getReference (index).value; + + jassertfalse; + return var::null; +} + +var* NamedValueSet::getVarPointerAt (int index) const noexcept +{ + if (isPositiveAndBelow (index, values.size())) + return &(values.getReference (index).value); + + return nullptr; } void NamedValueSet::setFromXmlAttributes (const XmlElement& xml) { - clear(); - LinkedListPointer::Appender appender (values); + values.clearQuick(); - const int numAtts = xml.getNumAttributes(); // xxx inefficient - should write an att iterator.. + for (const XmlElement::XmlAttributeNode* att = xml.attributes; att != nullptr; att = att->nextListItem) + { + if (att->name.toString().startsWith ("base64:")) + { + MemoryBlock mb; - for (int i = 0; i < numAtts; ++i) - appender.append (new NamedValue (xml.getAttributeName (i), var (xml.getAttributeValue (i)))); + if (mb.fromBase64Encoding (att->value)) + { + values.add (NamedValue (att->name.toString().substring (7), var (mb))); + continue; + } + } + + values.add (NamedValue (att->name, var (att->value))); + } } void NamedValueSet::copyToXmlAttributes (XmlElement& xml) const { - for (NamedValue* i = values; i != nullptr; i = i->nextListItem) + for (NamedValue* e = values.end(), *i = values.begin(); i != e; ++i) { - jassert (! i->value.isObject()); // DynamicObjects can't be stored as XML! + if (const MemoryBlock* mb = i->value.getBinaryData()) + { + xml.setAttribute ("base64:" + i->name.toString(), mb->toBase64Encoding()); + } + else + { + // These types can't be stored as XML! + jassert (! i->value.isObject()); + jassert (! i->value.isMethod()); + jassert (! i->value.isArray()); - xml.setAttribute (i->name.toString(), - i->value.toString()); + xml.setAttribute (i->name.toString(), + i->value.toString()); + } } } diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h index ded2011..0216326 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_NamedValueSet.h @@ -1,37 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_NAMEDVALUESET_JUCEHEADER__ -#define __JUCE_NAMEDVALUESET_JUCEHEADER__ - -#include "juce_Variant.h" -#include "../containers/juce_LinkedListPointer.h" -class XmlElement; -#ifndef DOXYGEN - class JSONFormatter; -#endif +#ifndef JUCE_NAMEDVALUESET_H_INCLUDED +#define JUCE_NAMEDVALUESET_H_INCLUDED //============================================================================== @@ -47,21 +43,21 @@ public: NamedValueSet() noexcept; /** Creates a copy of another set. */ - NamedValueSet (const NamedValueSet& other); + NamedValueSet (const NamedValueSet&); /** Replaces this set with a copy of another set. */ - NamedValueSet& operator= (const NamedValueSet& other); + NamedValueSet& operator= (const NamedValueSet&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - NamedValueSet (NamedValueSet&& other) noexcept; - NamedValueSet& operator= (NamedValueSet&& other) noexcept; + NamedValueSet (NamedValueSet&&) noexcept; + NamedValueSet& operator= (NamedValueSet&&) noexcept; #endif /** Destructor. */ ~NamedValueSet(); - bool operator== (const NamedValueSet& other) const; - bool operator!= (const NamedValueSet& other) const; + bool operator== (const NamedValueSet&) const; + bool operator!= (const NamedValueSet&) const; //============================================================================== /** Returns the total number of values that the set contains. */ @@ -80,16 +76,16 @@ public: /** 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. + value was already set the value passed-in. */ - bool set (const Identifier& name, const var& newValue); + bool set (Identifier name, const var& newValue); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS /** 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. + value was already set the value passed-in. */ - bool set (const Identifier& name, var&& newValue); + bool set (Identifier name, var&& newValue); #endif /** Returns true if the set contains an item with the specified name. */ @@ -104,17 +100,8 @@ public: /** Returns the name of the value at a given index. The index must be between 0 and size() - 1. */ - const Identifier getName (int index) const; + Identifier getName (int index) const noexcept; - /** Returns the value of the item at a given index. - The index must be between 0 and size() - 1. - */ - const var& getValueAt (int index) const; - - /** Removes all values. */ - void clear(); - - //============================================================================== /** Returns a pointer to the var that holds a named value, or null if there is no value with this name. @@ -123,6 +110,22 @@ public: */ var* getVarPointer (const Identifier& name) const noexcept; + /** Returns the value of the item at a given index. + The index must be between 0 and size() - 1. + */ + const var& getValueAt (int index) const noexcept; + + /** Returns the value of the item at a given index. + The index must be between 0 and size() - 1, or this will return a nullptr + */ + var* getVarPointerAt (int index) const noexcept; + + /** Returns the index of the given name, or -1 if it's not found. */ + int indexOf (const Identifier& name) const noexcept; + + /** Removes all values. */ + void clear(); + //============================================================================== /** Sets properties to the values of all of an XML element's attributes. */ void setFromXmlAttributes (const XmlElement& xml); @@ -134,33 +137,9 @@ public: private: //============================================================================== - class NamedValue - { - public: - NamedValue() noexcept; - NamedValue (const NamedValue&); - NamedValue (const Identifier& name, const var& value); - NamedValue& operator= (const NamedValue&); - #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - NamedValue (NamedValue&&) noexcept; - NamedValue (const Identifier& name, var&& value); - NamedValue& operator= (NamedValue&&) noexcept; - #endif - bool operator== (const NamedValue& other) const noexcept; - - LinkedListPointer nextListItem; - Identifier name; - var value; - - private: - JUCE_LEAK_DETECTOR (NamedValue); - }; - - friend class LinkedListPointer; - LinkedListPointer values; - - friend class JSONFormatter; + struct NamedValue; + Array values; }; -#endif // __JUCE_NAMEDVALUESET_JUCEHEADER__ +#endif // JUCE_NAMEDVALUESET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h index 96c25c2..a116df8 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__ -#define __JUCE_OWNEDARRAY_JUCEHEADER__ - -#include "juce_ArrayAllocationBase.h" -#include "juce_ElementComparator.h" -#include "../threads/juce_CriticalSection.h" +#ifndef JUCE_OWNEDARRAY_H_INCLUDED +#define JUCE_OWNEDARRAY_H_INCLUDED //============================================================================== @@ -70,7 +69,7 @@ public: */ ~OwnedArray() { - clear (true); + deleteAllObjects(); } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS @@ -83,6 +82,9 @@ public: OwnedArray& operator= (OwnedArray&& other) noexcept { + const ScopedLockType lock (getLock()); + deleteAllObjects(); + data = static_cast &&> (other.data); numUsed = other.numUsed; other.numUsed = 0; @@ -92,20 +94,29 @@ public: //============================================================================== /** Clears the array, optionally deleting the objects inside it first. */ - void clear (const bool deleteObjects = true) + void clear (bool deleteObjects = true) { const ScopedLockType lock (getLock()); if (deleteObjects) - { - while (numUsed > 0) - delete data.elements [--numUsed]; - } + deleteAllObjects(); data.setAllocatedSize (0); numUsed = 0; } + //============================================================================== + /** Clears the array, optionally deleting the objects inside it first. */ + void clearQuick (bool deleteObjects) + { + const ScopedLockType lock (getLock()); + + if (deleteObjects) + deleteAllObjects(); + + numUsed = 0; + } + //============================================================================== /** Returns the number of items currently in the array. @see operator[] @@ -126,19 +137,24 @@ public: inline ObjectClass* operator[] (const int index) const noexcept { const ScopedLockType lock (getLock()); - return isPositiveAndBelow (index, numUsed) ? data.elements [index] - : static_cast (nullptr); + if (isPositiveAndBelow (index, numUsed)) + { + jassert (data.elements != nullptr); + return data.elements [index]; + } + + return nullptr; } /** 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. + it can be used when you're sure the index is always going to be legal. */ inline ObjectClass* getUnchecked (const int index) const noexcept { const ScopedLockType lock (getLock()); - jassert (isPositiveAndBelow (index, numUsed)); + jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } @@ -150,8 +166,14 @@ public: inline ObjectClass* getFirst() const noexcept { const ScopedLockType lock (getLock()); - return numUsed > 0 ? data.elements [0] - : static_cast (nullptr); + + if (numUsed > 0) + { + jassert (data.elements != nullptr); + return data.elements [0]; + } + + return nullptr; } /** Returns a pointer to the last object in the array. @@ -162,8 +184,14 @@ public: inline ObjectClass* getLast() const noexcept { const ScopedLockType lock (getLock()); - return numUsed > 0 ? data.elements [numUsed - 1] - : static_cast (nullptr); + + if (numUsed > 0) + { + jassert (data.elements != nullptr); + return data.elements [numUsed - 1]; + } + + return nullptr; } /** Returns a pointer to the actual array data. @@ -189,6 +217,11 @@ public: */ inline ObjectClass** end() const noexcept { + #if JUCE_DEBUG + if (data.elements == nullptr || numUsed <= 0) // (to keep static analysers happy) + return data.elements; + #endif + return data.elements + numUsed; } @@ -198,7 +231,7 @@ public: @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 noexcept + int indexOf (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); ObjectClass* const* e = data.elements.getData(); @@ -216,7 +249,7 @@ public: @param objectToLookFor the object to look for @returns true if the object is in the array */ - bool contains (const ObjectClass* const objectToLookFor) const noexcept + bool contains (const ObjectClass* objectToLookFor) const noexcept { const ScopedLockType lock (getLock()); ObjectClass* const* e = data.elements.getData(); @@ -238,14 +271,17 @@ public: 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 + @param newObject the new object to add to the array + @returns the new object that was added @see set, insert, addIfNotAlreadyThere, addSorted */ - void add (const ObjectClass* const newObject) noexcept + ObjectClass* add (ObjectClass* newObject) noexcept { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); - data.elements [numUsed++] = const_cast (newObject); + jassert (data.elements != nullptr); + data.elements [numUsed++] = newObject; + return newObject; } /** Inserts a new object into the array at the given index. @@ -263,32 +299,70 @@ public: @param indexToInsertAt the index at which the new element should be inserted @param newObject the new object to add to the array + @returns the new object that was added @see add, addSorted, addIfNotAlreadyThere, set */ - void insert (int indexToInsertAt, - const ObjectClass* const newObject) noexcept + ObjectClass* insert (int indexToInsertAt, ObjectClass* newObject) noexcept { - if (indexToInsertAt >= 0) + if (indexToInsertAt < 0) + return add (newObject); + + const ScopedLockType lock (getLock()); + + if (indexToInsertAt > numUsed) + indexToInsertAt = numUsed; + + data.ensureAllocatedSize (numUsed + 1); + jassert (data.elements != nullptr); + + ObjectClass** const e = data.elements + indexToInsertAt; + const int numToMove = numUsed - indexToInsertAt; + + if (numToMove > 0) + memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); + + *e = newObject; + ++numUsed; + return newObject; + } + + /** 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 newObjects 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, + ObjectClass* const* newObjects, + int numberOfElements) + { + if (numberOfElements > 0) { const ScopedLockType lock (getLock()); + data.ensureAllocatedSize (numUsed + numberOfElements); + ObjectClass** insertPos = data.elements; - if (indexToInsertAt > numUsed) - indexToInsertAt = numUsed; + if (isPositiveAndBelow (indexToInsertAt, numUsed)) + { + insertPos += indexToInsertAt; + const size_t numberToMove = (size_t) (numUsed - indexToInsertAt); + memmove (insertPos + numberOfElements, insertPos, numberToMove * sizeof (ObjectClass*)); + } + else + { + insertPos += numUsed; + } - data.ensureAllocatedSize (numUsed + 1); + numUsed += numberOfElements; - ObjectClass** const e = data.elements + indexToInsertAt; - const int numToMove = numUsed - indexToInsertAt; - - if (numToMove > 0) - memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); - - *e = const_cast (newObject); - ++numUsed; - } - else - { - add (newObject); + while (--numberOfElements >= 0) + *insertPos++ = *newObjects++; } } @@ -298,13 +372,16 @@ public: If the array already contains a matching object, nothing will be done. @param newObject the new object to add to the array + @returns the new object that was added */ - void addIfNotAlreadyThere (const ObjectClass* const newObject) noexcept + ObjectClass* addIfNotAlreadyThere (ObjectClass* newObject) noexcept { const ScopedLockType lock (getLock()); if (! contains (newObject)) add (newObject); + + return newObject; } /** Replaces an object in the array with a different one. @@ -320,13 +397,11 @@ public: @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) + ObjectClass* set (int indexToChange, ObjectClass* newObject, bool deleteOldElement = true) { if (indexToChange >= 0) { - ObjectClass* toDelete = nullptr; + ScopedPointer toDelete; { const ScopedLockType lock (getLock()); @@ -338,27 +413,25 @@ public: toDelete = data.elements [indexToChange]; if (toDelete == newObject) - toDelete = nullptr; + toDelete.release(); } - data.elements [indexToChange] = const_cast (newObject); + data.elements [indexToChange] = newObject; } else { data.ensureAllocatedSize (numUsed + 1); - data.elements [numUsed++] = const_cast (newObject); + data.elements [numUsed++] = newObject; } } - - delete toDelete; // don't want to use a ScopedPointer here because if the - // object has a private destructor, both OwnedArray and - // ScopedPointer would need to be friend classes.. } else { jassertfalse; // you're trying to set an object at a negative index, which doesn't have // any effect - but since the object is not being added, it may be leaking.. } + + return newObject; } /** Adds elements from another array to the end of this array. @@ -388,6 +461,7 @@ public: numElementsToAdd = arrayToAddFrom.size() - startIndex; data.ensureAllocatedSize (numUsed + numElementsToAdd); + jassert (numElementsToAdd <= 0 || data.elements != nullptr); while (--numElementsToAdd >= 0) { @@ -428,12 +502,10 @@ public: numElementsToAdd = arrayToAddFrom.size() - startIndex; data.ensureAllocatedSize (numUsed + numElementsToAdd); + jassert (numElementsToAdd <= 0 || data.elements != nullptr); while (--numElementsToAdd >= 0) - { - data.elements [numUsed] = new ObjectClass (*arrayToAddFrom.getUnchecked (startIndex++)); - ++numUsed; - } + data.elements [numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++)); } /** Inserts a new object into the array assuming that the array is sorted. @@ -471,9 +543,8 @@ public: @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 noexcept + template + int indexOfSorted (ElementComparator& comparator, const ObjectClass* const objectToLookFor) const noexcept { (void) comparator; const ScopedLockType lock (getLock()); @@ -508,10 +579,9 @@ public: @param deleteObject whether to delete the object that is removed @see removeObject, removeRange */ - void remove (const int indexToRemove, - const bool deleteObject = true) + void remove (int indexToRemove, bool deleteObject = true) { - ObjectClass* toDelete = nullptr; + ScopedPointer toDelete; { const ScopedLockType lock (getLock()); @@ -531,10 +601,6 @@ public: } } - delete toDelete; // don't want to use a ScopedPointer here because if the - // object has a private destructor, both OwnedArray and - // ScopedPointer would need to be friend classes.. - if ((numUsed << 1) < data.numAllocated) minimiseStorageOverheads(); } @@ -548,7 +614,7 @@ public: @param indexToRemove the index of the element to remove @see remove, removeObject, removeRange */ - ObjectClass* removeAndReturn (const int indexToRemove) + ObjectClass* removeAndReturn (int indexToRemove) { ObjectClass* removedItem = nullptr; const ScopedLockType lock (getLock()); @@ -579,8 +645,7 @@ public: @param deleteObject whether to delete the object (if it's found) @see remove, removeRange */ - void removeObject (const ObjectClass* const objectToRemove, - const bool deleteObject = true) + void removeObject (const ObjectClass* objectToRemove, bool deleteObject = true) { const ScopedLockType lock (getLock()); ObjectClass** const e = data.elements.getData(); @@ -608,9 +673,7 @@ public: @param deleteObjects whether to delete the objects that get removed @see remove, removeObject */ - void removeRange (int startIndex, - const int numberToRemove, - const bool deleteObjects = true) + void removeRange (int startIndex, int numberToRemove, bool deleteObjects = true) { const ScopedLockType lock (getLock()); const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); @@ -622,7 +685,7 @@ public: { for (int i = startIndex; i < endIndex; ++i) { - delete data.elements [i]; + ContainerDeletePolicy::destroy (data.elements [i]); data.elements [i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) } } @@ -650,7 +713,7 @@ public: @see remove, removeObject, removeRange */ void removeLast (int howManyToRemove = 1, - const bool deleteObjects = true) + bool deleteObjects = true) { const ScopedLockType lock (getLock()); @@ -665,16 +728,16 @@ public: 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) noexcept + void swap (int index1, + int index2) noexcept { const ScopedLockType lock (getLock()); if (isPositiveAndBelow (index1, numUsed) && isPositiveAndBelow (index2, numUsed)) { - swapVariables (data.elements [index1], - data.elements [index2]); + std::swap (data.elements [index1], + data.elements [index2]); } } @@ -691,8 +754,7 @@ public: @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) noexcept + void move (int currentIndex, int newIndex) noexcept { if (currentIndex != newIndex) { @@ -728,13 +790,13 @@ public: 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) noexcept + template + void swapWith (OtherArrayType& otherArray) noexcept { const ScopedLockType lock1 (getLock()); - const ScopedLockType lock2 (otherArray.getLock()); - + const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); data.swapWith (otherArray.data); - swapVariables (numUsed, otherArray.numUsed); + std::swap (numUsed, otherArray.numUsed); } //============================================================================== @@ -790,7 +852,7 @@ public: */ template void sort (ElementComparator& comparator, - const bool retainOrderOfEquivalentItems = false) const noexcept + bool retainOrderOfEquivalentItems = false) const noexcept { (void) comparator; // if you pass in an object with a static compareElements() method, this // avoids getting warning messages about the parameter being unused @@ -810,13 +872,26 @@ public: typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; + //============================================================================== + #ifndef DOXYGEN + // Note that the swapWithArray method has been replaced by a more flexible templated version, + // and renamed "swapWith" to be more consistent with the names used in other classes. + JUCE_DEPRECATED_WITH_BODY (void swapWithArray (OwnedArray& other) noexcept, { swapWith (other); }) + #endif + private: //============================================================================== ArrayAllocationBase data; int numUsed; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray); + void deleteAllObjects() + { + while (numUsed > 0) + ContainerDeletePolicy::destroy (data.elements [--numUsed]); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OwnedArray) }; -#endif // __JUCE_OWNEDARRAY_JUCEHEADER__ +#endif // JUCE_OWNEDARRAY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp index 8464c5b..6b02baf 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -62,8 +65,7 @@ void PropertySet::clear() } } -String PropertySet::getValue (const String& keyName, - const String& defaultValue) const noexcept +String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept { const ScopedLock sl (lock); @@ -76,8 +78,7 @@ String PropertySet::getValue (const String& keyName, : defaultValue; } -int PropertySet::getIntValue (const String& keyName, - const int defaultValue) const noexcept +int PropertySet::getIntValue (StringRef keyName, const int defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); @@ -89,8 +90,7 @@ int PropertySet::getIntValue (const String& keyName, : defaultValue; } -double PropertySet::getDoubleValue (const String& keyName, - const double defaultValue) const noexcept +double PropertySet::getDoubleValue (StringRef keyName, const double defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); @@ -102,8 +102,7 @@ double PropertySet::getDoubleValue (const String& keyName, : defaultValue; } -bool PropertySet::getBoolValue (const String& keyName, - const bool defaultValue) const noexcept +bool PropertySet::getBoolValue (StringRef keyName, const bool defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); @@ -115,7 +114,7 @@ bool PropertySet::getBoolValue (const String& keyName, : defaultValue; } -XmlElement* PropertySet::getXmlValue (const String& keyName) const +XmlElement* PropertySet::getXmlValue (StringRef keyName) const { return XmlDocument::parse (getValue (keyName)); } @@ -139,7 +138,7 @@ void PropertySet::setValue (const String& keyName, const var& v) } } -void PropertySet::removeValue (const String& keyName) +void PropertySet::removeValue (StringRef keyName) { if (keyName.isNotEmpty()) { @@ -156,11 +155,11 @@ void PropertySet::removeValue (const String& keyName) void PropertySet::setValue (const String& keyName, const XmlElement* const xml) { - setValue (keyName, xml == nullptr ? var::null - : var (xml->createDocument (String::empty, true))); + setValue (keyName, xml == nullptr ? var() + : var (xml->createDocument ("", true))); } -bool PropertySet::containsKey (const String& keyName) const noexcept +bool PropertySet::containsKey (StringRef keyName) const noexcept { const ScopedLock sl (lock); return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys); diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h index b3c4d58..2a4ecb1 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_PROPERTYSET_JUCEHEADER__ -#define __JUCE_PROPERTYSET_JUCEHEADER__ - -#include "../text/juce_StringPairArray.h" -#include "../xml/juce_XmlElement.h" -#include "../containers/juce_Variant.h" +#ifndef JUCE_PROPERTYSET_H_INCLUDED +#define JUCE_PROPERTYSET_H_INCLUDED //============================================================================== @@ -46,18 +45,15 @@ class JUCE_API PropertySet public: //============================================================================== /** Creates an empty PropertySet. - - @param ignoreCaseOfKeyNames if true, the names of properties are compared in a - case-insensitive way + @param ignoreCaseOfKeyNames if true, the names of properties are compared in a + case-insensitive way */ PropertySet (bool ignoreCaseOfKeyNames = false); - /** Creates a copy of another PropertySet. - */ + /** Creates a copy of another PropertySet. */ PropertySet (const PropertySet& other); - /** Copies another PropertySet over this one. - */ + /** Copies another PropertySet over this one. */ PropertySet& operator= (const PropertySet& other); /** Destructor. */ @@ -73,8 +69,7 @@ public: @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ - String getValue (const String& keyName, - const String& defaultReturnValue = String::empty) const noexcept; + String getValue (StringRef keyName, const String& defaultReturnValue = String()) const noexcept; /** Returns one of the properties as an integer. @@ -85,8 +80,7 @@ public: @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 noexcept; + int getIntValue (StringRef keyName, int defaultReturnValue = 0) const noexcept; /** Returns one of the properties as an double. @@ -97,8 +91,7 @@ public: @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 noexcept; + double getDoubleValue (StringRef keyName, double defaultReturnValue = 0.0) const noexcept; /** Returns one of the properties as an boolean. @@ -112,13 +105,12 @@ public: @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 noexcept; + bool getBoolValue (StringRef keyName, bool defaultReturnValue = false) const noexcept; /** 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. + The result will a new XMLElement object that the caller must delete. If may return nullptr + 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), @@ -126,7 +118,7 @@ public: @param keyName the name of the property to retrieve */ - XmlElement* getXmlValue (const String& keyName) const; + XmlElement* getXmlValue (StringRef keyName) const; //============================================================================== /** Sets a named property. @@ -152,13 +144,12 @@ public: //============================================================================== /** Deletes a property. - @param keyName the name of the property to delete. (This mustn't be an empty string) */ - void removeValue (const String& keyName); + void removeValue (StringRef keyName); /** Returns true if the properies include the given key. */ - bool containsKey (const String& keyName) const noexcept; + bool containsKey (StringRef keyName) const noexcept; /** Removes all values. */ void clear(); @@ -172,17 +163,13 @@ public: //============================================================================== /** 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; /** 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); @@ -208,19 +195,17 @@ public: PropertySet* getFallbackPropertySet() const noexcept { return fallbackProperties; } protected: - //============================================================================== /** Subclasses can override this to be told when one of the properies has been changed. */ virtual void propertyChanged(); private: - //============================================================================== StringPairArray properties; PropertySet* fallbackProperties; CriticalSection lock; bool ignoreCaseOfKeys; - JUCE_LEAK_DETECTOR (PropertySet); + JUCE_LEAK_DETECTOR (PropertySet) }; -#endif // __JUCE_PROPERTYSET_JUCEHEADER__ +#endif // JUCE_PROPERTYSET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h index 88c9b87..8de5ec9 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ReferenceCountedArray.h @@ -1,40 +1,46 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ -#define __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ - -#include "../memory/juce_ReferenceCountedObject.h" -#include "juce_ArrayAllocationBase.h" -#include "juce_ElementComparator.h" -#include "../threads/juce_CriticalSection.h" +#ifndef JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED +#define JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED //============================================================================== /** - Holds a list of objects derived from ReferenceCountedObject. + Holds a list of objects derived from ReferenceCountedObject, or which implement basic + reference-count handling methods. + + The template parameter specifies the class of the object you want to point to - the easiest + way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject + or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable + class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and + decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods + should behave. A ReferenceCountedArray holds objects derived from ReferenceCountedObject, and takes care of incrementing and decrementing their ref counts when they @@ -69,8 +75,8 @@ public: memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*)); for (int i = numUsed; --i >= 0;) - if (data.elements[i] != nullptr) - data.elements[i]->incReferenceCount(); + if (ObjectClass* o = data.elements[i]) + o->incReferenceCount(); } /** Creates a copy of another array */ @@ -83,8 +89,8 @@ public: memcpy (data.elements, other.getRawDataPointer(), numUsed * sizeof (ObjectClass*)); for (int i = numUsed; --i >= 0;) - if (data.elements[i] != nullptr) - data.elements[i]->incReferenceCount(); + if (ObjectClass* o = data.elements[i]) + o->incReferenceCount(); } /** Copies another array into this one. @@ -93,7 +99,7 @@ public: ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept { ReferenceCountedArray otherCopy (other); - swapWithArray (otherCopy); + swapWith (otherCopy); return *this; } @@ -104,7 +110,7 @@ public: ReferenceCountedArray& operator= (const ReferenceCountedArray& other) noexcept { ReferenceCountedArray otherCopy (other); - swapWithArray (otherCopy); + swapWith (otherCopy); return *this; } @@ -126,8 +132,8 @@ public: const ScopedLockType lock (getLock()); while (numUsed > 0) - if (data.elements [--numUsed] != nullptr) - data.elements [numUsed]->decReferenceCount(); + if (ObjectClass* o = data.elements [--numUsed]) + releaseObject (o); jassert (numUsed == 0); data.setAllocatedSize (0); @@ -156,7 +162,7 @@ public: 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. + it can be used when you're sure the index is always going to be legal. */ inline ObjectClassPtr getUnchecked (const int index) const noexcept { @@ -174,8 +180,14 @@ public: inline ObjectClass* getObjectPointer (const int index) const noexcept { const ScopedLockType lock (getLock()); - return isPositiveAndBelow (index, numUsed) ? data.elements [index] - : nullptr; + + if (isPositiveAndBelow (index, numUsed)) + { + jassert (data.elements != nullptr); + return data.elements [index]; + } + + return ObjectClassPtr(); } /** Returns a raw pointer to the object at this index in the array, without checking @@ -184,7 +196,7 @@ public: inline ObjectClass* getObjectPointerUnchecked (const int index) const noexcept { const ScopedLockType lock (getLock()); - jassert (isPositiveAndBelow (index, numUsed)); + jassert (isPositiveAndBelow (index, numUsed) && data.elements != nullptr); return data.elements [index]; } @@ -196,8 +208,14 @@ public: inline ObjectClassPtr getFirst() const noexcept { const ScopedLockType lock (getLock()); - return numUsed > 0 ? data.elements [0] - : static_cast (nullptr); + + if (numUsed > 0) + { + jassert (data.elements != nullptr); + return data.elements [0]; + } + + return ObjectClassPtr(); } /** Returns a pointer to the last object in the array. @@ -208,8 +226,14 @@ public: inline ObjectClassPtr getLast() const noexcept { const ScopedLockType lock (getLock()); - return numUsed > 0 ? data.elements [numUsed - 1] - : static_cast (nullptr); + + if (numUsed > 0) + { + jassert (data.elements != nullptr); + return data.elements [numUsed - 1]; + } + + return ObjectClassPtr(); } /** Returns a pointer to the actual array data. @@ -248,9 +272,9 @@ public: { const ScopedLockType lock (getLock()); ObjectClass** e = data.elements.getData(); - ObjectClass** const end_ = e + numUsed; + ObjectClass** const endPointer = e + numUsed; - while (e != end_) + while (e != endPointer) { if (objectToLookFor == *e) return static_cast (e - data.elements.getData()); @@ -270,9 +294,9 @@ public: { const ScopedLockType lock (getLock()); ObjectClass** e = data.elements.getData(); - ObjectClass** const end_ = e + numUsed; + ObjectClass** const endPointer = e + numUsed; - while (e != end_) + while (e != endPointer) { if (objectToLookFor == *e) return true; @@ -290,14 +314,17 @@ public: @param newObject the new object to add to the array @see set, insert, addIfNotAlreadyThere, addSorted, addArray */ - void add (ObjectClass* const newObject) noexcept + ObjectClass* add (ObjectClass* const newObject) noexcept { const ScopedLockType lock (getLock()); data.ensureAllocatedSize (numUsed + 1); + jassert (data.elements != nullptr); data.elements [numUsed++] = newObject; if (newObject != nullptr) newObject->incReferenceCount(); + + return newObject; } /** Inserts a new object into the array at the given index. @@ -313,35 +340,34 @@ public: @param newObject the new object to add to the array @see add, addSorted, addIfNotAlreadyThere, set */ - void insert (int indexToInsertAt, - ObjectClass* const newObject) noexcept + ObjectClass* insert (int indexToInsertAt, + ObjectClass* const newObject) noexcept { - if (indexToInsertAt >= 0) - { - const ScopedLockType lock (getLock()); + if (indexToInsertAt < 0) + return add (newObject); - if (indexToInsertAt > numUsed) - indexToInsertAt = numUsed; + const ScopedLockType lock (getLock()); - data.ensureAllocatedSize (numUsed + 1); + if (indexToInsertAt > numUsed) + indexToInsertAt = numUsed; - ObjectClass** const e = data.elements + indexToInsertAt; - const int numToMove = numUsed - indexToInsertAt; + data.ensureAllocatedSize (numUsed + 1); + jassert (data.elements != nullptr); - if (numToMove > 0) - memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); + ObjectClass** const e = data.elements + indexToInsertAt; + const int numToMove = numUsed - indexToInsertAt; - *e = newObject; + if (numToMove > 0) + memmove (e + 1, e, sizeof (ObjectClass*) * (size_t) numToMove); - if (newObject != nullptr) - newObject->incReferenceCount(); + *e = newObject; - ++numUsed; - } - else - { - add (newObject); - } + if (newObject != nullptr) + newObject->incReferenceCount(); + + ++numUsed; + + return newObject; } /** Appends a new object at the end of the array as long as the array doesn't @@ -382,14 +408,15 @@ public: if (indexToChange < numUsed) { - if (data.elements [indexToChange] != nullptr) - data.elements [indexToChange]->decReferenceCount(); + if (ObjectClass* o = data.elements [indexToChange]) + releaseObject (o); data.elements [indexToChange] = newObject; } else { data.ensureAllocatedSize (numUsed + 1); + jassert (data.elements != nullptr); data.elements [numUsed++] = newObject; } } @@ -531,8 +558,8 @@ public: { ObjectClass** const e = data.elements + indexToRemove; - if (*e != nullptr) - (*e)->decReferenceCount(); + if (ObjectClass* o = *e) + releaseObject (o); --numUsed; const int numberToShift = numUsed - indexToRemove; @@ -563,10 +590,10 @@ public: { ObjectClass** const e = data.elements + indexToRemove; - if (*e != nullptr) + if (ObjectClass* o = *e) { - removedItem = *e; - (*e)->decReferenceCount(); + removedItem = o; + releaseObject (o); } --numUsed; @@ -616,24 +643,24 @@ public: { const ScopedLockType lock (getLock()); - const int start = jlimit (0, numUsed, startIndex); - const int end_ = jlimit (0, numUsed, startIndex + numberToRemove); + const int start = jlimit (0, numUsed, startIndex); + const int endIndex = jlimit (0, numUsed, startIndex + numberToRemove); - if (end_ > start) + if (endIndex > start) { int i; - for (i = start; i < end_; ++i) + for (i = start; i < endIndex; ++i) { - if (data.elements[i] != nullptr) + if (ObjectClass* o = data.elements[i]) { - data.elements[i]->decReferenceCount(); + releaseObject (o); data.elements[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer) } } - const int rangeSize = end_ - start; + const int rangeSize = endIndex - start; ObjectClass** e = data.elements + start; - i = numUsed - end_; + i = numUsed - endIndex; numUsed -= rangeSize; while (--i >= 0) @@ -735,11 +762,11 @@ public: 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) noexcept + template + void swapWith (OtherArrayType& otherArray) noexcept { const ScopedLockType lock1 (getLock()); - const ScopedLockType lock2 (otherArray.getLock()); - + const typename OtherArrayType::ScopedLockType lock2 (otherArray.getLock()); data.swapWith (otherArray.data); std::swap (numUsed, otherArray.numUsed); } @@ -847,11 +874,24 @@ public: typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; + //============================================================================== + #ifndef DOXYGEN + // Note that the swapWithArray method has been replaced by a more flexible templated version, + // and renamed "swapWith" to be more consistent with the names used in other classes. + JUCE_DEPRECATED_WITH_BODY (void swapWithArray (ReferenceCountedArray& other) noexcept, { swapWith (other); }) + #endif + private: //============================================================================== ArrayAllocationBase data; int numUsed; + + static void releaseObject (ObjectClass* o) + { + if (o->decReferenceCountWithoutDeleting()) + ContainerDeletePolicy::destroy (o); + } }; -#endif // __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ +#endif // JUCE_REFERENCECOUNTEDARRAY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h b/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h index 9bed510..13b871e 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_ScopedValueSetter.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SCOPEDVALUESETTER_JUCEHEADER__ -#define __JUCE_SCOPEDVALUESETTER_JUCEHEADER__ +#ifndef JUCE_SCOPEDVALUESETTER_H_INCLUDED +#define JUCE_SCOPEDVALUESETTER_H_INCLUDED //============================================================================== @@ -61,7 +64,7 @@ public: given new value, and will then reset it to its original value when this object is deleted. */ ScopedValueSetter (ValueType& valueToSet, - const ValueType& newValue) + ValueType newValue) : value (valueToSet), originalValue (valueToSet) { @@ -72,8 +75,8 @@ public: given new value, and will then reset it to be valueWhenDeleted when this object is deleted. */ ScopedValueSetter (ValueType& valueToSet, - const ValueType& newValue, - const ValueType& valueWhenDeleted) + ValueType newValue, + ValueType valueWhenDeleted) : value (valueToSet), originalValue (valueWhenDeleted) { @@ -90,8 +93,8 @@ private: ValueType& value; const ValueType originalValue; - JUCE_DECLARE_NON_COPYABLE (ScopedValueSetter); + JUCE_DECLARE_NON_COPYABLE (ScopedValueSetter) }; -#endif // __JUCE_SCOPEDVALUESETTER_JUCEHEADER__ +#endif // JUCE_SCOPEDVALUESETTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h index d300a45..62fd923 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SortedSet.h @@ -1,40 +1,39 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SORTEDSET_JUCEHEADER__ -#define __JUCE_SORTEDSET_JUCEHEADER__ - -#include "juce_ArrayAllocationBase.h" -#include "../threads/juce_CriticalSection.h" +#ifndef JUCE_SORTEDSET_H_INCLUDED +#define JUCE_SORTEDSET_H_INCLUDED #if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4512) + #pragma warning (push) + #pragma warning (disable: 4512) #endif - //============================================================================== /** Holds a set of unique primitive objects, such as ints or doubles. @@ -64,19 +63,15 @@ public: //============================================================================== /** Creates an empty set. */ SortedSet() noexcept - : numUsed (0) { } /** Creates a copy of another set. @param other the set to copy */ - SortedSet (const SortedSet& other) noexcept + SortedSet (const SortedSet& other) + : data (other.data) { - const ScopedLockType lock (other.getLock()); - numUsed = other.numUsed; - data.setAllocatedSize (other.numUsed); - memcpy (data.elements, other.data.elements, numUsed * sizeof (ElementType)); } /** Destructor. */ @@ -89,47 +84,22 @@ public: */ SortedSet& operator= (const SortedSet& other) noexcept { - if (this != &other) - { - const ScopedLockType lock1 (other.getLock()); - const ScopedLockType lock2 (getLock()); - - data.ensureAllocatedSize (other.size()); - numUsed = other.numUsed; - memcpy (data.elements, other.data.elements, numUsed * sizeof (ElementType)); - minimiseStorageOverheads(); - } - + data = other.data; return *this; } //============================================================================== /** Compares this set to another one. - - Two sets are considered equal if they both contain the same set of - elements. - + 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 noexcept { - const ScopedLockType lock (getLock()); - - if (numUsed != other.numUsed) - return false; - - for (int i = numUsed; --i >= 0;) - if (! (data.elements[i] == other.data.elements[i])) - return false; - - return true; + return data == other.data; } /** Compares this set to another one. - - Two sets are considered equal if they both contain the same set of - elements. - + 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 noexcept @@ -148,27 +118,22 @@ public: */ void clear() noexcept { - const ScopedLockType lock (getLock()); - data.setAllocatedSize (0); - numUsed = 0; + data.clear(); } /** Removes all elements from the set without freeing the array's allocated storage. - @see clear */ void clearQuick() noexcept { - const ScopedLockType lock (getLock()); - numUsed = 0; + data.clearQuick(); } //============================================================================== - /** Returns the current number of elements in the set. - */ + /** Returns the current number of elements in the set. */ inline int size() const noexcept { - return numUsed; + return data.size(); } /** Returns one of the elements in the set. @@ -184,9 +149,7 @@ public: */ inline ElementType operator[] (const int index) const noexcept { - const ScopedLockType lock (getLock()); - return isPositiveAndBelow (index, numUsed) ? data.elements [index] - : ElementType(); + return data [index]; } /** Returns one of the elements in the set, without checking the index passed in. @@ -199,9 +162,7 @@ public: */ inline ElementType getUnchecked (const int index) const noexcept { - const ScopedLockType lock (getLock()); - jassert (isPositiveAndBelow (index, numUsed)); - return data.elements [index]; + return data.getUnchecked (index); } /** Returns a direct reference to one of the elements in the set, without checking the index passed in. @@ -214,29 +175,23 @@ public: */ inline ElementType& getReference (const int index) const noexcept { - const ScopedLockType lock (getLock()); - jassert (isPositiveAndBelow (index, numUsed)); - return data.elements [index]; + return data.getReference (index); } /** Returns the first element in the set, or 0 if the set is empty. - @see operator[], getUnchecked, getLast */ inline ElementType getFirst() const noexcept { - const ScopedLockType lock (getLock()); - return numUsed > 0 ? data.elements [0] : ElementType(); + return data.getFirst(); } /** Returns the last element in the set, or 0 if the set is empty. - @see operator[], getUnchecked, getFirst */ inline ElementType getLast() const noexcept { - const ScopedLockType lock (getLock()); - return numUsed > 0 ? data.elements [numUsed - 1] : ElementType(); + return data.getLast(); } //============================================================================== @@ -245,7 +200,7 @@ public: */ inline ElementType* begin() const noexcept { - return data.elements; + return data.begin(); } /** Returns a pointer to the element which follows the last element in the set. @@ -253,7 +208,7 @@ public: */ inline ElementType* end() const noexcept { - return data.elements + numUsed; + return data.end(); } //============================================================================== @@ -265,34 +220,30 @@ public: @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 noexcept + int indexOf (const ElementType& elementToLookFor) const noexcept { - const ScopedLockType lock (getLock()); + const ScopedLockType lock (data.getLock()); - int start = 0; - int end_ = numUsed; + int s = 0; + int e = data.size(); for (;;) { - if (start >= end_) - { + if (s >= e) return -1; - } - else if (elementToLookFor == data.elements [start]) - { - return start; - } - else - { - const int halfway = (start + end_) >> 1; - if (halfway == start) - return -1; - else if (elementToLookFor < data.elements [halfway]) - end_ = halfway; - else - start = halfway; - } + if (elementToLookFor == data.getReference (s)) + return s; + + const int halfway = (s + e) / 2; + + if (halfway == s) + return -1; + + if (elementToLookFor < data.getReference (halfway)) + e = halfway; + else + s = halfway; } } @@ -301,81 +252,58 @@ public: @param elementToLookFor the value or object to look for @returns true if the item is found */ - bool contains (const ElementType elementToLookFor) const noexcept + bool contains (const ElementType& elementToLookFor) const noexcept { - const ScopedLockType lock (getLock()); - - int start = 0; - int end_ = numUsed; - - for (;;) - { - if (start >= end_) - { - return false; - } - else if (elementToLookFor == data.elements [start]) - { - return true; - } - else - { - const int halfway = (start + end_) >> 1; - - if (halfway == start) - return false; - else if (elementToLookFor < data.elements [halfway]) - end_ = halfway; - else - start = halfway; - } - } + return indexOf (elementToLookFor) >= 0; } //============================================================================== /** 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 + Note that if a matching element already exists, the new value will be assigned + to the existing one using operator=, so that if there are any differences between + the objects which were not recognised by the object's operator==, then the + set will always contain a copy of the most recently added one. + + @param newElement the new object to add to the set + @returns true if the value was added, or false if it already existed @see set, insert, addIfNotAlreadyThere, addSorted, addSet, addArray */ - void add (const ElementType newElement) noexcept + bool add (const ElementType& newElement) noexcept { const ScopedLockType lock (getLock()); - int start = 0; - int end_ = numUsed; + int s = 0; + int e = data.size(); - for (;;) + while (s < e) { - if (start >= end_) - { - jassert (start <= end_); - insertInternal (start, newElement); - break; - } - else if (newElement == data.elements [start]) + ElementType& elem = data.getReference (s); + if (newElement == elem) { + elem = newElement; // force an update in case operator== permits differences. + return false; + } + + const int halfway = (s + e) / 2; + const bool isBeforeHalfway = (newElement < data.getReference (halfway)); + + if (halfway == s) + { + if (! isBeforeHalfway) + ++s; + break; } + + if (isBeforeHalfway) + e = halfway; else - { - const int halfway = (start + end_) >> 1; - - if (halfway == start) - { - if (newElement < data.elements [halfway]) - insertInternal (start, newElement); - else - insertInternal (start + 1, newElement); - - break; - } - else if (newElement < data.elements [halfway]) - end_ = halfway; - else - start = halfway; - } + s = halfway; } + + data.insert (s, newElement); + return true; } /** Adds elements from an array to this set. @@ -424,12 +352,12 @@ public: if (numElementsToAdd < 0 || startIndex + numElementsToAdd > setToAddFrom.size()) numElementsToAdd = setToAddFrom.size() - startIndex; - addArray (setToAddFrom.elements + startIndex, numElementsToAdd); + if (numElementsToAdd > 0) + addArray (&setToAddFrom.data.getReference (startIndex), numElementsToAdd); } } } - //============================================================================== /** Removes an element from the set. @@ -442,26 +370,7 @@ public: */ ElementType remove (const int indexToRemove) noexcept { - const ScopedLockType lock (getLock()); - - if (isPositiveAndBelow (indexToRemove, numUsed)) - { - --numUsed; - - ElementType* const e = data.elements + indexToRemove; - ElementType const removed = *e; - const int numberToShift = numUsed - indexToRemove; - - if (numberToShift > 0) - memmove (e, e + 1, sizeof (ElementType) * (size_t) numberToShift); - - if ((numUsed << 1) < data.numAllocated) - minimiseStorageOverheads(); - - return removed; - } - - return ElementType(); + return data.remove (indexToRemove); } /** Removes an item from the set. @@ -474,7 +383,7 @@ public: void removeValue (const ElementType valueToRemove) noexcept { const ScopedLockType lock (getLock()); - remove (indexOf (valueToRemove)); + data.remove (indexOf (valueToRemove)); } /** Removes any elements which are also in another set. @@ -492,14 +401,11 @@ public: { clear(); } - else + else if (otherSet.size() > 0) { - if (otherSet.size() > 0) - { - for (int i = numUsed; --i >= 0;) - if (otherSet.contains (data.elements [i])) - remove (i); - } + for (int i = data.size(); --i >= 0;) + if (otherSet.contains (data.getReference (i))) + remove (i); } } @@ -524,13 +430,24 @@ public: } else { - for (int i = numUsed; --i >= 0;) - if (! otherSet.contains (data.elements [i])) + for (int i = data.size(); --i >= 0;) + if (! otherSet.contains (data.getReference (i))) remove (i); } } } + /** This swaps the contents of this array with those of another array. + + If you need to exchange two arrays, this is vastly quicker than using copy-by-value + because it just swaps their internal pointers. + */ + template + void swapWith (OtherSetType& otherSet) noexcept + { + data.swapWith (otherSet.data); + } + //============================================================================== /** Reduces the amount of storage being used by the set. @@ -540,8 +457,7 @@ public: */ void minimiseStorageOverheads() noexcept { - const ScopedLockType lock (getLock()); - data.shrinkToNoMoreThan (numUsed); + data.minimiseStorageOverheads(); } /** Increases the set's internal storage to hold a minimum number of elements. @@ -552,8 +468,7 @@ public: */ void ensureStorageAllocated (const int minNumElements) { - const ScopedLockType lock (getLock()); - data.ensureAllocatedSize (minNumElements); + data.ensureStorageAllocated (minNumElements); } //============================================================================== @@ -561,7 +476,7 @@ public: 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 noexcept { return data; } + inline const TypeOfCriticalSectionToUse& getLock() const noexcept { return data.getLock(); } /** Returns the type of scoped lock to use for locking this array */ typedef typename TypeOfCriticalSectionToUse::ScopedLockType ScopedLockType; @@ -569,26 +484,11 @@ public: private: //============================================================================== - ArrayAllocationBase data; - int numUsed; - - void insertInternal (const int indexToInsertAt, const ElementType newElement) noexcept - { - data.ensureAllocatedSize (numUsed + 1); - - ElementType* const insertPos = data.elements + indexToInsertAt; - const int numberToMove = numUsed - indexToInsertAt; - - if (numberToMove > 0) - memmove (insertPos + 1, insertPos, sizeof (ElementType) * (size_t) numberToMove); - - *insertPos = newElement; - ++numUsed; - } + Array data; }; #if JUCE_MSVC - #pragma warning (pop) + #pragma warning (pop) #endif -#endif // __JUCE_SORTEDSET_JUCEHEADER__ +#endif // JUCE_SORTEDSET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h index 1e3f1a2..86366fc 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_SparseSet.h @@ -1,44 +1,44 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SPARSESET_JUCEHEADER__ -#define __JUCE_SPARSESET_JUCEHEADER__ - -#include "../maths/juce_Range.h" -#include "../threads/juce_CriticalSection.h" +#ifndef JUCE_SPARSESET_H_INCLUDED +#define JUCE_SPARSESET_H_INCLUDED //============================================================================== /** 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 + This container acts like an array, but can efficiently hold large contiguous 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, + The type used as a template parameter must be an integer type, such as int, short, int64, etc. */ template @@ -139,8 +139,8 @@ public: if (isPositiveAndBelow (rangeIndex, getNumRanges())) return Range (values.getUnchecked (rangeIndex << 1), values.getUnchecked ((rangeIndex << 1) + 1)); - else - return Range(); + + return Range(); } /** Returns the range between the lowest and highest values in the set. @@ -162,7 +162,7 @@ public: /** 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) + void addRange (const Range range) { jassert (range.getLength() >= 0); if (range.getLength() > 0) @@ -179,7 +179,7 @@ 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) + void removeRange (const Range rangeToRemove) { jassert (rangeToRemove.getLength() >= 0); @@ -216,23 +216,22 @@ public: } /** Does an XOR of the values in a given range. */ - void invertRange (const Range& range) + void invertRange (const Range range) { SparseSet newItems; newItems.addRange (range); - int i; - for (i = getNumRanges(); --i >= 0;) + for (int i = getNumRanges(); --i >= 0;) newItems.removeRange (getRange (i)); removeRange (range); - for (i = newItems.getNumRanges(); --i >= 0;) + for (int i = newItems.getNumRanges(); --i >= 0;) addRange (newItems.getRange(i)); } /** Checks whether any part of a given range overlaps any part of this set. */ - bool overlapsRange (const Range& range) + bool overlapsRange (const Range range) { if (range.getLength() > 0) { @@ -250,7 +249,7 @@ public: } /** Checks whether the whole of a given range is contained within this one. */ - bool containsRange (const Range& range) + bool containsRange (const Range range) { if (range.getLength() > 0) { @@ -296,4 +295,4 @@ private: -#endif // __JUCE_SPARSESET_JUCEHEADER__ +#endif // JUCE_SPARSESET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp index 70299ca..db3f2f5 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -31,7 +34,9 @@ enum VariantStreamMarkers varMarker_Double = 4, varMarker_String = 5, varMarker_Int64 = 6, - varMarker_Array = 7 + varMarker_Array = 7, + varMarker_Binary = 8, + varMarker_Undefined = 9 }; //============================================================================== @@ -47,9 +52,12 @@ public: virtual String toString (const ValueUnion&) const { return String::empty; } virtual bool toBool (const ValueUnion&) const noexcept { return false; } virtual ReferenceCountedObject* toObject (const ValueUnion&) const noexcept { return nullptr; } - virtual Array* toArray (const ValueUnion&) const noexcept { return 0; } + virtual Array* toArray (const ValueUnion&) const noexcept { return nullptr; } + virtual MemoryBlock* toBinary (const ValueUnion&) const noexcept { return nullptr; } + virtual var clone (const var& original) const { return original; } virtual bool isVoid() const noexcept { return false; } + virtual bool isUndefined() const noexcept { return false; } virtual bool isInt() const noexcept { return false; } virtual bool isInt64() const noexcept { return false; } virtual bool isBool() const noexcept { return false; } @@ -57,6 +65,7 @@ public: virtual bool isString() const noexcept { return false; } virtual bool isObject() const noexcept { return false; } virtual bool isArray() const noexcept { return false; } + virtual bool isBinary() const noexcept { return false; } virtual bool isMethod() const noexcept { return false; } virtual void cleanUp (ValueUnion&) const noexcept {} @@ -72,9 +81,27 @@ public: VariantType_Void() noexcept {} static const VariantType_Void instance; - bool isVoid() const noexcept { return true; } - bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept { return otherType.isVoid(); } - void writeToStream (const ValueUnion&, OutputStream& output) const { output.writeCompressedInt (0); } + bool isVoid() const noexcept override { return true; } + bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } + void writeToStream (const ValueUnion&, OutputStream& output) const override { output.writeCompressedInt (0); } +}; + +//============================================================================== +class var::VariantType_Undefined : public var::VariantType +{ +public: + VariantType_Undefined() noexcept {} + static const VariantType_Undefined instance; + + bool isUndefined() const noexcept override { return true; } + String toString (const ValueUnion&) const override { return "undefined"; } + bool equals (const ValueUnion&, const ValueUnion&, const VariantType& otherType) const noexcept override { return otherType.isVoid() || otherType.isUndefined(); } + + void writeToStream (const ValueUnion&, OutputStream& output) const override + { + output.writeCompressedInt (1); + output.writeByte (varMarker_Undefined); + } }; //============================================================================== @@ -84,19 +111,22 @@ public: VariantType_Int() noexcept {} static const VariantType_Int instance; - int toInt (const ValueUnion& data) const noexcept { return data.intValue; }; - int64 toInt64 (const ValueUnion& data) const noexcept { return (int64) data.intValue; }; - double toDouble (const ValueUnion& data) const noexcept { return (double) data.intValue; } - String toString (const ValueUnion& data) const { return String (data.intValue); } - bool toBool (const ValueUnion& data) const noexcept { return data.intValue != 0; } - bool isInt() const noexcept { return true; } + int toInt (const ValueUnion& data) const noexcept override { return data.intValue; }; + int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.intValue; }; + double toDouble (const ValueUnion& data) const noexcept override { return (double) data.intValue; } + String toString (const ValueUnion& data) const override { return String (data.intValue); } + bool toBool (const ValueUnion& data) const noexcept override { return data.intValue != 0; } + bool isInt() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { + if (otherType.isDouble() || otherType.isInt64() || otherType.isString()) + return otherType.equals (otherData, data, *this); + return otherType.toInt (otherData) == data.intValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const + void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (5); output.writeByte (varMarker_Int); @@ -111,19 +141,22 @@ public: VariantType_Int64() noexcept {} static const VariantType_Int64 instance; - int toInt (const ValueUnion& data) const noexcept { return (int) data.int64Value; }; - int64 toInt64 (const ValueUnion& data) const noexcept { return data.int64Value; }; - double toDouble (const ValueUnion& data) const noexcept { return (double) data.int64Value; } - String toString (const ValueUnion& data) const { return String (data.int64Value); } - bool toBool (const ValueUnion& data) const noexcept { return data.int64Value != 0; } - bool isInt64() const noexcept { return true; } + int toInt (const ValueUnion& data) const noexcept override { return (int) data.int64Value; }; + int64 toInt64 (const ValueUnion& data) const noexcept override { return data.int64Value; }; + double toDouble (const ValueUnion& data) const noexcept override { return (double) data.int64Value; } + String toString (const ValueUnion& data) const override { return String (data.int64Value); } + bool toBool (const ValueUnion& data) const noexcept override { return data.int64Value != 0; } + bool isInt64() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { + if (otherType.isDouble() || otherType.isString()) + return otherType.equals (otherData, data, *this); + return otherType.toInt64 (otherData) == data.int64Value; } - void writeToStream (const ValueUnion& data, OutputStream& output) const + void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (9); output.writeByte (varMarker_Int64); @@ -138,19 +171,19 @@ public: VariantType_Double() noexcept {} static const VariantType_Double instance; - int toInt (const ValueUnion& data) const noexcept { return (int) data.doubleValue; }; - int64 toInt64 (const ValueUnion& data) const noexcept { return (int64) data.doubleValue; }; - double toDouble (const ValueUnion& data) const noexcept { return data.doubleValue; } - String toString (const ValueUnion& data) const { return String (data.doubleValue); } - bool toBool (const ValueUnion& data) const noexcept { return data.doubleValue != 0; } - bool isDouble() const noexcept { return true; } + int toInt (const ValueUnion& data) const noexcept override { return (int) data.doubleValue; }; + int64 toInt64 (const ValueUnion& data) const noexcept override { return (int64) data.doubleValue; }; + double toDouble (const ValueUnion& data) const noexcept override { return data.doubleValue; } + String toString (const ValueUnion& data) const override { return String (data.doubleValue); } + bool toBool (const ValueUnion& data) const noexcept override { return data.doubleValue != 0; } + bool isDouble() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return std::abs (otherType.toDouble (otherData) - data.doubleValue) < std::numeric_limits::epsilon(); } - void writeToStream (const ValueUnion& data, OutputStream& output) const + void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (9); output.writeByte (varMarker_Double); @@ -165,19 +198,19 @@ public: VariantType_Bool() noexcept {} static const VariantType_Bool instance; - int toInt (const ValueUnion& data) const noexcept { return data.boolValue ? 1 : 0; }; - int64 toInt64 (const ValueUnion& data) const noexcept { return data.boolValue ? 1 : 0; }; - double toDouble (const ValueUnion& data) const noexcept { return data.boolValue ? 1.0 : 0.0; } - String toString (const ValueUnion& data) const { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } - bool toBool (const ValueUnion& data) const noexcept { return data.boolValue; } - bool isBool() const noexcept { return true; } + int toInt (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; }; + int64 toInt64 (const ValueUnion& data) const noexcept override { return data.boolValue ? 1 : 0; }; + double toDouble (const ValueUnion& data) const noexcept override { return data.boolValue ? 1.0 : 0.0; } + String toString (const ValueUnion& data) const override { return String::charToString (data.boolValue ? (juce_wchar) '1' : (juce_wchar) '0'); } + bool toBool (const ValueUnion& data) const noexcept override { return data.boolValue; } + bool isBool() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.toBool (otherData) == data.boolValue; } - void writeToStream (const ValueUnion& data, OutputStream& output) const + void writeToStream (const ValueUnion& data, OutputStream& output) const override { output.writeCompressedInt (1); output.writeByte (data.boolValue ? (char) varMarker_BoolTrue : (char) varMarker_BoolFalse); @@ -191,30 +224,30 @@ public: VariantType_String() noexcept {} static const VariantType_String instance; - void cleanUp (ValueUnion& data) const noexcept { getString (data)-> ~String(); } - void createCopy (ValueUnion& dest, const ValueUnion& source) const { new (dest.stringValue) String (*getString (source)); } + void cleanUp (ValueUnion& data) const noexcept override { getString (data)-> ~String(); } + void createCopy (ValueUnion& dest, const ValueUnion& source) const override { new (dest.stringValue) String (*getString (source)); } - bool isString() const noexcept { return true; } - int toInt (const ValueUnion& data) const noexcept { return getString (data)->getIntValue(); }; - int64 toInt64 (const ValueUnion& data) const noexcept { return getString (data)->getLargeIntValue(); }; - double toDouble (const ValueUnion& data) const noexcept { return getString (data)->getDoubleValue(); } - String toString (const ValueUnion& data) const { return *getString (data); } - bool toBool (const ValueUnion& data) const noexcept { return getString (data)->getIntValue() != 0 - || getString (data)->trim().equalsIgnoreCase ("true") - || getString (data)->trim().equalsIgnoreCase ("yes"); } + bool isString() const noexcept override { return true; } + int toInt (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue(); }; + int64 toInt64 (const ValueUnion& data) const noexcept override { return getString (data)->getLargeIntValue(); }; + double toDouble (const ValueUnion& data) const noexcept override { return getString (data)->getDoubleValue(); } + String toString (const ValueUnion& data) const override { return *getString (data); } + bool toBool (const ValueUnion& data) const noexcept override { return getString (data)->getIntValue() != 0 + || getString (data)->trim().equalsIgnoreCase ("true") + || getString (data)->trim().equalsIgnoreCase ("yes"); } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.toString (otherData) == *getString (data); } - void writeToStream (const ValueUnion& data, OutputStream& output) const + void writeToStream (const ValueUnion& data, OutputStream& output) const override { const String* const s = getString (data); - const int len = s->getNumBytesAsUTF8() + 1; - HeapBlock temp ((size_t) len); + const size_t len = s->getNumBytesAsUTF8() + 1; + HeapBlock temp (len); s->copyToUTF8 (temp, len); - output.writeCompressedInt (len + 1); + output.writeCompressedInt ((int) (len + 1)); output.writeByte (varMarker_String); output.write (temp, len); } @@ -231,26 +264,35 @@ public: VariantType_Object() noexcept {} static const VariantType_Object instance; - void cleanUp (ValueUnion& data) const noexcept { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } + void cleanUp (ValueUnion& data) const noexcept override { if (data.objectValue != nullptr) data.objectValue->decReferenceCount(); } - void createCopy (ValueUnion& dest, const ValueUnion& source) const + void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.objectValue = source.objectValue; if (dest.objectValue != nullptr) dest.objectValue->incReferenceCount(); } - String toString (const ValueUnion& data) const { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } - bool toBool (const ValueUnion& data) const noexcept { return data.objectValue != 0; } - ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept { return data.objectValue; } - bool isObject() const noexcept { return true; } + String toString (const ValueUnion& data) const override { return "Object 0x" + String::toHexString ((int) (pointer_sized_int) data.objectValue); } + bool toBool (const ValueUnion& data) const noexcept override { return data.objectValue != 0; } + ReferenceCountedObject* toObject (const ValueUnion& data) const noexcept override { return data.objectValue; } + bool isObject() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.toObject (otherData) == data.objectValue; } - void writeToStream (const ValueUnion&, OutputStream& output) const + var clone (const var& original) const override + { + if (DynamicObject* d = original.getDynamicObject()) + return d->clone().get(); + + jassertfalse; // can only clone DynamicObjects! + return var(); + } + + void writeToStream (const ValueUnion&, OutputStream& output) const override { jassertfalse; // Can't write an object to a stream! output.writeCompressedInt (0); @@ -258,37 +300,95 @@ public: }; //============================================================================== -class var::VariantType_Array : public var::VariantType +class var::VariantType_Array : public var::VariantType_Object { public: VariantType_Array() noexcept {} static const VariantType_Array instance; - void cleanUp (ValueUnion& data) const noexcept { delete data.arrayValue; } - void createCopy (ValueUnion& dest, const ValueUnion& source) const { dest.arrayValue = new Array (*(source.arrayValue)); } + String toString (const ValueUnion&) const override { return "[Array]"; } + ReferenceCountedObject* toObject (const ValueUnion&) const noexcept override { return nullptr; } + bool isArray() const noexcept override { return true; } - String toString (const ValueUnion&) const { return "[Array]"; } - bool isArray() const noexcept { return true; } - Array* toArray (const ValueUnion& data) const noexcept { return data.arrayValue; } - - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + Array* toArray (const ValueUnion& data) const noexcept override { - const Array* const otherArray = otherType.toArray (otherData); - return otherArray != nullptr && *otherArray == *(data.arrayValue); + if (RefCountedArray* a = dynamic_cast (data.objectValue)) + return &(a->array); + + return nullptr; } - void writeToStream (const ValueUnion& data, OutputStream& output) const + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { - MemoryOutputStream buffer (512); - const int numItems = data.arrayValue->size(); - buffer.writeCompressedInt (numItems); + const Array* const thisArray = toArray (data); + const Array* const otherArray = otherType.toArray (otherData); + return thisArray == otherArray || (thisArray != nullptr && otherArray != nullptr && *otherArray == *thisArray); + } - for (int i = 0; i < numItems; ++i) - data.arrayValue->getReference(i).writeToStream (buffer); + var clone (const var& original) const override + { + Array arrayCopy; - output.writeCompressedInt (1 + (int) buffer.getDataSize()); - output.writeByte (varMarker_Array); - output << buffer; + if (const Array* array = toArray (original.value)) + for (int i = 0; i < array->size(); ++i) + arrayCopy.add (array->getReference(i).clone()); + + return var (arrayCopy); + } + + void writeToStream (const ValueUnion& data, OutputStream& output) const override + { + if (const Array* array = toArray (data)) + { + MemoryOutputStream buffer (512); + const int numItems = array->size(); + buffer.writeCompressedInt (numItems); + + for (int i = 0; i < numItems; ++i) + array->getReference(i).writeToStream (buffer); + + output.writeCompressedInt (1 + (int) buffer.getDataSize()); + output.writeByte (varMarker_Array); + output << buffer; + } + } + + struct RefCountedArray : public ReferenceCountedObject + { + RefCountedArray (const Array& a) : array (a) { incReferenceCount(); } + #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + RefCountedArray (Array&& a) : array (static_cast&&> (a)) { incReferenceCount(); } + #endif + Array array; + }; +}; + +//============================================================================== +class var::VariantType_Binary : public var::VariantType +{ +public: + VariantType_Binary() noexcept {} + + static const VariantType_Binary instance; + + void cleanUp (ValueUnion& data) const noexcept override { delete data.binaryValue; } + void createCopy (ValueUnion& dest, const ValueUnion& source) const override { dest.binaryValue = new MemoryBlock (*source.binaryValue); } + + String toString (const ValueUnion& data) const override { return data.binaryValue->toBase64Encoding(); } + bool isBinary() const noexcept override { return true; } + MemoryBlock* toBinary (const ValueUnion& data) const noexcept override { return data.binaryValue; } + + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override + { + const MemoryBlock* const otherBlock = otherType.toBinary (otherData); + return otherBlock != nullptr && *otherBlock == *data.binaryValue; + } + + void writeToStream (const ValueUnion& data, OutputStream& output) const override + { + output.writeCompressedInt (1 + (int) data.binaryValue->getSize()); + output.writeByte (varMarker_Binary); + output << *data.binaryValue; } }; @@ -299,16 +399,16 @@ public: VariantType_Method() noexcept {} static const VariantType_Method instance; - String toString (const ValueUnion&) const { return "Method"; } - bool toBool (const ValueUnion& data) const noexcept { return data.methodValue != 0; } - bool isMethod() const noexcept { return true; } + String toString (const ValueUnion&) const override { return "Method"; } + bool toBool (const ValueUnion& data) const noexcept override { return data.methodValue != nullptr; } + bool isMethod() const noexcept override { return true; } - bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept + bool equals (const ValueUnion& data, const ValueUnion& otherData, const VariantType& otherType) const noexcept override { return otherType.isMethod() && otherData.methodValue == data.methodValue; } - void writeToStream (const ValueUnion&, OutputStream& output) const + void writeToStream (const ValueUnion&, OutputStream& output) const override { jassertfalse; // Can't write a method to a stream! output.writeCompressedInt (0); @@ -316,26 +416,23 @@ public: }; //============================================================================== -const var::VariantType_Void var::VariantType_Void::instance; -const var::VariantType_Int var::VariantType_Int::instance; -const var::VariantType_Int64 var::VariantType_Int64::instance; -const var::VariantType_Bool var::VariantType_Bool::instance; -const var::VariantType_Double var::VariantType_Double::instance; -const var::VariantType_String var::VariantType_String::instance; -const var::VariantType_Object var::VariantType_Object::instance; -const var::VariantType_Array var::VariantType_Array::instance; -const var::VariantType_Method var::VariantType_Method::instance; +const var::VariantType_Void var::VariantType_Void::instance; +const var::VariantType_Undefined var::VariantType_Undefined::instance; +const var::VariantType_Int var::VariantType_Int::instance; +const var::VariantType_Int64 var::VariantType_Int64::instance; +const var::VariantType_Bool var::VariantType_Bool::instance; +const var::VariantType_Double var::VariantType_Double::instance; +const var::VariantType_String var::VariantType_String::instance; +const var::VariantType_Object var::VariantType_Object::instance; +const var::VariantType_Array var::VariantType_Array::instance; +const var::VariantType_Binary var::VariantType_Binary::instance; +const var::VariantType_Method var::VariantType_Method::instance; //============================================================================== -var::var() noexcept : type (&VariantType_Void::instance) -{ -} - -var::~var() noexcept -{ - type->cleanUp (value); -} +var::var() noexcept : type (&VariantType_Void::instance) {} +var::var (const VariantType& t) noexcept : type (&t) {} +var::~var() noexcept { type->cleanUp (value); } const var var::null; @@ -345,20 +442,17 @@ var::var (const var& valueToCopy) : type (valueToCopy.type) type->createCopy (value, valueToCopy.value); } -var::var (const int value_) noexcept : type (&VariantType_Int::instance) { value.intValue = value_; } -var::var (const int64 value_) noexcept : type (&VariantType_Int64::instance) { value.int64Value = value_; } -var::var (const bool value_) noexcept : type (&VariantType_Bool::instance) { value.boolValue = value_; } -var::var (const double value_) noexcept : type (&VariantType_Double::instance) { value.doubleValue = value_; } -var::var (MethodFunction method_) noexcept : type (&VariantType_Method::instance) { value.methodValue = method_; } - -var::var (const String& value_) : type (&VariantType_String::instance) { new (value.stringValue) String (value_); } -var::var (const char* const value_) : type (&VariantType_String::instance) { new (value.stringValue) String (value_); } -var::var (const wchar_t* const value_) : type (&VariantType_String::instance) { new (value.stringValue) String (value_); } - -var::var (const Array& value_) : type (&VariantType_Array::instance) -{ - value.arrayValue = new Array (value_); -} +var::var (const int v) noexcept : type (&VariantType_Int::instance) { value.intValue = v; } +var::var (const int64 v) noexcept : type (&VariantType_Int64::instance) { value.int64Value = v; } +var::var (const bool v) noexcept : type (&VariantType_Bool::instance) { value.boolValue = v; } +var::var (const double v) noexcept : type (&VariantType_Double::instance) { value.doubleValue = v; } +var::var (NativeFunction m) noexcept : type (&VariantType_Method::instance) { value.methodValue = m; } +var::var (const Array& v) : type (&VariantType_Array::instance) { value.objectValue = new VariantType_Array::RefCountedArray(v); } +var::var (const String& v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } +var::var (const char* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } +var::var (const wchar_t* const v) : type (&VariantType_String::instance) { new (value.stringValue) String (v); } +var::var (const void* v, size_t sz) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v, sz); } +var::var (const MemoryBlock& v) : type (&VariantType_Binary::instance) { value.binaryValue = new MemoryBlock (v); } var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::instance) { @@ -368,16 +462,20 @@ var::var (ReferenceCountedObject* const object) : type (&VariantType_Object::in object->incReferenceCount(); } +var var::undefined() noexcept { return var (VariantType_Undefined::instance); } + //============================================================================== -bool var::isVoid() const noexcept { return type->isVoid(); } -bool var::isInt() const noexcept { return type->isInt(); } -bool var::isInt64() const noexcept { return type->isInt64(); } -bool var::isBool() const noexcept { return type->isBool(); } -bool var::isDouble() const noexcept { return type->isDouble(); } -bool var::isString() const noexcept { return type->isString(); } -bool var::isObject() const noexcept { return type->isObject(); } -bool var::isArray() const noexcept { return type->isArray(); } -bool var::isMethod() const noexcept { return type->isMethod(); } +bool var::isVoid() const noexcept { return type->isVoid(); } +bool var::isUndefined() const noexcept { return type->isUndefined(); } +bool var::isInt() const noexcept { return type->isInt(); } +bool var::isInt64() const noexcept { return type->isInt64(); } +bool var::isBool() const noexcept { return type->isBool(); } +bool var::isDouble() const noexcept { return type->isDouble(); } +bool var::isString() const noexcept { return type->isString(); } +bool var::isObject() const noexcept { return type->isObject(); } +bool var::isArray() const noexcept { return type->isArray(); } +bool var::isBinaryData() const noexcept { return type->isBinary(); } +bool var::isMethod() const noexcept { return type->isMethod(); } var::operator int() const noexcept { return type->toInt (value); } var::operator int64() const noexcept { return type->toInt64 (value); } @@ -388,6 +486,7 @@ String var::toString() const { return type->toString var::operator String() const { return type->toString (value); } ReferenceCountedObject* var::getObject() const noexcept { return type->toObject (value); } Array* var::getArray() const noexcept { return type->toArray (value); } +MemoryBlock* var::getBinaryData() const noexcept { return type->toBinary (value); } DynamicObject* var::getDynamicObject() const noexcept { return dynamic_cast (getObject()); } //============================================================================== @@ -407,7 +506,7 @@ var& var::operator= (const wchar_t* const v) { type->cleanUp (value); type = var& var::operator= (const String& v) { type->cleanUp (value); type = &VariantType_String::instance; new (value.stringValue) String (v); return *this; } var& var::operator= (const Array& v) { var v2 (v); swapWith (v2); return *this; } var& var::operator= (ReferenceCountedObject* v) { var v2 (v); swapWith (v2); return *this; } -var& var::operator= (MethodFunction v) { var v2 (v); swapWith (v2); return *this; } +var& var::operator= (NativeFunction v) { var v2 (v); swapWith (v2); return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS var::var (var&& other) noexcept @@ -423,9 +522,19 @@ var& var::operator= (var&& other) noexcept return *this; } -var::var (String&& value_) : type (&VariantType_String::instance) +var::var (String&& v) : type (&VariantType_String::instance) { - new (value.stringValue) String (static_cast (value_)); + new (value.stringValue) String (static_cast (v)); +} + +var::var (MemoryBlock&& v) : type (&VariantType_Binary::instance) +{ + value.binaryValue = new MemoryBlock (static_cast (v)); +} + +var::var (Array&& v) : type (&VariantType_Array::instance) +{ + value.objectValue = new VariantType_Array::RefCountedArray (static_cast&&> (v)); } var& var::operator= (String&& v) @@ -448,6 +557,11 @@ bool var::equalsWithSameType (const var& other) const noexcept return type == other.type && equals (other); } +bool var::hasSameTypeAs (const var& other) const noexcept +{ + return type == other.type; +} + bool operator== (const var& v1, const var& v2) noexcept { return v1.equals (v2); } bool operator!= (const var& v1, const var& v2) noexcept { return ! v1.equals (v2); } bool operator== (const var& v1, const String& v2) { return v1.toString() == v2; } @@ -455,12 +569,19 @@ bool operator!= (const var& v1, const String& v2) { return v1.toString bool operator== (const var& v1, const char* const v2) { return v1.toString() == v2; } bool operator!= (const var& v1, const char* const v2) { return v1.toString() != v2; } +//============================================================================== +var var::clone() const noexcept +{ + return type->clone (*this); +} //============================================================================== -var var::operator[] (const Identifier& propertyName) const +var var::operator[] (const Identifier propertyName) const { - DynamicObject* const o = getDynamicObject(); - return o != nullptr ? o->getProperty (propertyName) : var::null; + if (DynamicObject* const o = getDynamicObject()) + return o->getProperty (propertyName); + + return var(); } var var::operator[] (const char* const propertyName) const @@ -468,57 +589,56 @@ var var::operator[] (const char* const propertyName) const return operator[] (Identifier (propertyName)); } -var var::getProperty (const Identifier& propertyName, const var& defaultReturnValue) const +var var::getProperty (const Identifier propertyName, const var& defaultReturnValue) const { - DynamicObject* const o = getDynamicObject(); - return o != nullptr ? o->getProperties().getWithDefault (propertyName, defaultReturnValue) : defaultReturnValue; + if (DynamicObject* const o = getDynamicObject()) + return o->getProperties().getWithDefault (propertyName, defaultReturnValue); + + return defaultReturnValue; } -var var::invoke (const Identifier& method, const var* arguments, int numArguments) const +var::NativeFunction var::getNativeFunction() const { - DynamicObject* const o = getDynamicObject(); - return o != nullptr ? o->invokeMethod (method, arguments, numArguments) : var::null; + return isMethod() ? value.methodValue : nullptr; } -var var::invokeMethod (DynamicObject* const target, const var* const arguments, const int numArguments) const +var var::invoke (Identifier method, const var* arguments, int numArguments) const { - jassert (target != nullptr); + if (DynamicObject* const o = getDynamicObject()) + return o->invokeMethod (method, var::NativeFunctionArgs (*this, arguments, numArguments)); - if (isMethod()) - return (target->*(value.methodValue)) (arguments, numArguments); - - return var::null; + return var(); } -var var::call (const Identifier& method) const +var var::call (const Identifier method) const { return invoke (method, nullptr, 0); } -var var::call (const Identifier& method, const var& arg1) const +var var::call (const Identifier method, const var& arg1) const { return invoke (method, &arg1, 1); } -var var::call (const Identifier& method, const var& arg1, const var& arg2) const +var var::call (const Identifier method, const var& arg1, const var& arg2) const { var args[] = { arg1, arg2 }; return invoke (method, args, 2); } -var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3) +var var::call (const Identifier method, const var& arg1, const var& arg2, const var& arg3) { var args[] = { arg1, arg2, arg3 }; return invoke (method, args, 3); } -var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const +var var::call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const { var args[] = { arg1, arg2, arg3, arg4 }; return invoke (method, args, 4); } -var var::call (const Identifier& method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const +var var::call (const Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const { var args[] = { arg1, arg2, arg3, arg4, arg5 }; return invoke (method, args, 5); @@ -527,8 +647,10 @@ var var::call (const Identifier& method, const var& arg1, const var& arg2, const //============================================================================== int var::size() const { - const Array* const array = getArray(); - return array != nullptr ? array->size() : 0; + if (const Array* const array = getArray()) + return array->size(); + + return 0; } const var& var::operator[] (int arrayIndex) const @@ -555,21 +677,15 @@ var& var::operator[] (int arrayIndex) Array* var::convertToArray() { - Array* array = getArray(); + if (Array* array = getArray()) + return array; - if (array == nullptr) - { - const Array tempVar; - var v (tempVar); - array = v.value.arrayValue; + Array tempVar; + if (! isVoid()) + tempVar.add (*this); - if (! isVoid()) - array->add (*this); - - swapWith (v); - } - - return array; + *this = tempVar; + return getArray(); } void var::append (const var& n) @@ -579,9 +695,7 @@ void var::append (const var& n) void var::remove (const int index) { - Array* const array = getArray(); - - if (array != nullptr) + if (Array* const array = getArray()) array->remove (index); } @@ -597,8 +711,10 @@ void var::resize (const int numArrayElementsWanted) int var::indexOf (const var& n) const { - const Array* const array = getArray(); - return array != nullptr ? array->indexOf (n) : -1; + if (const Array* const array = getArray()) + return array->indexOf (n); + + return -1; } //============================================================================== @@ -627,6 +743,19 @@ var var::readFromStream (InputStream& input) return var (mo.toUTF8()); } + case varMarker_Binary: + { + MemoryBlock mb ((size_t) numBytes - 1); + + if (numBytes > 1) + { + const int numRead = input.read (mb.getData(), numBytes - 1); + mb.setSize ((size_t) numRead); + } + + return var (mb); + } + case varMarker_Array: { var v; @@ -643,5 +772,9 @@ var var::readFromStream (InputStream& input) } } - return var::null; + return var(); } + +var::NativeFunctionArgs::NativeFunctionArgs (const var& t, const var* args, int numArgs) noexcept + : thisObject (t), arguments (args), numArguments (numArgs) +{} diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h index 107bc11..41a4884 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_Variant.h @@ -1,40 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_VARIANT_JUCEHEADER__ -#define __JUCE_VARIANT_JUCEHEADER__ +#ifndef JUCE_VARIANT_H_INCLUDED +#define JUCE_VARIANT_H_INCLUDED -#include "../text/juce_Identifier.h" -#include "../streams/juce_OutputStream.h" -#include "../streams/juce_InputStream.h" -#include "../containers/juce_Array.h" - -#ifndef DOXYGEN - class ReferenceCountedObject; - class DynamicObject; -#endif //============================================================================== /** @@ -53,7 +47,21 @@ class JUCE_API var { public: //============================================================================== - typedef const var (DynamicObject::*MethodFunction) (const var* arguments, int numArguments); + /** This structure is passed to a NativeFunction callback, and contains invocation + details about the function's arguments and context. + */ + struct NativeFunctionArgs + { + NativeFunctionArgs (const var& thisObject, const var* args, int numArgs) noexcept; + + const var& thisObject; + const var* arguments; + int numArguments; + + JUCE_DECLARE_NON_COPYABLE (NativeFunctionArgs) + }; + + typedef var (*NativeFunction) (const NativeFunctionArgs&); typedef Identifier identifier; //============================================================================== @@ -76,7 +84,9 @@ public: var (const String& value); var (const Array& value); var (ReferenceCountedObject* object); - var (MethodFunction method) noexcept; + var (NativeFunction method) noexcept; + var (const void* binaryData, size_t dataSize); + var (const MemoryBlock& binaryData); var& operator= (const var& valueToCopy); var& operator= (int value); @@ -88,17 +98,23 @@ public: var& operator= (const String& value); var& operator= (const Array& value); var& operator= (ReferenceCountedObject* object); - var& operator= (MethodFunction method); + var& operator= (NativeFunction method); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS var (var&& other) noexcept; var (String&& value); + var (MemoryBlock&& binaryData); + var (Array&& value); var& operator= (var&& other) noexcept; var& operator= (String&& value); #endif void swapWith (var& other) noexcept; + /** Returns a var object that can be used where you need the javascript "undefined" value. */ + static var undefined() noexcept; + + //============================================================================== operator int() const noexcept; operator int64() const noexcept; operator bool() const noexcept; @@ -106,11 +122,29 @@ public: operator double() const noexcept; operator String() const; String toString() const; + + /** If this variant holds an array, this provides access to it. + NOTE: Beware when you use this - the array pointer is only valid for the lifetime + of the variant that returned it, so be very careful not to call this method on temporary + var objects that are the return-value of a function, and which may go out of scope before + you use the array! + */ Array* getArray() const noexcept; + + /** If this variant holds a memory block, this provides access to it. + NOTE: Beware when you use this - the MemoryBlock pointer is only valid for the lifetime + of the variant that returned it, so be very careful not to call this method on temporary + var objects that are the return-value of a function, and which may go out of scope before + you use the MemoryBlock! + */ + MemoryBlock* getBinaryData() const noexcept; + ReferenceCountedObject* getObject() const noexcept; DynamicObject* getDynamicObject() const noexcept; + //============================================================================== bool isVoid() const noexcept; + bool isUndefined() const noexcept; bool isInt() const noexcept; bool isInt64() const noexcept; bool isBool() const noexcept; @@ -118,6 +152,7 @@ public: bool isString() const noexcept; bool isObject() const noexcept; bool isArray() const noexcept; + bool isBinaryData() const noexcept; bool isMethod() const noexcept; /** Returns true if this var has the same value as the one supplied. @@ -133,6 +168,15 @@ public: */ bool equalsWithSameType (const var& other) const noexcept; + /** Returns true if this var has the same type as the one supplied. */ + bool hasSameTypeAs (const var& other) const noexcept; + + /** Returns a deep copy of this object. + For simple types this just returns a copy, but if the object contains any arrays + or DynamicObjects, they will be cloned (recursively). + */ + var clone() const noexcept; + //============================================================================== /** If the var is an array, this returns the number of elements. If the var isn't actually an array, this will return 0. @@ -198,27 +242,29 @@ public: //============================================================================== /** If this variant is an object, this returns one of its properties. */ - var operator[] (const Identifier& propertyName) const; + var operator[] (Identifier propertyName) const; /** If this variant is an object, this returns one of its properties. */ var operator[] (const char* propertyName) const; /** If this variant is an object, this returns one of its properties, or a default fallback value if the property is not set. */ - var getProperty (const Identifier& propertyName, const var& defaultReturnValue) const; + var getProperty (Identifier propertyName, const var& defaultReturnValue) const; - /** If this variant is an object, this invokes one of its methods with no arguments. */ - var call (const Identifier& method) const; - /** If this variant is an object, this invokes one of its methods with one argument. */ - var call (const Identifier& method, const var& arg1) const; - /** If this variant is an object, this invokes one of its methods with 2 arguments. */ - 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. */ - 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. */ - 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. */ - 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. */ - var invoke (const Identifier& method, const var* arguments, int numArguments) const; + /** Invokes a named method call with no arguments. */ + var call (Identifier method) const; + /** Invokes a named method call with one argument. */ + var call (Identifier method, const var& arg1) const; + /** Invokes a named method call with 2 arguments. */ + var call (Identifier method, const var& arg1, const var& arg2) const; + /** Invokes a named method call with 3 arguments. */ + var call (Identifier method, const var& arg1, const var& arg2, const var& arg3); + /** Invokes a named method call with 4 arguments. */ + var call (Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4) const; + /** Invokes a named method call with 5 arguments. */ + var call (Identifier method, const var& arg1, const var& arg2, const var& arg3, const var& arg4, const var& arg5) const; + /** Invokes a named method call with a list of arguments. */ + var invoke (Identifier method, const var* arguments, int numArguments) const; + /** If this object is a method, this returns the function pointer. */ + NativeFunction getNativeFunction() const; //============================================================================== /** Writes a binary representation of this value to a stream. @@ -236,16 +282,18 @@ public: private: //============================================================================== - class VariantType; friend class VariantType; - class VariantType_Void; friend class VariantType_Void; - class VariantType_Int; friend class VariantType_Int; - class VariantType_Int64; friend class VariantType_Int64; - class VariantType_Double; friend class VariantType_Double; - class VariantType_Bool; friend class VariantType_Bool; - class VariantType_String; friend class VariantType_String; - class VariantType_Object; friend class VariantType_Object; - class VariantType_Array; friend class VariantType_Array; - class VariantType_Method; friend class VariantType_Method; + class VariantType; friend class VariantType; + class VariantType_Void; friend class VariantType_Void; + class VariantType_Undefined; friend class VariantType_Undefined; + class VariantType_Int; friend class VariantType_Int; + class VariantType_Int64; friend class VariantType_Int64; + class VariantType_Double; friend class VariantType_Double; + class VariantType_Bool; friend class VariantType_Bool; + class VariantType_String; friend class VariantType_String; + class VariantType_Object; friend class VariantType_Object; + class VariantType_Array; friend class VariantType_Array; + class VariantType_Binary; friend class VariantType_Binary; + class VariantType_Method; friend class VariantType_Method; union ValueUnion { @@ -255,16 +303,15 @@ private: double doubleValue; char stringValue [sizeof (String)]; ReferenceCountedObject* objectValue; - Array* arrayValue; - MethodFunction methodValue; + MemoryBlock* binaryValue; + NativeFunction methodValue; }; const VariantType* type; ValueUnion value; Array* convertToArray(); - friend class DynamicObject; - var invokeMethod (DynamicObject*, const var*, int) const; + var (const VariantType&) noexcept; }; /** Compares the values of two var objects, using the var::equals() comparison. */ @@ -277,4 +324,4 @@ bool operator== (const var& v1, const char* v2); bool operator!= (const var& v1, const char* v2); -#endif // __JUCE_VARIANT_JUCEHEADER__ +#endif // JUCE_VARIANT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp index bbe55c1..7d6dd4b 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.cpp @@ -1,50 +1,70 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -DirectoryIterator::DirectoryIterator (const File& directory, - bool isRecursive_, - const String& wildCard_, - const int whatToLookFor_) - : fileFinder (directory, isRecursive_ ? "*" : wildCard_), - wildCard (wildCard_), +DirectoryIterator::DirectoryIterator (const File& directory, bool recursive, + const String& pattern, const int type) + : wildCards (parseWildcards (pattern)), + fileFinder (directory, (recursive || wildCards.size() > 1) ? "*" : pattern), + wildCard (pattern), path (File::addTrailingSeparator (directory.getFullPathName())), index (-1), totalNumFiles (-1), - whatToLookFor (whatToLookFor_), - isRecursive (isRecursive_), + whatToLookFor (type), + isRecursive (recursive), hasBeenAdvanced (false) { // you have to specify the type of files you're looking for! - jassert ((whatToLookFor_ & (File::findFiles | File::findDirectories)) != 0); - jassert (whatToLookFor_ > 0 && whatToLookFor_ <= 7); + jassert ((type & (File::findFiles | File::findDirectories)) != 0); + jassert (type > 0 && type <= 7); } DirectoryIterator::~DirectoryIterator() { } +StringArray DirectoryIterator::parseWildcards (const String& pattern) +{ + StringArray s; + s.addTokens (pattern, ";,", "\"'"); + s.trim(); + s.removeEmptyStrings(); + return s; +} + +bool DirectoryIterator::fileMatches (const StringArray& wildCards, const String& filename) +{ + for (int i = 0; i < wildCards.size(); ++i) + if (filename.matchesWildcard (wildCards[i], ! File::areFileNamesCaseSensitive())) + return true; + + return false; +} + bool DirectoryIterator::next() { return next (nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); @@ -64,8 +84,11 @@ bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResul } String filename; - bool isDirectory, isHidden; - while (fileFinder.next (filename, &isDirectory, &isHidden, fileSize, modTime, creationTime, isReadOnly)) + bool isDirectory, isHidden = false; + + while (fileFinder.next (filename, &isDirectory, + (isHiddenResult != nullptr || (whatToLookFor & File::ignoreHiddenFiles) != 0) ? &isHidden : nullptr, + fileSize, modTime, creationTime, isReadOnly)) { ++index; @@ -86,9 +109,9 @@ bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResul matches = (whatToLookFor & File::findFiles) != 0; } - // if recursive, we're not relying on the OS iterator to do the wildcard match, so do it now.. - if (matches && isRecursive) - matches = filename.matchesWildcard (wildCard, ! File::areFileNamesCaseSensitive()); + // if we're not relying on the OS iterator to do the wildcard match, do it now.. + if (matches && (isRecursive || wildCards.size() > 1)) + matches = fileMatches (wildCards, filename); if (matches && (whatToLookFor & File::ignoreHiddenFiles) != 0) matches = ! isHidden; @@ -101,10 +124,9 @@ bool DirectoryIterator::next (bool* const isDirResult, bool* const isHiddenResul return true; } - else if (subIterator != nullptr) - { - return next(); - } + + if (subIterator != nullptr) + return next (isDirResult, isHiddenResult, fileSize, modTime, creationTime, isReadOnly); } } diff --git a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h index 05e8673..3ca2265 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_DirectoryIterator.h @@ -1,47 +1,47 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_DIRECTORYITERATOR_JUCEHEADER__ -#define __JUCE_DIRECTORYITERATOR_JUCEHEADER__ - -#include "juce_File.h" -#include "../memory/juce_ScopedPointer.h" +#ifndef JUCE_DIRECTORYITERATOR_H_INCLUDED +#define JUCE_DIRECTORYITERATOR_H_INCLUDED //============================================================================== /** - Searches through a the files in a directory, returning each file that is found. + Searches through 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. + If you may be scanning a large number of files, it's usually smarter to use this + class than File::findChildFiles() because it allows you to stop at any time, rather + than having to wait for the entire scan to finish before getting the results. - It can also guess how far it's got using a wildly inaccurate algorithm. + It also provides an estimate of its progress, using a (highly inaccurate!) algorithm. */ class JUCE_API DirectoryIterator { @@ -64,7 +64,8 @@ public: @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 wildCard the file pattern to match. This may contain multiple patterns + separated by a semi-colon or comma, e.g. "*.jpg;*.png" @param whatToLookFor a value from the File::TypesOfFileToFind enum, specifying whether to look for files, directories, or both. */ @@ -131,13 +132,14 @@ private: private: friend class DirectoryIterator; - friend class ScopedPointer; + friend struct ContainerDeletePolicy; ScopedPointer pimpl; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeIterator) }; - friend class ScopedPointer; + friend struct ContainerDeletePolicy; + StringArray wildCards; NativeIterator fileFinder; String wildCard, path; int index; @@ -145,10 +147,13 @@ private: const int whatToLookFor; const bool isRecursive; bool hasBeenAdvanced; - ScopedPointer subIterator; + ScopedPointer subIterator; File currentFile; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator); + static StringArray parseWildcards (const String& pattern); + static bool fileMatches (const StringArray& wildCards, const String& filename); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryIterator) }; -#endif // __JUCE_DIRECTORYITERATOR_JUCEHEADER__ +#endif // JUCE_DIRECTORYITERATOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp index 33da253..695d48e 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -72,7 +75,7 @@ const File File::nonexistent; String File::parseAbsolutePath (const String& p) { if (p.isEmpty()) - return String::empty; + return String(); #if JUCE_WINDOWS // Windows.. @@ -130,21 +133,29 @@ String File::parseAbsolutePath (const String& p) // expand a name of type "~dave/abc" const String userName (path.substring (1).upToFirstOccurrenceOf ("/", false, false)); - struct passwd* const pw = getpwnam (userName.toUTF8()); - if (pw != nullptr) + if (struct passwd* const pw = getpwnam (userName.toUTF8())) path = addTrailingSeparator (pw->pw_dir) + path.fromFirstOccurrenceOf ("/", false, false); } } else if (! path.startsWithChar (separator)) { - /* When you supply a raw string to the File object constructor, it must be an absolute path. - If you're trying to parse a string that may be either a relative path or an absolute path, - you MUST provide a context against which the partial path can be evaluated - you can do - this by simply using File::getChildFile() instead of the File constructor. E.g. saying - "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute - path if that's what was supplied, or would evaluate a partial path relative to the CWD. - */ - jassert (path.startsWith ("./") || path.startsWith ("../")); // (assume that a path "./xyz" is deliberately intended to be relative to the CWD) + #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS + if (! (path.startsWith ("./") || path.startsWith ("../"))) + { + /* When you supply a raw string to the File object constructor, it must be an absolute path. + If you're trying to parse a string that may be either a relative path or an absolute path, + you MUST provide a context against which the partial path can be evaluated - you can do + this by simply using File::getChildFile() instead of the File constructor. E.g. saying + "File::getCurrentWorkingDirectory().getChildFile (myUnknownPath)" would return an absolute + path if that's what was supplied, or would evaluate a partial path relative to the CWD. + */ + jassertfalse; + + #if JUCE_LOG_ASSERTIONS + Logger::writeToLog ("Illegal absolute path: " + path); + #endif + } + #endif return File::getCurrentWorkingDirectory().getChildFile (path).getFullPathName(); } @@ -229,6 +240,9 @@ bool File::moveFileTo (const File& newFile) const if (newFile.fullPath == fullPath) return true; + if (! exists()) + return false; + #if ! NAMES_ARE_CASE_SENSITIVE if (*this != newFile) #endif @@ -275,10 +289,11 @@ String File::getPathUpToLastSlash() const if (lastSlash > 0) return fullPath.substring (0, lastSlash); - else if (lastSlash == 0) + + if (lastSlash == 0) return separatorString; - else - return fullPath; + + return fullPath; } File File::getParentDirectory() const @@ -301,13 +316,13 @@ String File::getFileNameWithoutExtension() const if (lastDot > lastSlash) return fullPath.substring (lastSlash, lastDot); - else - return fullPath.substring (lastSlash); + + return fullPath.substring (lastSlash); } bool File::isAChildOf (const File& potentialParent) const { - if (potentialParent == File::nonexistent) + if (potentialParent.fullPath.isEmpty()) return false; const String ourPath (getPathUpToLastSlash()); @@ -325,66 +340,67 @@ int File::hashCode() const { return fullPath.hashCode(); } int64 File::hashCode64() const { return fullPath.hashCode64(); } //============================================================================== -bool File::isAbsolutePath (const String& path) +bool File::isAbsolutePath (StringRef path) { - return path.startsWithChar (separator) + return path.text[0] == separator #if JUCE_WINDOWS - || (path.isNotEmpty() && path[1] == ':'); + || (path.isNotEmpty() && path.text[1] == ':'); #else - || path.startsWithChar ('~'); + || path.text[0] == '~'; #endif } -File File::getChildFile (String relativePath) const +File File::getChildFile (StringRef relativePath) const { if (isAbsolutePath (relativePath)) - return File (relativePath); + return File (String (relativePath.text)); + + if (relativePath[0] != '.') + return File (addTrailingSeparator (fullPath) + relativePath); String path (fullPath); // It's relative, so remove any ../ or ./ bits at the start.. - if (relativePath[0] == '.') + #if JUCE_WINDOWS + if (relativePath.text.indexOf ((juce_wchar) '/') >= 0) + return getChildFile (String (relativePath.text).replaceCharacter ('/', '\\')); + #endif + + while (relativePath[0] == '.') { - #if JUCE_WINDOWS - relativePath = relativePath.replaceCharacter ('/', '\\'); - #endif + const juce_wchar secondChar = relativePath[1]; - while (relativePath[0] == '.') + if (secondChar == '.') { - const juce_wchar secondChar = relativePath[1]; + const juce_wchar thirdChar = relativePath[2]; - if (secondChar == '.') + if (thirdChar == 0 || thirdChar == separator) { - const juce_wchar thirdChar = relativePath[2]; + const int lastSlash = path.lastIndexOfChar (separator); + if (lastSlash >= 0) + path = path.substring (0, lastSlash); - if (thirdChar == 0 || thirdChar == separator) - { - const int lastSlash = path.lastIndexOfChar (separator); - if (lastSlash >= 0) - path = path.substring (0, lastSlash); - - relativePath = relativePath.substring (3); - } - else - { - break; - } - } - else if (secondChar == separator) - { - relativePath = relativePath.substring (2); + relativePath = relativePath.text + (thirdChar == 0 ? 2 : 3); } else { break; } } + else if (secondChar == separator) + { + relativePath = relativePath.text + 2; + } + else + { + break; + } } return File (addTrailingSeparator (path) + relativePath); } -File File::getSiblingFile (const String& fileName) const +File File::getSiblingFile (StringRef fileName) const { return getParentDirectory().getChildFile (fileName); } @@ -445,13 +461,13 @@ Result File::createDirectory() const } //============================================================================== -Time File::getLastModificationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (m); } -Time File::getLastAccessTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (a); } -Time File::getCreationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (c); } +Time File::getLastModificationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (m); } +Time File::getLastAccessTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (a); } +Time File::getCreationTime() const { int64 m, a, c; getFileTimesInternal (m, a, c); return Time (c); } -bool File::setLastModificationTime (const Time& t) const { return setFileTimesInternal (t.toMilliseconds(), 0, 0); } -bool File::setLastAccessTime (const Time& t) const { return setFileTimesInternal (0, t.toMilliseconds(), 0); } -bool File::setCreationTime (const Time& t) const { return setFileTimesInternal (0, 0, t.toMilliseconds()); } +bool File::setLastModificationTime (Time t) const { return setFileTimesInternal (t.toMilliseconds(), 0, 0); } +bool File::setLastAccessTime (Time t) const { return setFileTimesInternal (0, t.toMilliseconds(), 0); } +bool File::setCreationTime (Time t) const { return setFileTimesInternal (0, 0, t.toMilliseconds()); } //============================================================================== bool File::loadFileAsData (MemoryBlock& destBlock) const @@ -466,11 +482,11 @@ bool File::loadFileAsData (MemoryBlock& destBlock) const String File::loadFileAsString() const { if (! existsAsFile()) - return String::empty; + return String(); FileInputStream in (*this); return in.openedOk() ? in.readEntireStreamAsString() - : String::empty; + : String(); } void File::readLines (StringArray& destLines) const @@ -484,10 +500,9 @@ int File::findChildFiles (Array& results, const bool searchRecursively, const String& wildCardPattern) const { - DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor); - int total = 0; - while (di.next()) + + for (DirectoryIterator di (*this, searchRecursively, wildCardPattern, whatToLookFor); di.next();) { results.add (di.getFile()); ++total; @@ -498,10 +513,9 @@ int File::findChildFiles (Array& results, int File::getNumberOfChildFiles (const int whatToLookFor, const String& wildCardPattern) const { - DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor); - int total = 0; - while (di.next()) + + for (DirectoryIterator di (*this, false, wildCardPattern, whatToLookFor); di.next();) ++total; return total; @@ -584,42 +598,38 @@ String File::getFileExtension() const if (indexOfDot > fullPath.lastIndexOfChar (separator)) return fullPath.substring (indexOfDot); - return String::empty; + return String(); } -bool File::hasFileExtension (const String& possibleSuffix) const +bool File::hasFileExtension (StringRef possibleSuffix) const { if (possibleSuffix.isEmpty()) return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (separator); - const int semicolon = possibleSuffix.indexOfChar (0, ';'); + const int semicolon = possibleSuffix.text.indexOf ((juce_wchar) ';'); if (semicolon >= 0) - { - return hasFileExtension (possibleSuffix.substring (0, semicolon).trimEnd()) - || hasFileExtension (possibleSuffix.substring (semicolon + 1).trimStart()); - } - else - { - if (fullPath.endsWithIgnoreCase (possibleSuffix)) - { - if (possibleSuffix.startsWithChar ('.')) - return true; + return hasFileExtension (String (possibleSuffix.text).substring (0, semicolon).trimEnd()) + || hasFileExtension ((possibleSuffix.text + (semicolon + 1)).findEndOfWhitespace()); - const int dotPos = fullPath.length() - possibleSuffix.length() - 1; + if (fullPath.endsWithIgnoreCase (possibleSuffix)) + { + if (possibleSuffix.text[0] == '.') + return true; - if (dotPos >= 0) - return fullPath [dotPos] == '.'; - } + const int dotPos = fullPath.length() - possibleSuffix.length() - 1; + + if (dotPos >= 0) + return fullPath [dotPos] == '.'; } return false; } -File File::withFileExtension (const String& newExtension) const +File File::withFileExtension (StringRef newExtension) const { if (fullPath.isEmpty()) - return File::nonexistent; + return File(); String filePart (getFileName()); @@ -627,7 +637,7 @@ File File::withFileExtension (const String& newExtension) const if (i >= 0) filePart = filePart.substring (0, i); - if (newExtension.isNotEmpty() && ! newExtension.startsWithChar ('.')) + if (newExtension.isNotEmpty() && newExtension.text[0] != '.') filePart << '.'; return getSiblingFile (filePart + newExtension); @@ -642,13 +652,15 @@ bool File::startAsProcess (const String& parameters) const //============================================================================== FileInputStream* File::createInputStream() const { - if (existsAsFile()) - return new FileInputStream (*this); + ScopedPointer fin (new FileInputStream (*this)); + + if (fin->openedOk()) + return fin.release(); return nullptr; } -FileOutputStream* File::createOutputStream (const int bufferSize) const +FileOutputStream* File::createOutputStream (const size_t bufferSize) const { ScopedPointer out (new FileOutputStream (*this, bufferSize)); @@ -658,9 +670,11 @@ FileOutputStream* File::createOutputStream (const int bufferSize) const //============================================================================== bool File::appendData (const void* const dataToAppend, - const int numberOfBytes) const + const size_t numberOfBytes) const { - if (numberOfBytes <= 0) + jassert (((ssize_t) numberOfBytes) >= 0); + + if (numberOfBytes == 0) return true; FileOutputStream out (*this, 8192); @@ -668,11 +682,9 @@ bool File::appendData (const void* const dataToAppend, } bool File::replaceWithData (const void* const dataToWrite, - const int numberOfBytes) const + const size_t numberOfBytes) const { - jassert (numberOfBytes >= 0); // a negative number of bytes?? - - if (numberOfBytes <= 0) + if (numberOfBytes == 0) return deleteFile(); TemporaryFile tempFile (*this, TemporaryFile::useHiddenFile); @@ -714,7 +726,7 @@ bool File::hasIdenticalContentTo (const File& other) const if (in1.openedOk() && in2.openedOk()) { const int bufferSize = 4096; - HeapBlock buffer1 (bufferSize), buffer2 (bufferSize); + HeapBlock buffer1 (bufferSize), buffer2 (bufferSize); for (;;) { @@ -742,7 +754,7 @@ String File::createLegalPathName (const String& original) String s (original); String start; - if (s[1] == ':') + if (s.isNotEmpty() && s[1] == ':') { start = s.substring (0, 2); s = s.substring (2); @@ -857,7 +869,7 @@ String File::getRelativePathFrom (const File& dir) const } //============================================================================== -File File::createTempFile (const String& fileNameEnding) +File File::createTempFile (StringRef fileNameEnding) { const File tempFile (getSpecialLocation (tempDirectory) .getChildFile ("temp_" + String::toHexString (Random::getSystemRandom().nextInt())) @@ -869,6 +881,19 @@ File File::createTempFile (const String& fileNameEnding) return tempFile; } +//============================================================================== +MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode) + : address (nullptr), range (0, file.getSize()), fileHandle (0) +{ + openInternal (file, mode); +} + +MemoryMappedFile::MemoryMappedFile (const File& file, const Range& fileRange, AccessMode mode) + : address (nullptr), range (fileRange.getIntersectionWith (Range (0, file.getSize()))), fileHandle (0) +{ + openInternal (file, mode); +} + //============================================================================== #if JUCE_UNIT_TESTS diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.h b/JuceLibraryCode/modules/juce_core/files/juce_File.h index cc0a38f..28c2e70 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.h @@ -1,39 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_FILE_JUCEHEADER__ -#define __JUCE_FILE_JUCEHEADER__ - -#include "../containers/juce_Array.h" -#include "../time/juce_Time.h" -#include "../text/juce_StringArray.h" -#include "../memory/juce_MemoryBlock.h" -#include "../memory/juce_ScopedPointer.h" -#include "../misc/juce_Result.h" -class FileInputStream; -class FileOutputStream; +#ifndef JUCE_FILE_H_INCLUDED +#define JUCE_FILE_H_INCLUDED //============================================================================== @@ -72,10 +66,10 @@ public: On the Mac/Linux, the path can include "~" notation for referring to user home directories. */ - File (const String& path); + File (const String& absolutePath); /** Creates a copy of another file object. */ - File (const File& other); + File (const File&); /** Destructor. */ ~File() noexcept {} @@ -90,14 +84,14 @@ public: On the Mac/Linux, the path can include "~" notation for referring to user home directories. */ - File& operator= (const String& newFilePath); + File& operator= (const String& newAbsolutePath); /** Copies from another file object. */ File& operator= (const File& otherFile); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - File (File&& otherFile) noexcept; - File& operator= (File&& otherFile) noexcept; + File (File&&) noexcept; + File& operator= (File&&) noexcept; #endif //============================================================================== @@ -202,15 +196,14 @@ public: @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 + will have the same effect. 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; + bool hasFileExtension (StringRef extensionToTest) const; /** Returns a version of this file with a different file extension. @@ -222,7 +215,7 @@ public: @see getFileName, getFileExtension, hasFileExtension, getFileNameWithoutExtension */ - File withFileExtension (const String& newExtension) const; + File withFileExtension (StringRef newExtension) const; /** Returns the last part of the filename, without its file extension. @@ -248,12 +241,13 @@ public: int64 hashCode64() const; //============================================================================== - /** Returns a file based on a relative path. + /** Returns a file that represents a relative (or absolute) sub-path of the current one. 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 ("haddock/foo.txt") will produce "/moose/fish/haddock/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. @@ -261,7 +255,7 @@ public: @see getSiblingFile, getParentDirectory, getRelativePathFrom, isAChildOf */ - File getChildFile (String relativePath) const; + File getChildFile (StringRef relativeOrAbsolutePath) const; /** Returns a file which is in the same directory as this one. @@ -269,7 +263,7 @@ public: @see getChildFile, getParentDirectory */ - File getSiblingFile (const String& siblingFileName) const; + File getSiblingFile (StringRef siblingFileName) const; //============================================================================== /** Returns the directory that contains this file or directory. @@ -324,13 +318,13 @@ public: //============================================================================== /** Compares the pathnames for two files. */ - bool operator== (const File& otherFile) const; + bool operator== (const File&) const; /** Compares the pathnames for two files. */ - bool operator!= (const File& otherFile) const; + bool operator!= (const File&) const; /** Compares the pathnames for two files. */ - bool operator< (const File& otherFile) const; + bool operator< (const File&) const; /** Compares the pathnames for two files. */ - bool operator> (const File& otherFile) const; + bool operator> (const File&) const; //============================================================================== /** Checks whether a file can be created or written to. @@ -355,17 +349,26 @@ public: 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. + /** Returns true if this file is a link or alias that can be followed using getLinkedTarget(). */ + bool isLink() const; - If this file isn't actually link, it'll just return itself. + /** If this file is a link or alias, this returns the file that it points to. + If the file isn't actually link, it'll just return itself. */ File getLinkedTarget() const; + /** Returns a unique identifier for the file, if one is available. + + Depending on the OS and file-system, this may be a unix inode number or + a win32 file identifier, or 0 if it fails to find one. The number will + be unique on the filesystem, but not globally. + */ + uint64 getFileIdentifier() const; + //============================================================================== /** Returns the last modification time of this file. @@ -394,7 +397,7 @@ public: @returns true if it manages to change the file's time. @see getLastModificationTime, setLastAccessTime, setCreationTime */ - bool setLastModificationTime (const Time& newTime) const; + bool setLastModificationTime (Time newTime) const; /** Changes the last-access time for this file. @@ -402,7 +405,7 @@ public: @returns true if it manages to change the file's time. @see getLastAccessTime, setLastModificationTime, setCreationTime */ - bool setLastAccessTime (const Time& newTime) const; + bool setLastAccessTime (Time newTime) const; /** Changes the creation date for this file. @@ -410,7 +413,7 @@ public: @returns true if it manages to change the file's time. @see getCreationTime, setLastModificationTime, setLastAccessTime */ - bool setCreationTime (const Time& newTime) const; + bool setCreationTime (Time newTime) const; /** If possible, this will try to create a version string for the given file. @@ -573,7 +576,7 @@ public: /** 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 + start of the file), or nullptr if the file can't be opened for some reason @see createOutputStream, loadFileAsData */ FileInputStream* createInputStream() const; @@ -585,10 +588,10 @@ public: 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 + end of the file), or nullptr if the file can't be opened for some reason @see createInputStream, appendData, appendText */ - FileOutputStream* createOutputStream (int bufferSize = 0x8000) const; + FileOutputStream* createOutputStream (size_t bufferSize = 0x8000) const; //============================================================================== /** Loads a file's contents into memory as a block of binary data. @@ -607,8 +610,8 @@ public: 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. + This makes use of InputStream::readEntireStreamAsString, which can + read either UTF-16 or UTF-8 file formats. */ String loadFileAsString() const; @@ -625,7 +628,7 @@ public: @returns false if it can't write to the file for some reason */ bool appendData (const void* dataToAppend, - int numberOfBytes) const; + size_t numberOfBytes) const; /** Replaces this file's contents with a given block of data. @@ -642,7 +645,7 @@ public: @see appendText */ bool replaceWithData (const void* dataToWrite, - int numberOfBytes) const; + size_t numberOfBytes) const; /** Appends a string to the end of the file. @@ -695,13 +698,11 @@ public: 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 */ 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; @@ -748,7 +749,7 @@ public: @see revealToUser */ - bool startAsProcess (const String& parameters = String::empty) const; + bool startAsProcess (const String& parameters = String()) const; /** Opens Finder, Explorer, or whatever the OS uses, to show the user this file's location. @see startAsProcess @@ -772,6 +773,15 @@ public: /** The folder that contains the user's desktop objects. */ userDesktopDirectory, + /** 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, + + /** The most likely place where a user might store their picture files. */ + userPicturesDirectory, + /** 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, @@ -789,8 +799,14 @@ public: */ commonApplicationDataDirectory, - /** The folder that should be used for temporary files. + /** A place to put documents which are shared by all users of the machine. + On Windows this may be somewhere like "C:\Users\Public\Documents", on OSX it + will be something like "/Users/Shared". Other OSes may have no such concept + though, so be careful. + */ + commonDocumentsDirectory, + /** The folder that should be used for temporary files. Always delete them when you're finished, to keep the user's computer tidy! */ tempDirectory, @@ -829,19 +845,10 @@ public: hostApplicationPath, /** 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, + globalApplicationsDirectory }; /** Finds the location of a special type of file or directory, such as a home folder or @@ -853,17 +860,14 @@ public: //============================================================================== /** 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 File createTempFile (const String& fileNameEnding); + static File createTempFile (StringRef fileNameEnding); //============================================================================== /** Returns the current working directory. - @see setAsCurrentWorkingDirectory */ static File getCurrentWorkingDirectory(); @@ -879,31 +883,30 @@ public: //============================================================================== /** 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. + /** Returns a version of a filename with any illegal characters removed. 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. + Because this will remove slashes, don't use it on an absolute pathname - use + createLegalPathName() for that. @see createLegalPathName */ static String createLegalFileName (const String& fileNameToFix); - /** Removes illegal characters from a pathname. + /** Returns a version of a path with any illegal characters removed. Similar to createLegalFileName(), but this won't remove slashes, so can be used on a complete pathname. @@ -912,20 +915,18 @@ public: */ static String createLegalPathName (const String& pathNameToFix); - /** Indicates whether filenames are case-sensitive on the current operating system. - */ + /** 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); + /** Returns true if the string seems to be a fully-specified absolute path. */ + static bool isAbsolutePath (StringRef 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 File createFileWithoutCheckingPath (const String& path) noexcept; + static File createFileWithoutCheckingPath (const String& absolutePath) noexcept; /** Adds a separator character to the end of a path if it doesn't already have one. */ static String addTrailingSeparator (const String& path); @@ -944,6 +945,11 @@ public: void addToDock() const; #endif + #if JUCE_WINDOWS + /** Windows ONLY - Creates a win32 .LNK shortcut file that links to this file. */ + bool createLink (const String& description, const File& linkFileToCreate) const; + #endif + private: //============================================================================== String fullPath; @@ -954,11 +960,9 @@ private: Result createDirectoryInternal (const String&) const; bool copyInternal (const File&) const; bool moveInternal (const File&) const; - bool setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 creationTime) const; - void getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const; - bool setFileReadOnlyInternal (bool shouldBeReadOnly) const; - - JUCE_LEAK_DETECTOR (File); + bool setFileTimesInternal (int64 m, int64 a, int64 c) const; + void getFileTimesInternal (int64& m, int64& a, int64& c) const; + bool setFileReadOnlyInternal (bool) const; }; -#endif // __JUCE_FILE_JUCEHEADER__ +#endif // JUCE_FILE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp new file mode 100644 index 0000000..bd0bdf8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.cpp @@ -0,0 +1,41 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +FileFilter::FileFilter (const String& filterDescription) + : description (filterDescription) +{ +} + +FileFilter::~FileFilter() +{ +} + +const String& FileFilter::getDescription() const noexcept +{ + return description; +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileFilter.h b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h similarity index 54% rename from JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileFilter.h rename to JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h index 6a80af5..4c02415 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileFilter.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileFilter.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_FILEFILTER_JUCEHEADER__ -#define __JUCE_FILEFILTER_JUCEHEADER__ +#ifndef JUCE_FILEFILTER_H_INCLUDED +#define JUCE_FILEFILTER_H_INCLUDED //============================================================================== @@ -71,4 +74,4 @@ protected: }; -#endif // __JUCE_FILEFILTER_JUCEHEADER__ +#endif // JUCE_FILEFILTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp index c07fdd3..801ccfa 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.cpp @@ -1,66 +1,61 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ int64 juce_fileSetPosition (void* handle, int64 pos); + //============================================================================== FileInputStream::FileInputStream (const File& f) : file (f), fileHandle (nullptr), currentPosition (0), - totalSize (0), - status (Result::ok()), - needToSeek (true) + status (Result::ok()) { openHandle(); } -FileInputStream::~FileInputStream() -{ - closeHandle(); -} - -//============================================================================== int64 FileInputStream::getTotalLength() { - return totalSize; + // You should always check that a stream opened successfully before using it! + jassert (openedOk()); + + return file.getSize(); } int FileInputStream::read (void* buffer, int bytesToRead) { + // You should always check that a stream opened successfully before using it! jassert (openedOk()); + + // The buffer should never be null, and a negative size is probably a + // sign that something is broken! jassert (buffer != nullptr && bytesToRead >= 0); - if (needToSeek) - { - if (juce_fileSetPosition (fileHandle, currentPosition) < 0) - return 0; - - needToSeek = false; - } - const size_t num = readInternal (buffer, (size_t) bytesToRead); currentPosition += num; @@ -69,7 +64,7 @@ int FileInputStream::read (void* buffer, int bytesToRead) bool FileInputStream::isExhausted() { - return currentPosition >= totalSize; + return currentPosition >= getTotalLength(); } int64 FileInputStream::getPosition() @@ -79,11 +74,11 @@ int64 FileInputStream::getPosition() bool FileInputStream::setPosition (int64 pos) { + // You should always check that a stream opened successfully before using it! jassert (openedOk()); - pos = jlimit ((int64) 0, totalSize, pos); - needToSeek |= (currentPosition != pos); - currentPosition = pos; + if (pos != currentPosition) + currentPosition = juce_fileSetPosition (fileHandle, pos); - return true; + return currentPosition == pos; } diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h index 1e45ff1..35963ba 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileInputStream.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_FILEINPUTSTREAM_JUCEHEADER__ -#define __JUCE_FILEINPUTSTREAM_JUCEHEADER__ - -#include "juce_File.h" -#include "../streams/juce_InputStream.h" +#ifndef JUCE_FILEINPUTSTREAM_H_INCLUDED +#define JUCE_FILEINPUTSTREAM_H_INCLUDED //============================================================================== @@ -40,10 +40,11 @@ class JUCE_API FileInputStream : public InputStream { public: //============================================================================== - /** Creates a FileInputStream. + /** Creates a FileInputStream to read from the given file. - @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 + After creating a FileInputStream, you should use openedOk() or failedToOpen() + to make sure that it's OK before trying to read from it! If it failed, you + can call getStatus() to get more error information. */ explicit FileInputStream (const File& fileToRead); @@ -72,25 +73,24 @@ public: //============================================================================== - int64 getTotalLength(); - int read (void* destBuffer, int maxBytesToRead); - bool isExhausted(); - int64 getPosition(); - bool setPosition (int64 pos); + int64 getTotalLength() override; + int read (void*, int) override; + bool isExhausted() override; + int64 getPosition() override; + bool setPosition (int64) override; private: //============================================================================== - File file; + const File file; void* fileHandle; - int64 currentPosition, totalSize; + int64 currentPosition; Result status; - bool needToSeek; void openHandle(); - void closeHandle(); - size_t readInternal (void* buffer, size_t numBytes); + size_t readInternal (void*, size_t); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputStream) }; -#endif // __JUCE_FILEINPUTSTREAM_JUCEHEADER__ + +#endif // JUCE_FILEINPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp index 5409b1d..961a8d1 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -26,14 +29,14 @@ int64 juce_fileSetPosition (void* handle, int64 pos); //============================================================================== -FileOutputStream::FileOutputStream (const File& f, const int bufferSize_) +FileOutputStream::FileOutputStream (const File& f, const size_t bufferSizeToUse) : file (f), fileHandle (nullptr), status (Result::ok()), currentPosition (0), - bufferSize (bufferSize_), + bufferSize (bufferSizeToUse), bytesInBuffer (0), - buffer ((size_t) jmax (bufferSize_, 16)) + buffer (jmax (bufferSizeToUse, (size_t) 16)) { openHandle(); } @@ -41,7 +44,6 @@ FileOutputStream::FileOutputStream (const File& f, const int bufferSize_) FileOutputStream::~FileOutputStream() { flushBuffer(); - flushInternal(); closeHandle(); } @@ -67,7 +69,7 @@ bool FileOutputStream::flushBuffer() if (bytesInBuffer > 0) { - ok = (writeInternal (buffer, bytesInBuffer) == bytesInBuffer); + ok = (writeInternal (buffer, bytesInBuffer) == (ssize_t) bytesInBuffer); bytesInBuffer = 0; } @@ -80,13 +82,13 @@ void FileOutputStream::flush() flushInternal(); } -bool FileOutputStream::write (const void* const src, const int numBytes) +bool FileOutputStream::write (const void* const src, const size_t numBytes) { - jassert (src != nullptr && numBytes >= 0); + jassert (src != nullptr && ((ssize_t) numBytes) >= 0); if (bytesInBuffer + numBytes < bufferSize) { - memcpy (buffer + bytesInBuffer, src, (size_t) numBytes); + memcpy (buffer + bytesInBuffer, src, numBytes); bytesInBuffer += numBytes; currentPosition += numBytes; } @@ -97,37 +99,36 @@ bool FileOutputStream::write (const void* const src, const int numBytes) if (numBytes < bufferSize) { - memcpy (buffer + bytesInBuffer, src, (size_t) numBytes); + memcpy (buffer + bytesInBuffer, src, numBytes); bytesInBuffer += numBytes; currentPosition += numBytes; } else { - const int bytesWritten = writeInternal (src, numBytes); + const ssize_t bytesWritten = writeInternal (src, numBytes); if (bytesWritten < 0) return false; currentPosition += bytesWritten; - return bytesWritten == numBytes; + return bytesWritten == (ssize_t) numBytes; } } return true; } -void FileOutputStream::writeRepeatedByte (uint8 byte, int numBytes) +bool FileOutputStream::writeRepeatedByte (uint8 byte, size_t numBytes) { - jassert (numBytes >= 0); + jassert (((ssize_t) numBytes) >= 0); if (bytesInBuffer + numBytes < bufferSize) { - memset (buffer + bytesInBuffer, byte, (size_t) numBytes); + memset (buffer + bytesInBuffer, byte, numBytes); bytesInBuffer += numBytes; currentPosition += numBytes; + return true; } - else - { - OutputStream::writeRepeatedByte (byte, numBytes); - } + + return OutputStream::writeRepeatedByte (byte, numBytes); } diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h index 6d078c1..f80705f 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileOutputStream.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ -#define __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ - -#include "juce_File.h" -#include "../streams/juce_OutputStream.h" +#ifndef JUCE_FILEOUTPUTSTREAM_H_INCLUDED +#define JUCE_FILEOUTPUTSTREAM_H_INCLUDED //============================================================================== @@ -54,7 +54,7 @@ public: @see TemporaryFile */ FileOutputStream (const File& fileToWriteTo, - int bufferSizeToUse = 16384); + size_t bufferSizeToUse = 16384); /** Destructor. */ ~FileOutputStream(); @@ -87,11 +87,11 @@ public: Result truncate(); //============================================================================== - void flush(); - int64 getPosition(); - bool setPosition (int64 pos); - bool write (const void* data, int numBytes); - void writeRepeatedByte (uint8 byte, int numTimesToRepeat); + void flush() override; + int64 getPosition() override; + bool setPosition (int64) override; + bool write (const void*, size_t) override; + bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override; private: @@ -100,17 +100,17 @@ private: void* fileHandle; Result status; int64 currentPosition; - int bufferSize, bytesInBuffer; + size_t bufferSize, bytesInBuffer; HeapBlock buffer; void openHandle(); void closeHandle(); void flushInternal(); bool flushBuffer(); - int64 setPositionInternal (int64 newPosition); - int writeInternal (const void* data, int numBytes); + int64 setPositionInternal (int64); + ssize_t writeInternal (const void*, size_t); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileOutputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileOutputStream) }; -#endif // __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ +#endif // JUCE_FILEOUTPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp index d5d0b1d..ce0af7e 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.cpp @@ -1,31 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -FileSearchPath::FileSearchPath() -{ -} +FileSearchPath::FileSearchPath() {} +FileSearchPath::~FileSearchPath() {} FileSearchPath::FileSearchPath (const String& path) { @@ -33,12 +35,14 @@ FileSearchPath::FileSearchPath (const String& path) } FileSearchPath::FileSearchPath (const FileSearchPath& other) - : directories (other.directories) + : directories (other.directories) { } -FileSearchPath::~FileSearchPath() +FileSearchPath& FileSearchPath::operator= (const FileSearchPath& other) { + directories = other.directories; + return *this; } FileSearchPath& FileSearchPath::operator= (const String& path) diff --git a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h index 3ce5524..51baf2e 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_FileSearchPath.h @@ -1,38 +1,38 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_FILESEARCHPATH_JUCEHEADER__ -#define __JUCE_FILESEARCHPATH_JUCEHEADER__ - -#include "juce_File.h" -#include "../text/juce_StringArray.h" +#ifndef JUCE_FILESEARCHPATH_H_INCLUDED +#define JUCE_FILESEARCHPATH_H_INCLUDED //============================================================================== /** - Encapsulates a set of folders that make up a search path. + Represents a set of folders that make up a search path. @see File */ @@ -53,7 +53,10 @@ public: FileSearchPath (const String& path); /** Creates a copy of another search path. */ - FileSearchPath (const FileSearchPath& other); + FileSearchPath (const FileSearchPath&); + + /** Copies another search path. */ + FileSearchPath& operator= (const FileSearchPath&); /** Destructor. */ ~FileSearchPath(); @@ -67,15 +70,12 @@ public: //============================================================================== /** 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 */ File operator[] (int index) const; @@ -99,10 +99,9 @@ public: void remove (int indexToRemove); /** Merges another search path into this one. - This will remove any duplicate directories. */ - void addPath (const FileSearchPath& other); + void addPath (const FileSearchPath&); /** Removes any directories that are actually subdirectories of one of the other directories in the search path. @@ -158,9 +157,9 @@ private: //============================================================================== StringArray directories; - void init (const String& path); + void init (const String&); - JUCE_LEAK_DETECTOR (FileSearchPath); + JUCE_LEAK_DETECTOR (FileSearchPath) }; -#endif // __JUCE_FILESEARCHPATH_JUCEHEADER__ +#endif // JUCE_FILESEARCHPATH_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h b/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h index 210d9e8..8a79185 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_MemoryMappedFile.h @@ -1,32 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__ -#define __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__ +#ifndef JUCE_MEMORYMAPPEDFILE_H_INCLUDED +#define JUCE_MEMORYMAPPEDFILE_H_INCLUDED -#include "juce_File.h" //============================================================================== /** @@ -57,6 +59,26 @@ public: */ MemoryMappedFile (const File& file, AccessMode mode); + /** Opens a section of a file and maps it to an area of virtual memory. + + The file should already exist, and should already be the size that you want to work with + when you call this. If the file is resized after being opened, the behaviour is undefined. + + If the file exists and the operation succeeds, the getData() and getSize() methods will + return the location and size of the data that can be read or written. Note that the entire + file is not read into memory immediately - the OS simply creates a virtual mapping, which + will lazily pull the data into memory when blocks are accessed. + + If the file can't be opened for some reason, the getData() method will return a null pointer. + + NOTE: the start of the actual range used may be rounded-down to a multiple of the OS's page-size, + so do not assume that the mapped memory will begin at exactly the position you requested - always + use getRange() to check the actual range that is being used. + */ + MemoryMappedFile (const File& file, + const Range& fileRange, + AccessMode mode); + /** Destructor. */ ~MemoryMappedFile(); @@ -68,13 +90,15 @@ public: /** Returns the number of bytes of data that are available for reading or writing. This will normally be the size of the file. */ - size_t getSize() const noexcept { return length; } + size_t getSize() const noexcept { return (size_t) range.getLength(); } + /** Returns the section of the file at which the mapped memory represents. */ + Range getRange() const noexcept { return range; } private: //============================================================================== void* address; - size_t length; + Range range; #if JUCE_WINDOWS void* fileHandle; @@ -82,8 +106,10 @@ private: int fileHandle; #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile); + void openInternal (const File&, AccessMode); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile) }; -#endif // __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__ +#endif // JUCE_MEMORYMAPPEDFILE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp index 24c6f1e..50475d5 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp @@ -1,56 +1,61 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) +static File createTempFile (const File& parentDirectory, String name, + const String& suffix, const int optionFlags) { - createTempFile (File::getSpecialLocation (File::tempDirectory), - "temp_" + String::toHexString (Random::getSystemRandom().nextInt()), - suffix, - optionFlags); -} - -TemporaryFile::TemporaryFile (const File& targetFile_, const int optionFlags) - : targetFile (targetFile_) -{ - // If you use this constructor, you need to give it a valid target file! - jassert (targetFile != File::nonexistent); - - createTempFile (targetFile.getParentDirectory(), - targetFile.getFileNameWithoutExtension() - + "_temp" + String::toHexString (Random::getSystemRandom().nextInt()), - targetFile.getFileExtension(), - optionFlags); -} - -void TemporaryFile::createTempFile (const File& parentDirectory, String name, - const String& suffix, const int optionFlags) -{ - if ((optionFlags & useHiddenFile) != 0) + if ((optionFlags & TemporaryFile::useHiddenFile) != 0) name = "." + name; - temporaryFile = parentDirectory.getNonexistentChildFile (name, suffix, (optionFlags & putNumbersInBrackets) != 0); + return parentDirectory.getNonexistentChildFile (name, suffix, (optionFlags & TemporaryFile::putNumbersInBrackets) != 0); +} + +TemporaryFile::TemporaryFile (const String& suffix, const int optionFlags) + : temporaryFile (createTempFile (File::getSpecialLocation (File::tempDirectory), + "temp_" + String::toHexString (Random::getSystemRandom().nextInt()), + suffix, optionFlags)) +{ +} + +TemporaryFile::TemporaryFile (const File& target, const int optionFlags) + : temporaryFile (createTempFile (target.getParentDirectory(), + target.getFileNameWithoutExtension() + + "_temp" + String::toHexString (Random::getSystemRandom().nextInt()), + target.getFileExtension(), optionFlags)), + targetFile (target) +{ + // If you use this constructor, you need to give it a valid target file! + jassert (targetFile != File()); +} + +TemporaryFile::TemporaryFile (const File& target, const File& temporary) + : temporaryFile (temporary), targetFile (target) +{ } TemporaryFile::~TemporaryFile() @@ -74,7 +79,7 @@ bool TemporaryFile::overwriteTargetFileWithTemporary() const { // This method only works if you created this object with the constructor // that takes a target file! - jassert (targetFile != File::nonexistent); + jassert (targetFile != File()); if (temporaryFile.exists()) { diff --git a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h index 38e0982..04561a7 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_TEMPORARYFILE_JUCEHEADER__ -#define __JUCE_TEMPORARYFILE_JUCEHEADER__ - -#include "juce_File.h" +#ifndef JUCE_TEMPORARYFILE_H_INCLUDED +#define JUCE_TEMPORARYFILE_H_INCLUDED //============================================================================== @@ -88,7 +89,7 @@ public: 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, + TemporaryFile (const String& suffix = String(), int optionFlags = 0); /** Creates a temporary file in the same directory as a specified file. @@ -109,6 +110,16 @@ public: TemporaryFile (const File& targetFile, int optionFlags = 0); + /** Creates a temporary file using an explicit filename. + The other constructors are a better choice than this one, unless for some reason + you need to explicitly specify the temporary file you want to use. + + @param targetFile the file that you intend to overwrite + @param temporaryFile the temporary file to be used + */ + TemporaryFile (const File& targetFile, + const File& temporaryFile); + /** Destructor. When this object is deleted it will make sure that its temporary file is @@ -119,10 +130,10 @@ public: //============================================================================== /** Returns the temporary file. */ - const File& getFile() const { return temporaryFile; } + const File& getFile() const noexcept { return temporaryFile; } /** Returns the target file that was specified in the constructor. */ - const File& getTargetFile() const { return targetFile; } + const File& getTargetFile() const noexcept { return targetFile; } /** Tries to move the temporary file to overwrite the target file that was specified in the constructor. @@ -150,11 +161,9 @@ public: private: //============================================================================== - File temporaryFile, targetFile; + const File temporaryFile, targetFile; - void createTempFile (const File& parentDirectory, String name, const String& suffix, int optionFlags); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryFile); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TemporaryFile) }; -#endif // __JUCE_TEMPORARYFILE_JUCEHEADER__ +#endif // JUCE_TEMPORARYFILE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_WildcardFileFilter.cpp b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp similarity index 50% rename from JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_WildcardFileFilter.cpp rename to JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp index 2962635..1fef455 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_WildcardFileFilter.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.cpp @@ -1,33 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ WildcardFileFilter::WildcardFileFilter (const String& fileWildcardPatterns, const String& directoryWildcardPatterns, - const String& description_) - : FileFilter (description_.isEmpty() ? fileWildcardPatterns - : (description_ + " (" + fileWildcardPatterns + ")")) + const String& desc) + : FileFilter (desc.isEmpty() ? fileWildcardPatterns + : (desc + " (" + fileWildcardPatterns + ")")) { parse (fileWildcardPatterns, fileWildcards); parse (directoryWildcardPatterns, directoryWildcards); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_WildcardFileFilter.h b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h similarity index 59% rename from JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_WildcardFileFilter.h rename to JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h index 8f8de50..166ae4a 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_WildcardFileFilter.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_WildcardFileFilter.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ -#define __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ - -#include "juce_FileFilter.h" +#ifndef JUCE_WILDCARDFILEFILTER_H_INCLUDED +#define JUCE_WILDCARDFILEFILTER_H_INCLUDED //============================================================================== @@ -77,9 +78,9 @@ private: static void parse (const String& pattern, StringArray& result); static bool match (const File& file, const StringArray& wildcards); - JUCE_LEAK_DETECTOR (WildcardFileFilter); + JUCE_LEAK_DETECTOR (WildcardFileFilter) }; -#endif // __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ +#endif // JUCE_WILDCARDFILEFILTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/json/juce_JSON.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp similarity index 71% rename from JuceLibraryCode/modules/juce_core/json/juce_JSON.cpp rename to JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp index e805c3a..5a0f7f7 100644 --- a/JuceLibraryCode/modules/juce_core/json/juce_JSON.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -26,6 +29,77 @@ class JSONParser { public: + static Result parseObjectOrArray (String::CharPointerType t, var& result) + { + t = t.findEndOfWhitespace(); + + switch (t.getAndAdvance()) + { + case 0: result = var(); return Result::ok(); + case '{': return parseObject (t, result); + case '[': return parseArray (t, result); + } + + return createFail ("Expected '{' or '['", &t); + } + + static Result parseString (const juce_wchar quoteChar, String::CharPointerType& t, var& result) + { + MemoryOutputStream buffer (256); + + for (;;) + { + juce_wchar c = t.getAndAdvance(); + + if (c == quoteChar) + break; + + if (c == '\\') + { + c = t.getAndAdvance(); + + switch (c) + { + case '"': + case '\'': + case '\\': + case '/': break; + + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + + case 'u': + { + c = 0; + + for (int i = 4; --i >= 0;) + { + const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); + if (digitValue < 0) + return createFail ("Syntax error in unicode escape sequence"); + + c = (juce_wchar) ((c << 4) + digitValue); + } + + break; + } + } + } + + if (c == 0) + return createFail ("Unexpected end-of-input in string constant"); + + buffer.appendUTF8Char (c); + } + + result = buffer.toUTF8(); + return Result::ok(); + } + static Result parseAny (String::CharPointerType& t, var& result) { t = t.findEndOfWhitespace(); @@ -34,8 +108,9 @@ public: switch (t2.getAndAdvance()) { case '{': t = t2; return parseObject (t, result); - case '[': t = t2; return parseArray (t, result); - case '"': t = t2; return parseString (t, result); + case '[': t = t2; return parseArray (t, result); + case '"': t = t2; return parseString ('"', t, result); + case '\'': t = t2; return parseString ('\'', t, result); case '-': t2 = t2.findEndOfWhitespace(); @@ -72,7 +147,7 @@ public: if (t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 'l') { t = t2; - result = var::null; + result = var(); return Result::ok(); } break; @@ -163,14 +238,14 @@ private: if (c == '"') { var propertyNameVar; - Result r (parseString (t, propertyNameVar)); + Result r (parseString ('"', t, propertyNameVar)); if (r.failed()) return r; - const String propertyName (propertyNameVar.toString()); + const Identifier propertyName (propertyNameVar.toString()); - if (propertyName.isNotEmpty()) + if (propertyName.isValid()) { t = t.findEndOfWhitespace(); oldT = t; @@ -179,7 +254,7 @@ private: if (c2 != ':') return createFail ("Expected ':', but found", &oldT); - resultProperties.set (propertyName, var::null); + resultProperties.set (propertyName, var()); var* propertyValue = resultProperties.getVarPointer (propertyName); Result r2 (parseAny (t, *propertyValue)); @@ -194,7 +269,8 @@ private: if (nextChar == ',') continue; - else if (nextChar == '}') + + if (nextChar == '}') break; } } @@ -224,7 +300,7 @@ private: return createFail ("Unexpected end-of-input in array declaration"); t = oldT; - destArray->add (var::null); + destArray->add (var()); Result r (parseAny (t, destArray->getReference (destArray->size() - 1))); if (r.failed()) @@ -237,7 +313,8 @@ private: if (nextChar == ',') continue; - else if (nextChar == ']') + + if (nextChar == ']') break; return createFail ("Expected object array item, but found", &oldT); @@ -245,63 +322,6 @@ private: return Result::ok(); } - - static Result parseString (String::CharPointerType& t, var& result) - { - Array buffer; - buffer.ensureStorageAllocated (256); - - for (;;) - { - juce_wchar c = t.getAndAdvance(); - - if (c == '"') - break; - - if (c == '\\') - { - c = t.getAndAdvance(); - - switch (c) - { - case '"': - case '\\': - case '/': break; - - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - - case 'u': - { - c = 0; - - for (int i = 4; --i >= 0;) - { - const int digitValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); - if (digitValue < 0) - return createFail ("Syntax error in unicode escape sequence"); - - c = (juce_wchar) ((c << 4) + digitValue); - } - - break; - } - } - } - - if (c == 0) - return createFail ("Unexpected end-of-input in string constant"); - - buffer.add (c); - } - - buffer.add (0); - result = String (CharPointer_UTF32 (buffer.getRawDataPointer())); - return Result::ok(); - } }; //============================================================================== @@ -313,12 +333,18 @@ public: { if (v.isString()) { + out << '"'; writeString (out, v.toString().getCharPointer()); + out << '"'; } else if (v.isVoid()) { out << "null"; } + else if (v.isUndefined()) + { + out << "undefined"; + } else if (v.isBool()) { out << (static_cast (v) ? "true" : "false"); @@ -329,23 +355,20 @@ public: } else if (v.isObject()) { - DynamicObject* const object = v.getDynamicObject(); - - jassert (object != nullptr); // Only DynamicObjects can be converted to JSON! - - writeObject (out, *object, indentLevel, allOnOneLine); + if (DynamicObject* object = v.getDynamicObject()) + object->writeAsJSON (out, indentLevel, allOnOneLine); + else + jassertfalse; // Only DynamicObjects can be converted to JSON! } else { - jassert (! v.isMethod()); // Can't convert an object with methods to JSON! + // Can't convert these other types of object to JSON! + jassert (! (v.isMethod() || v.isBinaryData())); out << v.toString(); } } -private: - enum { indentSize = 2 }; - static void writeEscapedChar (OutputStream& out, const unsigned short value) { out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4); @@ -353,18 +376,17 @@ private: static void writeString (OutputStream& out, String::CharPointerType t) { - out << '"'; - for (;;) { const juce_wchar c (t.getAndAdvance()); switch (c) { - case 0: out << '"'; return; + case 0: return; case '\"': out << "\\\""; break; case '\\': out << "\\\\"; break; + case '\a': out << "\\a"; break; case '\b': out << "\\b"; break; case '\f': out << "\\f"; break; case '\t': out << "\\t"; break; @@ -400,92 +422,64 @@ private: static void writeSpaces (OutputStream& out, int numSpaces) { - out.writeRepeatedByte (' ', numSpaces); + out.writeRepeatedByte (' ', (size_t) numSpaces); } static void writeArray (OutputStream& out, const Array& array, const int indentLevel, const bool allOnOneLine) { out << '['; - if (! allOnOneLine) - out << newLine; - for (int i = 0; i < array.size(); ++i) + if (array.size() > 0) { if (! allOnOneLine) - writeSpaces (out, indentLevel + indentSize); - - write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine); - - if (i < array.size() - 1) - { - if (allOnOneLine) - out << ", "; - else - out << ',' << newLine; - } - else if (! allOnOneLine) out << newLine; - } - if (! allOnOneLine) - writeSpaces (out, indentLevel); + for (int i = 0; i < array.size(); ++i) + { + if (! allOnOneLine) + writeSpaces (out, indentLevel + indentSize); + + write (out, array.getReference(i), indentLevel + indentSize, allOnOneLine); + + if (i < array.size() - 1) + { + if (allOnOneLine) + out << ", "; + else + out << ',' << newLine; + } + else if (! allOnOneLine) + out << newLine; + } + + if (! allOnOneLine) + writeSpaces (out, indentLevel); + } out << ']'; } - static void writeObject (OutputStream& out, DynamicObject& object, - const int indentLevel, const bool allOnOneLine) - { - NamedValueSet& props = object.getProperties(); - - out << '{'; - if (! allOnOneLine) - out << newLine; - - LinkedListPointer* i = &(props.values); - - for (;;) - { - NamedValueSet::NamedValue* const v = i->get(); - - if (v == nullptr) - break; - - if (! allOnOneLine) - writeSpaces (out, indentLevel + indentSize); - - writeString (out, v->name); - out << ": "; - write (out, v->value, indentLevel + indentSize, allOnOneLine); - - if (v->nextListItem.get() != nullptr) - { - if (allOnOneLine) - out << ", "; - else - out << ',' << newLine; - } - else if (! allOnOneLine) - out << newLine; - - i = &(v->nextListItem); - } - - if (! allOnOneLine) - writeSpaces (out, indentLevel); - - out << '}'; - } + enum { indentSize = 2 }; }; //============================================================================== var JSON::parse (const String& text) { var result; - String::CharPointerType t (text.getCharPointer()); - if (! JSONParser::parseAny (t, result)) - result = var::null; + + if (! parse (text, result)) + result = var(); + + return result; +} + +var JSON::fromString (StringRef text) +{ + var result; + + if (! JSONParser::parseAny (text.text, result)) + result = var(); return result; } @@ -502,15 +496,14 @@ var JSON::parse (const File& file) Result JSON::parse (const String& text, var& result) { - String::CharPointerType t (text.getCharPointer()); - return JSONParser::parseAny (t, result); + return JSONParser::parseObjectOrArray (text.getCharPointer(), result); } String JSON::toString (const var& data, const bool allOnOneLine) { MemoryOutputStream mo (1024); JSONFormatter::write (mo, data, 0, allOnOneLine); - return mo.toString(); + return mo.toUTF8(); } void JSON::writeToStream (OutputStream& output, const var& data, const bool allOnOneLine) @@ -518,6 +511,23 @@ void JSON::writeToStream (OutputStream& output, const var& data, const bool allO JSONFormatter::write (output, data, 0, allOnOneLine); } +String JSON::escapeString (StringRef s) +{ + MemoryOutputStream mo; + JSONFormatter::writeString (mo, s.text); + return mo.toString(); +} + +Result JSON::parseQuotedString (String::CharPointerType& t, var& result) +{ + const juce_wchar quote = t.getAndAdvance(); + + if (quote == '"' || quote == '\'') + return JSONParser::parseString (quote, t, result); + + return Result::fail ("Not a quoted string!"); +} + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -565,7 +575,7 @@ public: { switch (r.nextInt (depth > 3 ? 6 : 8)) { - case 0: return var::null; + case 0: return var(); case 1: return r.nextInt(); case 2: return r.nextInt64(); case 3: return r.nextBool(); @@ -593,25 +603,24 @@ public: } default: - return var::null; + return var(); } } void runTest() { beginTest ("JSON"); - Random r; - r.setSeedRandomly(); + Random r = getRandom(); expect (JSON::parse (String::empty) == var::null); expect (JSON::parse ("{}").isObject()); expect (JSON::parse ("[]").isArray()); - expect (JSON::parse ("1234").isInt()); - expect (JSON::parse ("12345678901234").isInt64()); - expect (JSON::parse ("1.123e3").isDouble()); - expect (JSON::parse ("-1234").isInt()); - expect (JSON::parse ("-12345678901234").isInt64()); - expect (JSON::parse ("-1.123e3").isDouble()); + expect (JSON::parse ("[ 1234 ]")[0].isInt()); + expect (JSON::parse ("[ 12345678901234 ]")[0].isInt64()); + expect (JSON::parse ("[ 1.123e3 ]")[0].isDouble()); + expect (JSON::parse ("[ -1234]")[0].isInt()); + expect (JSON::parse ("[-12345678901234]")[0].isInt64()); + expect (JSON::parse ("[-1.123e3]")[0].isDouble()); for (int i = 100; --i >= 0;) { @@ -622,7 +631,7 @@ public: const bool oneLine = r.nextBool(); String asString (JSON::toString (v, oneLine)); - var parsed = JSON::parse (asString); + var parsed = JSON::parse ("[" + asString + "]")[0]; String parsedString (JSON::toString (parsed, oneLine)); expect (asString.isNotEmpty() && parsedString == asString); } diff --git a/JuceLibraryCode/modules/juce_core/json/juce_JSON.h b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h similarity index 55% rename from JuceLibraryCode/modules/juce_core/json/juce_JSON.h rename to JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h index 9ad90e4..7fb17ee 100644 --- a/JuceLibraryCode/modules/juce_core/json/juce_JSON.h +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.h @@ -1,36 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_JSON_JUCEHEADER__ -#define __JUCE_JSON_JUCEHEADER__ - -#include "../misc/juce_Result.h" -#include "../containers/juce_Variant.h" -class InputStream; -class OutputStream; -class File; +#ifndef JUCE_JSON_H_INCLUDED +#define JUCE_JSON_H_INCLUDED //============================================================================== @@ -43,7 +40,7 @@ class File; @see var */ -class JSON +class JUCE_API JSON { public: //============================================================================== @@ -56,6 +53,10 @@ public: If you're not interested in the error message, you can use one of the other shortcut parse methods, which simply return a var::null if the parsing fails. + + Note that this will only parse valid JSON, which means that the item given must + be either an object or an array definition. If you want to also be able to parse + any kind of primitive JSON object, use the fromString() method. */ static Result parse (const String& text, var& parsedResult); @@ -63,6 +64,10 @@ public: If the parsing fails, this simply returns var::null - if you need to find out more detail about the parse error, use the alternative parse() method which returns a Result. + + Note that this will only parse valid JSON, which means that the item given must + be either an object or an array definition. If you want to also be able to parse + any kind of primitive JSON object, use the fromString() method. */ static var parse (const String& text); @@ -97,6 +102,13 @@ public: static String toString (const var& objectToFormat, bool allOnOneLine = false); + /** Parses a string that was created with the toString() method. + This is slightly different to the parse() methods because they will reject primitive + values and only accept array or object definitions, whereas this method will handle + either. + */ + static var fromString (StringRef); + /** Writes a JSON-formatted representation of the var object to the given stream. If allOnOneLine is true, the result will be compacted into a single line of text with no carriage-returns. If false, it will be laid-out in a more human-readable format. @@ -106,10 +118,19 @@ public: const var& objectToFormat, bool allOnOneLine = false); + /** Returns a version of a string with any extended characters escaped. */ + static String escapeString (StringRef); + + /** Parses a quoted string-literal in JSON format, returning the un-escaped result in the + result parameter, and an error message in case the content was illegal. + This advances the text parameter, leaving it positioned after the closing quote. + */ + static Result parseQuotedString (String::CharPointerType& text, var& result); + private: //============================================================================== - JSON(); // This class can't be instantiated - just use its static methods. + JSON() JUCE_DELETED_FUNCTION; // This class can't be instantiated - just use its static methods. }; -#endif // __JUCE_JSON_JUCEHEADER__ +#endif // JUCE_JSON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp new file mode 100644 index 0000000..16641d6 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp @@ -0,0 +1,1710 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#define JUCE_JS_OPERATORS(X) \ + X(semicolon, ";") X(dot, ".") X(comma, ",") \ + X(openParen, "(") X(closeParen, ")") X(openBrace, "{") X(closeBrace, "}") \ + X(openBracket, "[") X(closeBracket, "]") X(colon, ":") X(question, "?") \ + X(typeEquals, "===") X(equals, "==") X(assign, "=") \ + X(typeNotEquals, "!==") X(notEquals, "!=") X(logicalNot, "!") \ + X(plusEquals, "+=") X(plusplus, "++") X(plus, "+") \ + X(minusEquals, "-=") X(minusminus, "--") X(minus, "-") \ + X(timesEquals, "*=") X(times, "*") X(divideEquals, "/=") X(divide, "/") \ + X(moduloEquals, "%=") X(modulo, "%") X(xorEquals, "^=") X(bitwiseXor, "^") \ + X(andEquals, "&=") X(logicalAnd, "&&") X(bitwiseAnd, "&") \ + X(orEquals, "|=") X(logicalOr, "||") X(bitwiseOr, "|") \ + X(leftShiftEquals, "<<=") X(lessThanOrEqual, "<=") X(leftShift, "<<") X(lessThan, "<") \ + X(rightShiftUnsigned, ">>>") X(rightShiftEquals, ">>=") X(rightShift, ">>") X(greaterThanOrEqual, ">=") X(greaterThan, ">") + +#define JUCE_JS_KEYWORDS(X) \ + X(var, "var") X(if_, "if") X(else_, "else") X(do_, "do") X(null_, "null") \ + X(while_, "while") X(for_, "for") X(break_, "break") X(continue_, "continue") X(undefined, "undefined") \ + X(function, "function") X(return_, "return") X(true_, "true") X(false_, "false") X(new_, "new") + +namespace TokenTypes +{ + #define JUCE_DECLARE_JS_TOKEN(name, str) static const char* const name = str; + JUCE_JS_KEYWORDS (JUCE_DECLARE_JS_TOKEN) + JUCE_JS_OPERATORS (JUCE_DECLARE_JS_TOKEN) + JUCE_DECLARE_JS_TOKEN (eof, "$eof") + JUCE_DECLARE_JS_TOKEN (literal, "$literal") + JUCE_DECLARE_JS_TOKEN (identifier, "$identifier") +} + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4702) +#endif + +//============================================================================== +struct JavascriptEngine::RootObject : public DynamicObject +{ + RootObject() + { + setMethod ("exec", exec); + setMethod ("eval", eval); + setMethod ("trace", trace); + setMethod ("charToInt", charToInt); + setMethod ("parseInt", IntegerClass::parseInt); + } + + Time timeout; + + typedef const var::NativeFunctionArgs& Args; + typedef const char* TokenType; + + void execute (const String& code) + { + ExpressionTreeBuilder tb (code); + ScopedPointer (tb.parseStatementList())->perform (Scope (nullptr, this, this), nullptr); + } + + var evaluate (const String& code) + { + ExpressionTreeBuilder tb (code); + return ExpPtr (tb.parseExpression())->getResult (Scope (nullptr, this, this)); + } + + //============================================================================== + static bool areTypeEqual (const var& a, const var& b) + { + return a.hasSameTypeAs (b) && isFunction (a) == isFunction (b) + && (((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) || a == b); + } + + static String getTokenName (TokenType t) { return t[0] == '$' ? String (t + 1) : ("'" + String (t) + "'"); } + static bool isFunction (const var& v) { return dynamic_cast (v.getObject()) != nullptr; } + static bool isNumericOrUndefined (const var& v) { return v.isInt() || v.isDouble() || v.isInt64() || v.isBool() || v.isUndefined(); } + static int64 getOctalValue (const String& s) { BigInteger b; b.parseString (s, 8); return b.toInt64(); } + static Identifier getPrototypeIdentifier() { static const Identifier i ("prototype"); return i; } + + //============================================================================== + struct CodeLocation + { + CodeLocation (const String& code) noexcept : program (code), location (program.getCharPointer()) {} + CodeLocation (const CodeLocation& other) noexcept : program (other.program), location (other.location) {} + + void throwError (const String& message) const + { + int col = 1, line = 1; + + for (String::CharPointerType i (program.getCharPointer()); i < location && ! i.isEmpty(); ++i) + { + ++col; + if (*i == '\n') { col = 1; ++line; } + } + + throw "Line " + String (line) + ", column " + String (col) + " : " + message; + } + + String program; + String::CharPointerType location; + }; + + //============================================================================== + struct Scope + { + Scope (const Scope* p, RootObject* r, DynamicObject* s) noexcept : parent (p), root (r), scope (s) {} + + const Scope* parent; + ReferenceCountedObjectPtr root; + DynamicObject::Ptr scope; + + var findFunctionCall (const CodeLocation& location, const var& targetObject, Identifier functionName) const + { + if (DynamicObject* o = targetObject.getDynamicObject()) + { + if (var* prop = o->getProperties().getVarPointer (functionName)) + return *prop; + + for (DynamicObject* p = o->getProperty (getPrototypeIdentifier()).getDynamicObject(); p != nullptr; + p = p->getProperty (getPrototypeIdentifier()).getDynamicObject()) + { + if (var* prop = p->getProperties().getVarPointer (functionName)) + return *prop; + } + } + + if (targetObject.isString()) + if (var* m = findRootClassProperty (StringClass::getClassName(), functionName)) + return *m; + + if (targetObject.isArray()) + if (var* m = findRootClassProperty (ArrayClass::getClassName(), functionName)) + return *m; + + if (var* m = findRootClassProperty (ObjectClass::getClassName(), functionName)) + return *m; + + location.throwError ("Unknown function '" + functionName.toString() + "'"); + return var(); + } + + var* findRootClassProperty (Identifier className, Identifier propName) const + { + if (DynamicObject* cls = root->getProperty (className).getDynamicObject()) + return cls->getProperties().getVarPointer (propName); + + return nullptr; + } + + var findSymbolInParentScopes (Identifier name) const + { + if (var* v = scope->getProperties().getVarPointer (name)) + return *v; + + return parent != nullptr ? parent->findSymbolInParentScopes (name) + : var::undefined(); + } + + bool findAndInvokeMethod (Identifier function, const var::NativeFunctionArgs& args, var& result) const + { + const NamedValueSet& props = scope->getProperties(); + + DynamicObject* target = args.thisObject.getDynamicObject(); + + if (target == nullptr || target == scope) + { + if (const var* m = props.getVarPointer (function)) + { + if (FunctionObject* fo = dynamic_cast (m->getObject())) + { + result = fo->invoke (*this, args); + return true; + } + } + } + + for (int i = 0; i < props.size(); ++i) + if (DynamicObject* o = props.getValueAt (i).getDynamicObject()) + if (Scope (this, root, o).findAndInvokeMethod (function, args, result)) + return true; + + return false; + } + + void checkTimeOut (const CodeLocation& location) const + { + if (Time::getCurrentTime() > root->timeout) + location.throwError ("Execution timed-out"); + } + }; + + //============================================================================== + struct Statement + { + Statement (const CodeLocation& l) noexcept : location (l) {} + virtual ~Statement() {} + + enum ResultCode { ok = 0, returnWasHit, breakWasHit, continueWasHit }; + virtual ResultCode perform (const Scope&, var*) const { return ok; } + + CodeLocation location; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Statement) + }; + + struct Expression : public Statement + { + Expression (const CodeLocation& l) noexcept : Statement (l) {} + + virtual var getResult (const Scope&) const { return var::undefined(); } + virtual void assign (const Scope&, const var&) const { location.throwError ("Cannot assign to this expression!"); } + + ResultCode perform (const Scope& s, var*) const override { getResult (s); return ok; } + }; + + typedef ScopedPointer ExpPtr; + + struct BlockStatement : public Statement + { + BlockStatement (const CodeLocation& l) noexcept : Statement (l) {} + + ResultCode perform (const Scope& s, var* returnedValue) const override + { + for (int i = 0; i < statements.size(); ++i) + if (ResultCode r = statements.getUnchecked(i)->perform (s, returnedValue)) + return r; + + return ok; + } + + OwnedArray statements; + }; + + struct IfStatement : public Statement + { + IfStatement (const CodeLocation& l) noexcept : Statement (l) {} + + ResultCode perform (const Scope& s, var* returnedValue) const override + { + return (condition->getResult(s) ? trueBranch : falseBranch)->perform (s, returnedValue); + } + + ExpPtr condition; + ScopedPointer trueBranch, falseBranch; + }; + + struct VarStatement : public Statement + { + VarStatement (const CodeLocation& l) noexcept : Statement (l) {} + + ResultCode perform (const Scope& s, var*) const override + { + s.scope->setProperty (name, initialiser->getResult (s)); + return ok; + } + + Identifier name; + ExpPtr initialiser; + }; + + struct LoopStatement : public Statement + { + LoopStatement (const CodeLocation& l, bool isDo) noexcept : Statement (l), isDoLoop (isDo) {} + + ResultCode perform (const Scope& s, var* returnedValue) const override + { + initialiser->perform (s, nullptr); + + while (isDoLoop || condition->getResult (s)) + { + s.checkTimeOut (location); + ResultCode r = body->perform (s, returnedValue); + + if (r == returnWasHit) return r; + if (r == breakWasHit) break; + + iterator->perform (s, nullptr); + + if (isDoLoop && r != continueWasHit && ! condition->getResult (s)) + break; + } + + return ok; + } + + ScopedPointer initialiser, iterator, body; + ExpPtr condition; + bool isDoLoop; + }; + + struct ReturnStatement : public Statement + { + ReturnStatement (const CodeLocation& l, Expression* v) noexcept : Statement (l), returnValue (v) {} + + ResultCode perform (const Scope& s, var* ret) const override + { + if (ret != nullptr) *ret = returnValue->getResult (s); + return returnWasHit; + } + + ExpPtr returnValue; + }; + + struct BreakStatement : public Statement + { + BreakStatement (const CodeLocation& l) noexcept : Statement (l) {} + ResultCode perform (const Scope&, var*) const override { return breakWasHit; } + }; + + struct ContinueStatement : public Statement + { + ContinueStatement (const CodeLocation& l) noexcept : Statement (l) {} + ResultCode perform (const Scope&, var*) const override { return continueWasHit; } + }; + + struct LiteralValue : public Expression + { + LiteralValue (const CodeLocation& l, const var& v) noexcept : Expression (l), value (v) {} + var getResult (const Scope&) const override { return value; } + var value; + }; + + struct UnqualifiedName : public Expression + { + UnqualifiedName (const CodeLocation& l, Identifier n) noexcept : Expression (l), name (n) {} + + var getResult (const Scope& s) const override { return s.findSymbolInParentScopes (name); } + + void assign (const Scope& s, const var& newValue) const override + { + if (var* v = s.scope->getProperties().getVarPointer (name)) + *v = newValue; + else + s.root->setProperty (name, newValue); + } + + Identifier name; + }; + + struct DotOperator : public Expression + { + DotOperator (const CodeLocation& l, ExpPtr& p, Identifier c) noexcept : Expression (l), parent (p), child (c) {} + + var getResult (const Scope& s) const override + { + var p (parent->getResult (s)); + static const Identifier lengthID ("length"); + + if (child == lengthID) + { + if (Array* array = p.getArray()) return array->size(); + if (p.isString()) return p.toString().length(); + } + + if (DynamicObject* o = p.getDynamicObject()) + if (var* v = o->getProperties().getVarPointer (child)) + return *v; + + return var::undefined(); + } + + void assign (const Scope& s, const var& newValue) const override + { + if (DynamicObject* o = parent->getResult (s).getDynamicObject()) + o->setProperty (child, newValue); + else + Expression::assign (s, newValue); + } + + ExpPtr parent; + Identifier child; + }; + + struct ArraySubscript : public Expression + { + ArraySubscript (const CodeLocation& l) noexcept : Expression (l) {} + + var getResult (const Scope& s) const override + { + if (const Array* array = object->getResult (s).getArray()) + return (*array) [static_cast (index->getResult (s))]; + + return var::undefined(); + } + + void assign (const Scope& s, const var& newValue) const override + { + if (Array* array = object->getResult (s).getArray()) + { + const int i = index->getResult (s); + while (array->size() < i) + array->add (var::undefined()); + + array->set (i, newValue); + return; + } + + Expression::assign (s, newValue); + } + + ExpPtr object, index; + }; + + struct BinaryOperatorBase : public Expression + { + BinaryOperatorBase (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept + : Expression (l), lhs (a), rhs (b), operation (op) {} + + ExpPtr lhs, rhs; + TokenType operation; + }; + + struct BinaryOperator : public BinaryOperatorBase + { + BinaryOperator (const CodeLocation& l, ExpPtr& a, ExpPtr& b, TokenType op) noexcept + : BinaryOperatorBase (l, a, b, op) {} + + virtual var getWithUndefinedArg() const { return var::undefined(); } + virtual var getWithDoubles (double, double) const { return throwError ("Double"); } + virtual var getWithInts (int64, int64) const { return throwError ("Integer"); } + virtual var getWithArrayOrObject (const var& a, const var&) const { return throwError (a.isArray() ? "Array" : "Object"); } + virtual var getWithStrings (const String&, const String&) const { return throwError ("String"); } + + var getResult (const Scope& s) const override + { + var a (lhs->getResult (s)), b (rhs->getResult (s)); + + if ((a.isUndefined() || a.isVoid()) && (b.isUndefined() || b.isVoid())) + return getWithUndefinedArg(); + + if (isNumericOrUndefined (a) && isNumericOrUndefined (b)) + return (a.isDouble() || b.isDouble()) ? getWithDoubles (a, b) : getWithInts (a, b); + + if (a.isArray() || a.isObject()) + return getWithArrayOrObject (a, b); + + return getWithStrings (a.toString(), b.toString()); + } + + var throwError (const char* typeName) const + { location.throwError (getTokenName (operation) + " is not allowed on the " + typeName + " type"); return var(); } + }; + + struct EqualsOp : public BinaryOperator + { + EqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::equals) {} + var getWithUndefinedArg() const override { return true; } + var getWithDoubles (double a, double b) const override { return a == b; } + var getWithInts (int64 a, int64 b) const override { return a == b; } + var getWithStrings (const String& a, const String& b) const override { return a == b; } + var getWithArrayOrObject (const var& a, const var& b) const override { return a == b; } + }; + + struct NotEqualsOp : public BinaryOperator + { + NotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::notEquals) {} + var getWithUndefinedArg() const override { return false; } + var getWithDoubles (double a, double b) const override { return a != b; } + var getWithInts (int64 a, int64 b) const override { return a != b; } + var getWithStrings (const String& a, const String& b) const override { return a != b; } + var getWithArrayOrObject (const var& a, const var& b) const override { return a != b; } + }; + + struct LessThanOp : public BinaryOperator + { + LessThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThan) {} + var getWithDoubles (double a, double b) const override { return a < b; } + var getWithInts (int64 a, int64 b) const override { return a < b; } + var getWithStrings (const String& a, const String& b) const override { return a < b; } + }; + + struct LessThanOrEqualOp : public BinaryOperator + { + LessThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::lessThanOrEqual) {} + var getWithDoubles (double a, double b) const override { return a <= b; } + var getWithInts (int64 a, int64 b) const override { return a <= b; } + var getWithStrings (const String& a, const String& b) const override { return a <= b; } + }; + + struct GreaterThanOp : public BinaryOperator + { + GreaterThanOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThan) {} + var getWithDoubles (double a, double b) const override { return a > b; } + var getWithInts (int64 a, int64 b) const override { return a > b; } + var getWithStrings (const String& a, const String& b) const override { return a > b; } + }; + + struct GreaterThanOrEqualOp : public BinaryOperator + { + GreaterThanOrEqualOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::greaterThanOrEqual) {} + var getWithDoubles (double a, double b) const override { return a >= b; } + var getWithInts (int64 a, int64 b) const override { return a >= b; } + var getWithStrings (const String& a, const String& b) const override { return a >= b; } + }; + + struct AdditionOp : public BinaryOperator + { + AdditionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::plus) {} + var getWithDoubles (double a, double b) const override { return a + b; } + var getWithInts (int64 a, int64 b) const override { return a + b; } + var getWithStrings (const String& a, const String& b) const override { return a + b; } + }; + + struct SubtractionOp : public BinaryOperator + { + SubtractionOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::minus) {} + var getWithDoubles (double a, double b) const override { return a - b; } + var getWithInts (int64 a, int64 b) const override { return a - b; } + }; + + struct MultiplyOp : public BinaryOperator + { + MultiplyOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::times) {} + var getWithDoubles (double a, double b) const override { return a * b; } + var getWithInts (int64 a, int64 b) const override { return a * b; } + }; + + struct DivideOp : public BinaryOperator + { + DivideOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::divide) {} + var getWithDoubles (double a, double b) const override { return a / b; } + var getWithInts (int64 a, int64 b) const override { return a / b; } + }; + + struct ModuloOp : public BinaryOperator + { + ModuloOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::modulo) {} + var getWithInts (int64 a, int64 b) const override { return a % b; } + }; + + struct BitwiseOrOp : public BinaryOperator + { + BitwiseOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseOr) {} + var getWithInts (int64 a, int64 b) const override { return a | b; } + }; + + struct BitwiseAndOp : public BinaryOperator + { + BitwiseAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseAnd) {} + var getWithInts (int64 a, int64 b) const override { return a & b; } + }; + + struct BitwiseXorOp : public BinaryOperator + { + BitwiseXorOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::bitwiseXor) {} + var getWithInts (int64 a, int64 b) const override { return a ^ b; } + }; + + struct LeftShiftOp : public BinaryOperator + { + LeftShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::leftShift) {} + var getWithInts (int64 a, int64 b) const override { return ((int) a) << (int) b; } + }; + + struct RightShiftOp : public BinaryOperator + { + RightShiftOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShift) {} + var getWithInts (int64 a, int64 b) const override { return ((int) a) >> (int) b; } + }; + + struct RightShiftUnsignedOp : public BinaryOperator + { + RightShiftUnsignedOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperator (l, a, b, TokenTypes::rightShiftUnsigned) {} + var getWithInts (int64 a, int64 b) const override { return (int) (((uint32) a) >> (int) b); } + }; + + struct LogicalAndOp : public BinaryOperatorBase + { + LogicalAndOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalAnd) {} + var getResult (const Scope& s) const override { return lhs->getResult (s) && rhs->getResult (s); } + }; + + struct LogicalOrOp : public BinaryOperatorBase + { + LogicalOrOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::logicalOr) {} + var getResult (const Scope& s) const override { return lhs->getResult (s) || rhs->getResult (s); } + }; + + struct TypeEqualsOp : public BinaryOperatorBase + { + TypeEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeEquals) {} + var getResult (const Scope& s) const override { return areTypeEqual (lhs->getResult (s), rhs->getResult (s)); } + }; + + struct TypeNotEqualsOp : public BinaryOperatorBase + { + TypeNotEqualsOp (const CodeLocation& l, ExpPtr& a, ExpPtr& b) noexcept : BinaryOperatorBase (l, a, b, TokenTypes::typeNotEquals) {} + var getResult (const Scope& s) const override { return ! areTypeEqual (lhs->getResult (s), rhs->getResult (s)); } + }; + + struct ConditionalOp : public Expression + { + ConditionalOp (const CodeLocation& l) noexcept : Expression (l) {} + + var getResult (const Scope& s) const override { return (condition->getResult (s) ? trueBranch : falseBranch)->getResult (s); } + void assign (const Scope& s, const var& v) const override { (condition->getResult (s) ? trueBranch : falseBranch)->assign (s, v); } + + ExpPtr condition, trueBranch, falseBranch; + }; + + struct Assignment : public Expression + { + Assignment (const CodeLocation& l, ExpPtr& dest, ExpPtr& source) noexcept : Expression (l), target (dest), newValue (source) {} + + var getResult (const Scope& s) const override + { + var value (newValue->getResult (s)); + target->assign (s, value); + return value; + } + + ExpPtr target, newValue; + }; + + struct SelfAssignment : public Expression + { + SelfAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept + : Expression (l), target (dest), newValue (source) {} + + var getResult (const Scope& s) const override + { + var value (newValue->getResult (s)); + target->assign (s, value); + return value; + } + + Expression* target; // Careful! this pointer aliases a sub-term of newValue! + ExpPtr newValue; + TokenType op; + }; + + struct PostAssignment : public SelfAssignment + { + PostAssignment (const CodeLocation& l, Expression* dest, Expression* source) noexcept : SelfAssignment (l, dest, source) {} + + var getResult (const Scope& s) const override + { + var oldValue (target->getResult (s)); + target->assign (s, newValue->getResult (s)); + return oldValue; + } + }; + + struct FunctionCall : public Expression + { + FunctionCall (const CodeLocation& l) noexcept : Expression (l) {} + + var getResult (const Scope& s) const override + { + if (DotOperator* dot = dynamic_cast (object.get())) + { + var thisObject (dot->parent->getResult (s)); + return invokeFunction (s, s.findFunctionCall (location, thisObject, dot->child), thisObject); + } + + var function (object->getResult (s)); + return invokeFunction (s, function, var (s.scope)); + } + + var invokeFunction (const Scope& s, const var& function, const var& thisObject) const + { + s.checkTimeOut (location); + + Array argVars; + for (int i = 0; i < arguments.size(); ++i) + argVars.add (arguments.getUnchecked(i)->getResult (s)); + + const var::NativeFunctionArgs args (thisObject, argVars.begin(), argVars.size()); + + if (var::NativeFunction nativeFunction = function.getNativeFunction()) + return nativeFunction (args); + + if (FunctionObject* fo = dynamic_cast (function.getObject())) + return fo->invoke (s, args); + + location.throwError ("This expression is not a function!"); return var(); + } + + ExpPtr object; + OwnedArray arguments; + }; + + struct NewOperator : public FunctionCall + { + NewOperator (const CodeLocation& l) noexcept : FunctionCall (l) {} + + var getResult (const Scope& s) const override + { + var classOrFunc = object->getResult (s); + + const bool isFunc = isFunction (classOrFunc); + if (! (isFunc || classOrFunc.getDynamicObject() != nullptr)) + return var::undefined(); + + DynamicObject::Ptr newObject (new DynamicObject()); + + if (isFunc) + invokeFunction (s, classOrFunc, newObject.get()); + else + newObject->setProperty (getPrototypeIdentifier(), classOrFunc); + + return newObject.get(); + } + }; + + struct ObjectDeclaration : public Expression + { + ObjectDeclaration (const CodeLocation& l) noexcept : Expression (l) {} + + var getResult (const Scope& s) const override + { + DynamicObject::Ptr newObject (new DynamicObject()); + + for (int i = 0; i < names.size(); ++i) + newObject->setProperty (names.getUnchecked(i), initialisers.getUnchecked(i)->getResult (s)); + + return newObject.get(); + } + + Array names; + OwnedArray initialisers; + }; + + struct ArrayDeclaration : public Expression + { + ArrayDeclaration (const CodeLocation& l) noexcept : Expression (l) {} + + var getResult (const Scope& s) const override + { + Array a; + + for (int i = 0; i < values.size(); ++i) + a.add (values.getUnchecked(i)->getResult (s)); + + return a; + } + + OwnedArray values; + }; + + //============================================================================== + struct FunctionObject : public DynamicObject + { + FunctionObject() noexcept {} + + FunctionObject (const FunctionObject& other) : DynamicObject(), functionCode (other.functionCode) + { + ExpressionTreeBuilder tb (functionCode); + tb.parseFunctionParamsAndBody (*this); + } + + DynamicObject::Ptr clone() override { return new FunctionObject (*this); } + + void writeAsJSON (OutputStream& out, int /*indentLevel*/, bool /*allOnOneLine*/) override + { + out << "function " << functionCode; + } + + var invoke (const Scope& s, const var::NativeFunctionArgs& args) const + { + DynamicObject::Ptr functionRoot (new DynamicObject()); + + static const Identifier thisIdent ("this"); + functionRoot->setProperty (thisIdent, args.thisObject); + + for (int i = 0; i < parameters.size(); ++i) + functionRoot->setProperty (parameters.getReference(i), + i < args.numArguments ? args.arguments[i] : var::undefined()); + + var result; + body->perform (Scope (&s, s.root, functionRoot), &result); + return result; + } + + String functionCode; + Array parameters; + ScopedPointer body; + }; + + //============================================================================== + struct TokenIterator + { + TokenIterator (const String& code) : location (code), p (code.getCharPointer()) { skip(); } + + void skip() + { + skipWhitespaceAndComments(); + location.location = p; + currentType = matchNextToken(); + } + + void match (TokenType expected) + { + if (currentType != expected) + location.throwError ("Found " + getTokenName (currentType) + " when expecting " + getTokenName (expected)); + + skip(); + } + + bool matchIf (TokenType expected) { if (currentType == expected) { skip(); return true; } return false; } + bool matchesAny (TokenType t1, TokenType t2) const { return currentType == t1 || currentType == t2; } + bool matchesAny (TokenType t1, TokenType t2, TokenType t3) const { return matchesAny (t1, t2) || currentType == t3; } + + CodeLocation location; + TokenType currentType; + var currentValue; + + private: + String::CharPointerType p; + + static bool isIdentifierStart (const juce_wchar c) noexcept { return CharacterFunctions::isLetter (c) || c == '_'; } + static bool isIdentifierBody (const juce_wchar c) noexcept { return CharacterFunctions::isLetterOrDigit (c) || c == '_'; } + + TokenType matchNextToken() + { + if (isIdentifierStart (*p)) + { + String::CharPointerType end (p); + while (isIdentifierBody (*++end)) {} + + const size_t len = end - p; + #define JUCE_JS_COMPARE_KEYWORD(name, str) if (len == sizeof (str) - 1 && matchToken (TokenTypes::name, len)) return TokenTypes::name; + JUCE_JS_KEYWORDS (JUCE_JS_COMPARE_KEYWORD) + + currentValue = String (p, end); p = end; + return TokenTypes::identifier; + } + + if (p.isDigit()) + { + if (parseHexLiteral() || parseFloatLiteral() || parseOctalLiteral() || parseDecimalLiteral()) + return TokenTypes::literal; + + location.throwError ("Syntax error in numeric constant"); + } + + if (parseStringLiteral (*p) || (*p == '.' && parseFloatLiteral())) + return TokenTypes::literal; + + #define JUCE_JS_COMPARE_OPERATOR(name, str) if (matchToken (TokenTypes::name, sizeof (str) - 1)) return TokenTypes::name; + JUCE_JS_OPERATORS (JUCE_JS_COMPARE_OPERATOR) + + if (! p.isEmpty()) + location.throwError ("Unexpected character '" + String::charToString (*p) + "' in source"); + + return TokenTypes::eof; + } + + bool matchToken (TokenType name, const size_t len) noexcept + { + if (p.compareUpTo (CharPointer_ASCII (name), (int) len) != 0) return false; + p += (int) len; return true; + } + + void skipWhitespaceAndComments() + { + for (;;) + { + p = p.findEndOfWhitespace(); + + if (*p == '/') + { + const juce_wchar c2 = p[1]; + + if (c2 == '/') { p = CharacterFunctions::find (p, (juce_wchar) '\n'); continue; } + + if (c2 == '*') + { + location.location = p; + p = CharacterFunctions::find (p + 2, CharPointer_ASCII ("*/")); + if (p.isEmpty()) location.throwError ("Unterminated '/*' comment"); + p += 2; continue; + } + } + + break; + } + } + + bool parseStringLiteral (juce_wchar quoteType) + { + if (quoteType != '"' && quoteType != '\'') + return false; + + Result r (JSON::parseQuotedString (p, currentValue)); + if (r.failed()) location.throwError (r.getErrorMessage()); + return true; + } + + bool parseHexLiteral() + { + if (*p != '0' || (p[1] != 'x' && p[1] != 'X')) return false; + + String::CharPointerType t (++p); + int64 v = CharacterFunctions::getHexDigitValue (*++t); + if (v < 0) return false; + + for (;;) + { + const int digit = CharacterFunctions::getHexDigitValue (*++t); + if (digit < 0) break; + v = v * 16 + digit; + } + + currentValue = v; p = t; + return true; + } + + bool parseFloatLiteral() + { + int numDigits = 0; + String::CharPointerType t (p); + while (t.isDigit()) { ++t; ++numDigits; } + + const bool hasPoint = (*t == '.'); + + if (hasPoint) + while ((++t).isDigit()) ++numDigits; + + if (numDigits == 0) + return false; + + juce_wchar c = *t; + const bool hasExponent = (c == 'e' || c == 'E'); + + if (hasExponent) + { + c = *++t; + if (c == '+' || c == '-') ++t; + if (! t.isDigit()) return false; + while ((++t).isDigit()) {} + } + + if (! (hasExponent || hasPoint)) return false; + + currentValue = CharacterFunctions::getDoubleValue (p); p = t; + return true; + } + + bool parseOctalLiteral() + { + String::CharPointerType t (p); + int64 v = *t - '0'; + if (v != 0) return false; // first digit of octal must be 0 + + for (;;) + { + const int digit = (int) (*++t - '0'); + if (isPositiveAndBelow (digit, 8)) v = v * 8 + digit; + else if (isPositiveAndBelow (digit, 10)) location.throwError ("Decimal digit in octal constant"); + else break; + } + + currentValue = v; p = t; + return true; + } + + bool parseDecimalLiteral() + { + int64 v = 0; + + for (;; ++p) + { + const int digit = (int) (*p - '0'); + if (isPositiveAndBelow (digit, 10)) v = v * 10 + digit; + else break; + } + + currentValue = v; + return true; + } + }; + + //============================================================================== + struct ExpressionTreeBuilder : private TokenIterator + { + ExpressionTreeBuilder (const String code) : TokenIterator (code) {} + + BlockStatement* parseStatementList() + { + ScopedPointer b (new BlockStatement (location)); + + while (currentType != TokenTypes::closeBrace && currentType != TokenTypes::eof) + b->statements.add (parseStatement()); + + return b.release(); + } + + void parseFunctionParamsAndBody (FunctionObject& fo) + { + match (TokenTypes::openParen); + + while (currentType != TokenTypes::closeParen) + { + fo.parameters.add (currentValue.toString()); + match (TokenTypes::identifier); + + if (currentType != TokenTypes::closeParen) + match (TokenTypes::comma); + } + + match (TokenTypes::closeParen); + fo.body = parseBlock(); + } + + Expression* parseExpression() + { + ExpPtr lhs (parseLogicOperator()); + + if (matchIf (TokenTypes::question)) return parseTerneryOperator (lhs); + if (matchIf (TokenTypes::assign)) { ExpPtr rhs (parseExpression()); return new Assignment (location, lhs, rhs); } + if (matchIf (TokenTypes::plusEquals)) return parseInPlaceOpExpression (lhs); + if (matchIf (TokenTypes::minusEquals)) return parseInPlaceOpExpression (lhs); + if (matchIf (TokenTypes::leftShiftEquals)) return parseInPlaceOpExpression (lhs); + if (matchIf (TokenTypes::rightShiftEquals)) return parseInPlaceOpExpression (lhs); + + return lhs.release(); + } + + private: + void throwError (const String& err) const { location.throwError (err); } + + template + Expression* parseInPlaceOpExpression (ExpPtr& lhs) + { + ExpPtr rhs (parseExpression()); + Expression* bareLHS = lhs; // careful - bare pointer is deliberately alised + return new SelfAssignment (location, bareLHS, new OpType (location, lhs, rhs)); + } + + BlockStatement* parseBlock() + { + match (TokenTypes::openBrace); + ScopedPointer b (parseStatementList()); + match (TokenTypes::closeBrace); + return b.release(); + } + + Statement* parseStatement() + { + if (currentType == TokenTypes::openBrace) return parseBlock(); + if (matchIf (TokenTypes::var)) return parseVar(); + if (matchIf (TokenTypes::if_)) return parseIf(); + if (matchIf (TokenTypes::while_)) return parseDoOrWhileLoop (false); + if (matchIf (TokenTypes::do_)) return parseDoOrWhileLoop (true); + if (matchIf (TokenTypes::for_)) return parseForLoop(); + if (matchIf (TokenTypes::return_)) return new ReturnStatement (location, matchIf (TokenTypes::semicolon) ? new Expression (location) : parseExpression()); + if (matchIf (TokenTypes::break_)) return new BreakStatement (location); + if (matchIf (TokenTypes::continue_)) return new ContinueStatement (location); + if (matchIf (TokenTypes::function)) return parseFunction(); + if (matchIf (TokenTypes::semicolon)) return new Statement (location); + if (matchIf (TokenTypes::plusplus)) return parsePreIncDec(); + if (matchIf (TokenTypes::minusminus)) return parsePreIncDec(); + + if (matchesAny (TokenTypes::openParen, TokenTypes::openBracket)) + return matchEndOfStatement (parseFactor()); + + if (matchesAny (TokenTypes::identifier, TokenTypes::literal, TokenTypes::minus)) + return matchEndOfStatement (parseExpression()); + + throwError ("Found " + getTokenName (currentType) + " when expecting a statement"); + return nullptr; + } + + Expression* matchEndOfStatement (Expression* ex) { ExpPtr e (ex); if (currentType != TokenTypes::eof) match (TokenTypes::semicolon); return e.release(); } + Expression* matchCloseParen (Expression* ex) { ExpPtr e (ex); match (TokenTypes::closeParen); return e.release(); } + + Statement* parseIf() + { + ScopedPointer s (new IfStatement (location)); + match (TokenTypes::openParen); + s->condition = parseExpression(); + match (TokenTypes::closeParen); + s->trueBranch = parseStatement(); + s->falseBranch = matchIf (TokenTypes::else_) ? parseStatement() : new Statement (location); + return s.release(); + } + + Statement* parseVar() + { + ScopedPointer s (new VarStatement (location)); + s->name = parseIdentifier(); + s->initialiser = matchIf (TokenTypes::assign) ? parseExpression() : new Expression (location); + + if (matchIf (TokenTypes::comma)) + { + ScopedPointer block (new BlockStatement (location)); + block->statements.add (s.release()); + block->statements.add (parseVar()); + return block.release(); + } + + match (TokenTypes::semicolon); + return s.release(); + } + + Statement* parseFunction() + { + Identifier name; + var fn = parseFunctionDefinition (name); + + if (name.isNull()) + throwError ("Functions defined at statement-level must have a name"); + + ExpPtr nm (new UnqualifiedName (location, name)), value (new LiteralValue (location, fn)); + return new Assignment (location, nm, value); + } + + Statement* parseForLoop() + { + ScopedPointer s (new LoopStatement (location, false)); + match (TokenTypes::openParen); + s->initialiser = parseStatement(); + + if (matchIf (TokenTypes::semicolon)) + s->condition = new LiteralValue (location, true); + else + { + s->condition = parseExpression(); + match (TokenTypes::semicolon); + } + + s->iterator = parseExpression(); + match (TokenTypes::closeParen); + s->body = parseStatement(); + return s.release(); + } + + Statement* parseDoOrWhileLoop (bool isDoLoop) + { + ScopedPointer s (new LoopStatement (location, isDoLoop)); + s->initialiser = new Statement (location); + s->iterator = new Statement (location); + + if (isDoLoop) + { + s->body = parseBlock(); + match (TokenTypes::while_); + } + + match (TokenTypes::openParen); + s->condition = parseExpression(); + match (TokenTypes::closeParen); + + if (! isDoLoop) + s->body = parseStatement(); + + return s.release(); + } + + Identifier parseIdentifier() + { + Identifier i; + if (currentType == TokenTypes::identifier) + i = currentValue.toString(); + + match (TokenTypes::identifier); + return i; + } + + var parseFunctionDefinition (Identifier& functionName) + { + const String::CharPointerType functionStart (location.location); + + if (currentType == TokenTypes::identifier) + functionName = parseIdentifier(); + + ScopedPointer fo (new FunctionObject()); + parseFunctionParamsAndBody (*fo); + fo->functionCode = String (functionStart, location.location); + return var (fo.release()); + } + + Expression* parseFunctionCall (FunctionCall* call, ExpPtr& function) + { + ScopedPointer s (call); + s->object = function; + match (TokenTypes::openParen); + + while (currentType != TokenTypes::closeParen) + { + s->arguments.add (parseExpression()); + if (currentType != TokenTypes::closeParen) + match (TokenTypes::comma); + } + + return matchCloseParen (s.release()); + } + + Expression* parseSuffixes (Expression* e) + { + ExpPtr input (e); + + if (matchIf (TokenTypes::dot)) + return parseSuffixes (new DotOperator (location, input, parseIdentifier())); + + if (currentType == TokenTypes::openParen) + return parseSuffixes (parseFunctionCall (new FunctionCall (location), input)); + + if (matchIf (TokenTypes::openBracket)) + { + ScopedPointer s (new ArraySubscript (location)); + s->object = input; + s->index = parseExpression(); + match (TokenTypes::closeBracket); + return parseSuffixes (s.release()); + } + + if (matchIf (TokenTypes::plusplus)) return parsePostIncDec (input); + if (matchIf (TokenTypes::minusminus)) return parsePostIncDec (input); + + return input.release(); + } + + Expression* parseFactor() + { + if (currentType == TokenTypes::identifier) return parseSuffixes (new UnqualifiedName (location, parseIdentifier())); + if (matchIf (TokenTypes::openParen)) return parseSuffixes (matchCloseParen (parseExpression())); + if (matchIf (TokenTypes::true_)) return parseSuffixes (new LiteralValue (location, (int) 1)); + if (matchIf (TokenTypes::false_)) return parseSuffixes (new LiteralValue (location, (int) 0)); + if (matchIf (TokenTypes::null_)) return parseSuffixes (new LiteralValue (location, var())); + if (matchIf (TokenTypes::undefined)) return parseSuffixes (new Expression (location)); + + if (currentType == TokenTypes::literal) + { + var v (currentValue); skip(); + return parseSuffixes (new LiteralValue (location, v)); + } + + if (matchIf (TokenTypes::openBrace)) + { + ScopedPointer e (new ObjectDeclaration (location)); + + while (currentType != TokenTypes::closeBrace) + { + e->names.add (currentValue.toString()); + match ((currentType == TokenTypes::literal && currentValue.isString()) + ? TokenTypes::literal : TokenTypes::identifier); + match (TokenTypes::colon); + e->initialisers.add (parseExpression()); + + if (currentType != TokenTypes::closeBrace) + match (TokenTypes::comma); + } + + match (TokenTypes::closeBrace); + return parseSuffixes (e.release()); + } + + if (matchIf (TokenTypes::openBracket)) + { + ScopedPointer e (new ArrayDeclaration (location)); + + while (currentType != TokenTypes::closeBracket) + { + e->values.add (parseExpression()); + + if (currentType != TokenTypes::closeBracket) + match (TokenTypes::comma); + } + + match (TokenTypes::closeBracket); + return parseSuffixes (e.release()); + } + + if (matchIf (TokenTypes::function)) + { + Identifier name; + var fn = parseFunctionDefinition (name); + + if (name.isValid()) + throwError ("Inline functions definitions cannot have a name"); + + return new LiteralValue (location, fn); + } + + if (matchIf (TokenTypes::new_)) + { + ExpPtr name (new UnqualifiedName (location, parseIdentifier())); + + while (matchIf (TokenTypes::dot)) + name = new DotOperator (location, name, parseIdentifier()); + + return parseFunctionCall (new NewOperator (location), name); + } + + throwError ("Found " + getTokenName (currentType) + " when expecting an expression"); + return nullptr; + } + + template + Expression* parsePreIncDec() + { + Expression* e = parseFactor(); // careful - bare pointer is deliberately alised + ExpPtr lhs (e), one (new LiteralValue (location, (int) 1)); + return new SelfAssignment (location, e, new OpType (location, lhs, one)); + } + + template + Expression* parsePostIncDec (ExpPtr& lhs) + { + Expression* e = lhs.release(); // careful - bare pointer is deliberately alised + ExpPtr lhs2 (e), one (new LiteralValue (location, (int) 1)); + return new PostAssignment (location, e, new OpType (location, lhs2, one)); + } + + Expression* parseUnary() + { + if (matchIf (TokenTypes::minus)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new SubtractionOp (location, a, b); } + if (matchIf (TokenTypes::logicalNot)) { ExpPtr a (new LiteralValue (location, (int) 0)), b (parseUnary()); return new EqualsOp (location, a, b); } + if (matchIf (TokenTypes::plusplus)) return parsePreIncDec(); + if (matchIf (TokenTypes::minusminus)) return parsePreIncDec(); + + return parseFactor(); + } + + Expression* parseMultiplyDivide() + { + ExpPtr a (parseUnary()); + + for (;;) + { + if (matchIf (TokenTypes::times)) { ExpPtr b (parseUnary()); a = new MultiplyOp (location, a, b); } + else if (matchIf (TokenTypes::divide)) { ExpPtr b (parseUnary()); a = new DivideOp (location, a, b); } + else if (matchIf (TokenTypes::modulo)) { ExpPtr b (parseUnary()); a = new ModuloOp (location, a, b); } + else break; + } + + return a.release(); + } + + Expression* parseAdditionSubtraction() + { + ExpPtr a (parseMultiplyDivide()); + + for (;;) + { + if (matchIf (TokenTypes::plus)) { ExpPtr b (parseMultiplyDivide()); a = new AdditionOp (location, a, b); } + else if (matchIf (TokenTypes::minus)) { ExpPtr b (parseMultiplyDivide()); a = new SubtractionOp (location, a, b); } + else break; + } + + return a.release(); + } + + Expression* parseShiftOperator() + { + ExpPtr a (parseAdditionSubtraction()); + + for (;;) + { + if (matchIf (TokenTypes::leftShift)) { ExpPtr b (parseExpression()); a = new LeftShiftOp (location, a, b); } + else if (matchIf (TokenTypes::rightShift)) { ExpPtr b (parseExpression()); a = new RightShiftOp (location, a, b); } + else if (matchIf (TokenTypes::rightShiftUnsigned)) { ExpPtr b (parseExpression()); a = new RightShiftUnsignedOp (location, a, b); } + else break; + } + + return a.release(); + } + + Expression* parseComparator() + { + ExpPtr a (parseShiftOperator()); + + for (;;) + { + if (matchIf (TokenTypes::equals)) { ExpPtr b (parseShiftOperator()); a = new EqualsOp (location, a, b); } + else if (matchIf (TokenTypes::notEquals)) { ExpPtr b (parseShiftOperator()); a = new NotEqualsOp (location, a, b); } + else if (matchIf (TokenTypes::typeEquals)) { ExpPtr b (parseShiftOperator()); a = new TypeEqualsOp (location, a, b); } + else if (matchIf (TokenTypes::typeNotEquals)) { ExpPtr b (parseShiftOperator()); a = new TypeNotEqualsOp (location, a, b); } + else if (matchIf (TokenTypes::lessThan)) { ExpPtr b (parseShiftOperator()); a = new LessThanOp (location, a, b); } + else if (matchIf (TokenTypes::lessThanOrEqual)) { ExpPtr b (parseShiftOperator()); a = new LessThanOrEqualOp (location, a, b); } + else if (matchIf (TokenTypes::greaterThan)) { ExpPtr b (parseShiftOperator()); a = new GreaterThanOp (location, a, b); } + else if (matchIf (TokenTypes::greaterThanOrEqual)) { ExpPtr b (parseShiftOperator()); a = new GreaterThanOrEqualOp (location, a, b); } + else break; + } + + return a.release(); + } + + Expression* parseLogicOperator() + { + ExpPtr a (parseComparator()); + + for (;;) + { + if (matchIf (TokenTypes::logicalAnd)) { ExpPtr b (parseComparator()); a = new LogicalAndOp (location, a, b); } + else if (matchIf (TokenTypes::logicalOr)) { ExpPtr b (parseComparator()); a = new LogicalOrOp (location, a, b); } + else if (matchIf (TokenTypes::bitwiseAnd)) { ExpPtr b (parseComparator()); a = new BitwiseAndOp (location, a, b); } + else if (matchIf (TokenTypes::bitwiseOr)) { ExpPtr b (parseComparator()); a = new BitwiseOrOp (location, a, b); } + else if (matchIf (TokenTypes::bitwiseXor)) { ExpPtr b (parseComparator()); a = new BitwiseXorOp (location, a, b); } + else break; + } + + return a.release(); + } + + Expression* parseTerneryOperator (ExpPtr& condition) + { + ScopedPointer e (new ConditionalOp (location)); + e->condition = condition; + e->trueBranch = parseExpression(); + match (TokenTypes::colon); + e->falseBranch = parseExpression(); + return e.release(); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ExpressionTreeBuilder) + }; + + //============================================================================== + static var get (Args a, int index) noexcept { return index < a.numArguments ? a.arguments[index] : var(); } + static bool isInt (Args a, int index) noexcept { return get (a, index).isInt() || get (a, index).isInt64(); } + static int getInt (Args a, int index) noexcept { return get (a, index); } + static double getDouble (Args a, int index) noexcept { return get (a, index); } + static String getString (Args a, int index) noexcept { return get (a, index).toString(); } + + //============================================================================== + struct ObjectClass : public DynamicObject + { + ObjectClass() + { + setMethod ("dump", dump); + setMethod ("clone", clone); + } + + static Identifier getClassName() { static const Identifier i ("Object"); return i; } + static var dump (Args a) { DBG (JSON::toString (a.thisObject)); (void) a; return var::undefined(); } + static var clone (Args a) { return a.thisObject.clone(); } + }; + + //============================================================================== + struct ArrayClass : public DynamicObject + { + ArrayClass() + { + setMethod ("contains", contains); + setMethod ("remove", remove); + setMethod ("join", join); + } + + static Identifier getClassName() { static const Identifier i ("Array"); return i; } + + static var contains (Args a) + { + if (const Array* array = a.thisObject.getArray()) + return array->contains (get (a, 0)); + + return false; + } + + static var remove (Args a) + { + if (Array* array = a.thisObject.getArray()) + array->removeAllInstancesOf (get (a, 0)); + + return var::undefined(); + } + + static var join (Args a) + { + StringArray strings; + + if (const Array* array = a.thisObject.getArray()) + for (int i = 0; i < array->size(); ++i) + strings.add (array->getReference(i).toString()); + + return strings.joinIntoString (getString (a, 0)); + } + }; + + //============================================================================== + struct StringClass : public DynamicObject + { + StringClass() + { + setMethod ("substring", substring); + setMethod ("indexOf", indexOf); + setMethod ("charAt", charAt); + setMethod ("charCodeAt", charCodeAt); + setMethod ("fromCharCode", fromCharCode); + setMethod ("split", split); + } + + static Identifier getClassName() { static const Identifier i ("String"); return i; } + + static var fromCharCode (Args a) { return String::charToString (getInt (a, 0)); } + static var substring (Args a) { return a.thisObject.toString().substring (getInt (a, 0), getInt (a, 1)); } + static var indexOf (Args a) { return a.thisObject.toString().indexOf (getString (a, 0)); } + static var charCodeAt (Args a) { return (int) a.thisObject.toString() [getInt (a, 0)]; } + static var charAt (Args a) { int p = getInt (a, 0); return a.thisObject.toString().substring (p, p + 1); } + + static var split (Args a) + { + const String str (a.thisObject.toString()); + const String sep (getString (a, 0)); + StringArray strings; + + if (sep.isNotEmpty()) + strings.addTokens (str, sep.substring (0, 1), ""); + else // special-case for empty separator: split all chars separately + for (String::CharPointerType pos = str.getCharPointer(); ! pos.isEmpty(); ++pos) + strings.add (String::charToString (*pos)); + + var array; + for (int i = 0; i < strings.size(); ++i) + array.append (strings[i]); + + return array; + } + }; + + //============================================================================== + struct MathClass : public DynamicObject + { + MathClass() + { + setMethod ("abs", Math_abs); setMethod ("round", Math_round); + setMethod ("random", Math_random); setMethod ("randInt", Math_randInt); + setMethod ("min", Math_min); setMethod ("max", Math_max); + setMethod ("range", Math_range); setMethod ("sign", Math_sign); + setMethod ("PI", Math_pi); setMethod ("E", Math_e); + setMethod ("toDegrees", Math_toDegrees); setMethod ("toRadians", Math_toRadians); + setMethod ("sin", Math_sin); setMethod ("asin", Math_asin); + setMethod ("sinh", Math_sinh); setMethod ("asinh", Math_asinh); + setMethod ("cos", Math_cos); setMethod ("acos", Math_acos); + setMethod ("cosh", Math_cosh); setMethod ("acosh", Math_acosh); + setMethod ("tan", Math_tan); setMethod ("atan", Math_atan); + setMethod ("tanh", Math_tanh); setMethod ("atanh", Math_atanh); + setMethod ("log", Math_log); setMethod ("log10", Math_log10); + setMethod ("exp", Math_exp); setMethod ("pow", Math_pow); + setMethod ("sqr", Math_sqr); setMethod ("sqrt", Math_sqrt); + } + + static var Math_pi (Args) { return double_Pi; } + static var Math_e (Args) { return exp (1.0); } + static var Math_random (Args) { return Random::getSystemRandom().nextDouble(); } + static var Math_randInt (Args a) { return Random::getSystemRandom().nextInt (Range (getInt (a, 0), getInt (a, 1))); } + static var Math_abs (Args a) { return isInt (a, 0) ? var (std::abs (getInt (a, 0))) : var (std::abs (getDouble (a, 0))); } + static var Math_round (Args a) { return isInt (a, 0) ? var (roundToInt (getInt (a, 0))) : var (roundToInt (getDouble (a, 0))); } + static var Math_sign (Args a) { return isInt (a, 0) ? var (sign (getInt (a, 0))) : var (sign (getDouble (a, 0))); } + static var Math_range (Args a) { return isInt (a, 0) ? var (jlimit (getInt (a, 1), getInt (a, 2), getInt (a, 0))) : var (jlimit (getDouble (a, 1), getDouble (a, 2), getDouble (a, 0))); } + static var Math_min (Args a) { return (isInt (a, 0) && isInt (a, 1)) ? var (jmin (getInt (a, 0), getInt (a, 1))) : var (jmin (getDouble (a, 0), getDouble (a, 1))); } + static var Math_max (Args a) { return (isInt (a, 0) && isInt (a, 1)) ? var (jmax (getInt (a, 0), getInt (a, 1))) : var (jmax (getDouble (a, 0), getDouble (a, 1))); } + static var Math_toDegrees (Args a) { return (180.0 / double_Pi) * getDouble (a, 0); } + static var Math_toRadians (Args a) { return (double_Pi / 180.0) * getDouble (a, 0); } + static var Math_sin (Args a) { return sin (getDouble (a, 0)); } + static var Math_asin (Args a) { return asin (getDouble (a, 0)); } + static var Math_cos (Args a) { return cos (getDouble (a, 0)); } + static var Math_acos (Args a) { return acos (getDouble (a, 0)); } + static var Math_sinh (Args a) { return sinh (getDouble (a, 0)); } + static var Math_asinh (Args a) { return asinh (getDouble (a, 0)); } + static var Math_cosh (Args a) { return cosh (getDouble (a, 0)); } + static var Math_acosh (Args a) { return acosh (getDouble (a, 0)); } + static var Math_tan (Args a) { return tan (getDouble (a, 0)); } + static var Math_tanh (Args a) { return tanh (getDouble (a, 0)); } + static var Math_atan (Args a) { return atan (getDouble (a, 0)); } + static var Math_atanh (Args a) { return atanh (getDouble (a, 0)); } + static var Math_log (Args a) { return log (getDouble (a, 0)); } + static var Math_log10 (Args a) { return log10 (getDouble (a, 0)); } + static var Math_exp (Args a) { return exp (getDouble (a, 0)); } + static var Math_pow (Args a) { return pow (getDouble (a, 0), getDouble (a, 1)); } + static var Math_sqr (Args a) { double x = getDouble (a, 0); return x * x; } + static var Math_sqrt (Args a) { return std::sqrt (getDouble (a, 0)); } + + static Identifier getClassName() { static const Identifier i ("Math"); return i; } + template static Type sign (Type n) noexcept { return n > 0 ? (Type) 1 : (n < 0 ? (Type) -1 : 0); } + }; + + //============================================================================== + struct JSONClass : public DynamicObject + { + JSONClass() { setMethod ("stringify", stringify); } + static Identifier getClassName() { static const Identifier i ("JSON"); return i; } + static var stringify (Args a) { return JSON::toString (get (a, 0)); } + }; + + //============================================================================== + struct IntegerClass : public DynamicObject + { + IntegerClass() { setMethod ("parseInt", parseInt); } + static Identifier getClassName() { static const Identifier i ("Integer"); return i; } + + static var parseInt (Args a) + { + const String s (getString (a, 0).trim()); + + return s[0] == '0' ? (s[1] == 'x' ? s.substring(2).getHexValue64() : getOctalValue (s)) + : s.getLargeIntValue(); + } + }; + + //============================================================================== + static var trace (Args a) { Logger::outputDebugString (JSON::toString (a.thisObject)); return var::undefined(); } + static var charToInt (Args a) { return (int) (getString (a, 0)[0]); } + + static var exec (Args a) + { + if (RootObject* root = dynamic_cast (a.thisObject.getObject())) + root->execute (getString (a, 0)); + + return var::undefined(); + } + + static var eval (Args a) + { + if (RootObject* root = dynamic_cast (a.thisObject.getObject())) + return root->evaluate (getString (a, 0)); + + return var::undefined(); + } +}; + +//============================================================================== +JavascriptEngine::JavascriptEngine() : maximumExecutionTime (15.0), root (new RootObject()) +{ + registerNativeObject (RootObject::ObjectClass ::getClassName(), new RootObject::ObjectClass()); + registerNativeObject (RootObject::ArrayClass ::getClassName(), new RootObject::ArrayClass()); + registerNativeObject (RootObject::StringClass ::getClassName(), new RootObject::StringClass()); + registerNativeObject (RootObject::MathClass ::getClassName(), new RootObject::MathClass()); + registerNativeObject (RootObject::JSONClass ::getClassName(), new RootObject::JSONClass()); + registerNativeObject (RootObject::IntegerClass ::getClassName(), new RootObject::IntegerClass()); +} + +JavascriptEngine::~JavascriptEngine() {} + +void JavascriptEngine::prepareTimeout() const { root->timeout = Time::getCurrentTime() + maximumExecutionTime; } + +void JavascriptEngine::registerNativeObject (Identifier name, DynamicObject* object) +{ + root->setProperty (name, object); +} + +Result JavascriptEngine::execute (const String& code) +{ + try + { + prepareTimeout(); + root->execute (code); + } + catch (String& error) + { + return Result::fail (error); + } + + return Result::ok(); +} + +var JavascriptEngine::evaluate (const String& code, Result* result) +{ + try + { + prepareTimeout(); + if (result != nullptr) *result = Result::ok(); + return root->evaluate (code); + } + catch (String& error) + { + if (result != nullptr) *result = Result::fail (error); + } + + return var::undefined(); +} + +var JavascriptEngine::callFunction (Identifier function, const var::NativeFunctionArgs& args, Result* result) +{ + var returnVal (var::undefined()); + + try + { + prepareTimeout(); + if (result != nullptr) *result = Result::ok(); + RootObject::Scope (nullptr, root, root).findAndInvokeMethod (function, args, returnVal); + } + catch (String& error) + { + if (result != nullptr) *result = Result::fail (error); + } + + return returnVal; +} + +#if JUCE_MSVC + #pragma warning (pop) +#endif diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h new file mode 100644 index 0000000..42368a4 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h @@ -0,0 +1,105 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +/** + A simple javascript interpreter! + + It's not fully standards-compliant, and won't be as fast as the fancy JIT-compiled + engines that you get in browsers, but this is an extremely compact, low-overhead javascript + interpreter, which is integrated with the juce var and DynamicObject classes. If you need + a few simple bits of scripting in your app, and want to be able to easily let the JS + work with native objects defined as DynamicObject subclasses, then this might do the job. + + To use, simply create an instance of this class and call execute() to run your code. + Variables that the script sets can be retrieved with evaluate(), and if you need to provide + native objects for the script to use, you can add them with registerNativeObject(). + + One caveat: Because the values and objects that the engine works with are DynamicObject + and var objects, they use reference-counting rather than garbage-collection, so if your + script creates complex connections between objects, you run the risk of creating cyclic + dependencies and hence leaking. +*/ +class JavascriptEngine +{ +public: + /** Creates an instance of the engine. + This creates a root namespace and defines some basic Object, String, Array + and Math library methods. + */ + JavascriptEngine(); + + /** Destructor. */ + ~JavascriptEngine(); + + /** Attempts to parse and run a block of javascript code. + If there's a parse or execution error, the error description is returned in + the result. + You can specify a maximum time for which the program is allowed to run, and + it'll return with an error message if this time is exceeded. + */ + Result execute (const String& javascriptCode); + + /** Attempts to parse and run a javascript expression, and returns the result. + If there's a syntax error, or the expression can't be evaluated, the return value + will be var::undefined(). The errorMessage parameter gives you a way to find out + any parsing errors. + You can specify a maximum time for which the program is allowed to run, and + it'll return with an error message if this time is exceeded. + */ + var evaluate (const String& javascriptCode, + Result* errorMessage = nullptr); + + /** Calls a function in the root namespace, and returns the result. + The function arguments are passed in the same format as used by native + methods in the var class. + */ + var callFunction (Identifier function, + const var::NativeFunctionArgs& args, + Result* errorMessage = nullptr); + + /** Adds a native object to the root namespace. + The object passed-in is reference-counted, and will be retained by the + engine until the engine is deleted. The name must be a simple JS identifier, + without any dots. + */ + void registerNativeObject (Identifier objectName, DynamicObject* object); + + /** This value indicates how long a call to one of the evaluate methods is permitted + to run before timing-out and failing. + The default value is a number of seconds, but you can change this to whatever value + suits your application. + */ + RelativeTime maximumExecutionTime; + +private: + JUCE_PUBLIC_IN_DLL_BUILD (struct RootObject) + ReferenceCountedObjectPtr root; + void prepareTimeout() const; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JavascriptEngine) +}; diff --git a/JuceLibraryCode/modules/juce_core/juce_core.cpp b/JuceLibraryCode/modules/juce_core/juce_core.cpp index 842788b..c992d50 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.cpp +++ b/JuceLibraryCode/modules/juce_core/juce_core.cpp @@ -1,29 +1,32 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#if defined (__JUCE_CORE_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_CORE_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -53,6 +56,14 @@ #include #include + #if ! JUCE_MINGW + #include + + #if ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES + #pragma comment (lib, "DbgHelp.lib") + #endif + #endif + #if JUCE_MINGW #include #endif @@ -66,16 +77,27 @@ #include #endif + #if JUCE_LINUX + #include + #endif + #include #include #include #include #include #include + #include + #include + + #if ! JUCE_ANDROID + #include + #endif #endif #if JUCE_MAC || JUCE_IOS #include + #include #endif #if JUCE_ANDROID @@ -87,11 +109,7 @@ namespace juce { -// START_AUTOINCLUDE containers/*.cpp, files/*.cpp, json/*.cpp, logging/*.cpp, maths/*.cpp, -// memory/*.cpp, misc/*.cpp, network/*.cpp, streams/*.cpp, system/*.cpp, text/*.cpp, threads/*.cpp, -// time/*.cpp, unit_tests/*.cpp, xml/*.cpp, zip/juce_GZIPD*.cpp, zip/juce_GZIPC*.cpp, zip/juce_Zip*.cpp #include "containers/juce_AbstractFifo.cpp" -#include "containers/juce_DynamicObject.cpp" #include "containers/juce_NamedValueSet.cpp" #include "containers/juce_PropertySet.cpp" #include "containers/juce_Variant.cpp" @@ -101,7 +119,9 @@ namespace juce #include "files/juce_FileOutputStream.cpp" #include "files/juce_FileSearchPath.cpp" #include "files/juce_TemporaryFile.cpp" -#include "json/juce_JSON.cpp" +#include "javascript/juce_JSON.cpp" +#include "javascript/juce_Javascript.cpp" +#include "containers/juce_DynamicObject.cpp" #include "logging/juce_FileLogger.cpp" #include "logging/juce_Logger.cpp" #include "maths/juce_BigInteger.cpp" @@ -113,23 +133,23 @@ namespace juce #include "network/juce_MACAddress.cpp" #include "network/juce_NamedPipe.cpp" #include "network/juce_Socket.cpp" -#include "network/juce_URL.cpp" +#include "network/juce_IPAddress.cpp" #include "streams/juce_BufferedInputStream.cpp" #include "streams/juce_FileInputSource.cpp" #include "streams/juce_InputStream.cpp" #include "streams/juce_MemoryInputStream.cpp" #include "streams/juce_MemoryOutputStream.cpp" -#include "streams/juce_OutputStream.cpp" #include "streams/juce_SubregionStream.cpp" #include "system/juce_SystemStats.cpp" #include "text/juce_CharacterFunctions.cpp" #include "text/juce_Identifier.cpp" #include "text/juce_LocalisedStrings.cpp" #include "text/juce_String.cpp" +#include "streams/juce_OutputStream.cpp" #include "text/juce_StringArray.cpp" #include "text/juce_StringPairArray.cpp" #include "text/juce_StringPool.cpp" -#include "threads/juce_ChildProcess.cpp" +#include "text/juce_TextDiff.cpp" #include "threads/juce_ReadWriteLock.cpp" #include "threads/juce_Thread.cpp" #include "threads/juce_ThreadPool.cpp" @@ -143,12 +163,12 @@ namespace juce #include "zip/juce_GZIPDecompressorInputStream.cpp" #include "zip/juce_GZIPCompressorOutputStream.cpp" #include "zip/juce_ZipFile.cpp" -// END_AUTOINCLUDE +#include "files/juce_FileFilter.cpp" +#include "files/juce_WildcardFileFilter.cpp" //============================================================================== #if JUCE_MAC || JUCE_IOS #include "native/juce_osx_ObjCHelpers.h" -#include "native/juce_mac_ObjCSuffix.h" #endif #if JUCE_ANDROID @@ -179,6 +199,7 @@ namespace juce //============================================================================== #elif JUCE_LINUX +#include "native/juce_linux_CommonFile.cpp" #include "native/juce_linux_Files.cpp" #include "native/juce_linux_Network.cpp" #include "native/juce_linux_SystemStats.cpp" @@ -186,6 +207,7 @@ namespace juce //============================================================================== #elif JUCE_ANDROID +#include "native/juce_linux_CommonFile.cpp" #include "native/juce_android_Files.cpp" #include "native/juce_android_Misc.cpp" #include "native/juce_android_Network.cpp" @@ -193,4 +215,9 @@ namespace juce #include "native/juce_android_Threads.cpp" #endif + +#include "threads/juce_ChildProcess.cpp" +#include "threads/juce_HighResolutionTimer.cpp" +#include "network/juce_URL.cpp" + } diff --git a/JuceLibraryCode/modules/juce_core/juce_core.h b/JuceLibraryCode/modules/juce_core/juce_core.h index e46ecf7..9496064 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.h +++ b/JuceLibraryCode/modules/juce_core/juce_core.h @@ -1,38 +1,52 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CORE_JUCEHEADER__ -#define __JUCE_CORE_JUCEHEADER__ +#ifndef JUCE_CORE_H_INCLUDED +#define JUCE_CORE_H_INCLUDED -/* This line is here as a sanity-check to catch syntax errors caused by mistakes in 3rd-party - header files that have been included prior to this one. If you hit an error at this line, - there's probably some kind of syntax problem in whatever code immediately precedes this header. +#ifndef JUCE_MODULE_AVAILABLE_juce_core + /* If you fail to make sure that all your compile units are building JUCE with the same set of + option flags, then there's a risk that different compile units will treat the classes as having + different memory layouts, leading to very nasty memory corruption errors when they all get + linked together. That's why it's best to always include the Introjucer-generated AppConfig.h + file before any juce headers. - It also causes an error if you attempt to build using a C or obj-C compiler rather than a C++ one. -*/ -namespace DummyNamespaceStatementToCatchSyntaxErrors {} + Note that if you do have an AppConfig.h file and hit this warning, it means that it doesn't + contain the JUCE_MODULE_AVAILABLE_xxx flags, which are necessary for some inter-module + functionality to work correctly. In that case, you should either rebuild your AppConfig.h with + the latest introjucer, or fix it manually to contain these flags. + */ + #ifdef _MSC_VER + #pragma message ("Have you included your AppConfig.h file before including the JUCE headers?") + #else + #warning "Have you included your AppConfig.h file before including the JUCE headers?" + #endif +#endif //============================================================================== #include "system/juce_TargetPlatform.h" @@ -50,7 +64,7 @@ namespace DummyNamespaceStatementToCatchSyntaxErrors {} //============================================================================= /** Config: JUCE_LOG_ASSERTIONS - If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog() + If this flag is enabled, 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, @@ -87,7 +101,7 @@ namespace DummyNamespaceStatementToCatchSyntaxErrors {} #define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 #endif -/* Config: JUCE_INCLUDE_ZLIB_CODE +/** Config: JUCE_INCLUDE_ZLIB_CODE This 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. @@ -105,12 +119,16 @@ namespace DummyNamespaceStatementToCatchSyntaxErrors {} /* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS If enabled, this will add some exception-catching code to forward unhandled exceptions - to your JUCEApplication::unhandledException() callback. + to your JUCEApplicationBase::unhandledException() callback. */ #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 #endif +#ifndef JUCE_STRING_UTF_TYPE + #define JUCE_STRING_UTF_TYPE 8 +#endif + //============================================================================= //============================================================================= #if JUCE_MSVC @@ -127,297 +145,134 @@ namespace DummyNamespaceStatementToCatchSyntaxErrors {} namespace juce { -// START_AUTOINCLUDE containers, files, json, logging, maths, memory, misc, network, -// streams, system, text, threads, time, unit_tests, xml, zip -#ifndef __JUCE_ABSTRACTFIFO_JUCEHEADER__ - #include "containers/juce_AbstractFifo.h" -#endif -#ifndef __JUCE_ARRAY_JUCEHEADER__ - #include "containers/juce_Array.h" -#endif -#ifndef __JUCE_ARRAYALLOCATIONBASE_JUCEHEADER__ - #include "containers/juce_ArrayAllocationBase.h" -#endif -#ifndef __JUCE_DYNAMICOBJECT_JUCEHEADER__ - #include "containers/juce_DynamicObject.h" -#endif -#ifndef __JUCE_ELEMENTCOMPARATOR_JUCEHEADER__ - #include "containers/juce_ElementComparator.h" -#endif -#ifndef __JUCE_HASHMAP_JUCEHEADER__ - #include "containers/juce_HashMap.h" -#endif -#ifndef __JUCE_LINKEDLISTPOINTER_JUCEHEADER__ - #include "containers/juce_LinkedListPointer.h" -#endif -#ifndef __JUCE_NAMEDVALUESET_JUCEHEADER__ - #include "containers/juce_NamedValueSet.h" -#endif -#ifndef __JUCE_OWNEDARRAY_JUCEHEADER__ - #include "containers/juce_OwnedArray.h" -#endif -#ifndef __JUCE_PROPERTYSET_JUCEHEADER__ - #include "containers/juce_PropertySet.h" -#endif -#ifndef __JUCE_REFERENCECOUNTEDARRAY_JUCEHEADER__ - #include "containers/juce_ReferenceCountedArray.h" -#endif -#ifndef __JUCE_SCOPEDVALUESETTER_JUCEHEADER__ - #include "containers/juce_ScopedValueSetter.h" -#endif -#ifndef __JUCE_SORTEDSET_JUCEHEADER__ - #include "containers/juce_SortedSet.h" -#endif -#ifndef __JUCE_SPARSESET_JUCEHEADER__ - #include "containers/juce_SparseSet.h" -#endif -#ifndef __JUCE_VARIANT_JUCEHEADER__ - #include "containers/juce_Variant.h" -#endif -#ifndef __JUCE_DIRECTORYITERATOR_JUCEHEADER__ - #include "files/juce_DirectoryIterator.h" -#endif -#ifndef __JUCE_FILE_JUCEHEADER__ - #include "files/juce_File.h" -#endif -#ifndef __JUCE_FILEINPUTSTREAM_JUCEHEADER__ - #include "files/juce_FileInputStream.h" -#endif -#ifndef __JUCE_FILEOUTPUTSTREAM_JUCEHEADER__ - #include "files/juce_FileOutputStream.h" -#endif -#ifndef __JUCE_FILESEARCHPATH_JUCEHEADER__ - #include "files/juce_FileSearchPath.h" -#endif -#ifndef __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__ - #include "files/juce_MemoryMappedFile.h" -#endif -#ifndef __JUCE_TEMPORARYFILE_JUCEHEADER__ - #include "files/juce_TemporaryFile.h" -#endif -#ifndef __JUCE_JSON_JUCEHEADER__ - #include "json/juce_JSON.h" -#endif -#ifndef __JUCE_FILELOGGER_JUCEHEADER__ - #include "logging/juce_FileLogger.h" -#endif -#ifndef __JUCE_LOGGER_JUCEHEADER__ - #include "logging/juce_Logger.h" -#endif -#ifndef __JUCE_BIGINTEGER_JUCEHEADER__ - #include "maths/juce_BigInteger.h" -#endif -#ifndef __JUCE_EXPRESSION_JUCEHEADER__ - #include "maths/juce_Expression.h" -#endif -#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ - #include "maths/juce_MathsFunctions.h" -#endif -#ifndef __JUCE_RANDOM_JUCEHEADER__ - #include "maths/juce_Random.h" -#endif -#ifndef __JUCE_RANGE_JUCEHEADER__ - #include "maths/juce_Range.h" -#endif -#ifndef __JUCE_ATOMIC_JUCEHEADER__ - #include "memory/juce_Atomic.h" -#endif -#ifndef __JUCE_BYTEORDER_JUCEHEADER__ - #include "memory/juce_ByteOrder.h" -#endif -#ifndef __JUCE_HEAPBLOCK_JUCEHEADER__ - #include "memory/juce_HeapBlock.h" -#endif -#ifndef __JUCE_LEAKEDOBJECTDETECTOR_JUCEHEADER__ - #include "memory/juce_LeakedObjectDetector.h" -#endif -#ifndef __JUCE_MEMORY_JUCEHEADER__ - #include "memory/juce_Memory.h" -#endif -#ifndef __JUCE_MEMORYBLOCK_JUCEHEADER__ - #include "memory/juce_MemoryBlock.h" -#endif -#ifndef __JUCE_OPTIONALSCOPEDPOINTER_JUCEHEADER__ - #include "memory/juce_OptionalScopedPointer.h" -#endif -#ifndef __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ - #include "memory/juce_ReferenceCountedObject.h" -#endif -#ifndef __JUCE_SCOPEDPOINTER_JUCEHEADER__ - #include "memory/juce_ScopedPointer.h" -#endif -#ifndef __JUCE_SINGLETON_JUCEHEADER__ - #include "memory/juce_Singleton.h" -#endif -#ifndef __JUCE_WEAKREFERENCE_JUCEHEADER__ - #include "memory/juce_WeakReference.h" -#endif -#ifndef __JUCE_RESULT_JUCEHEADER__ - #include "misc/juce_Result.h" -#endif -#ifndef __JUCE_UUID_JUCEHEADER__ - #include "misc/juce_Uuid.h" -#endif -#ifndef __JUCE_WINDOWSREGISTRY_JUCEHEADER__ - #include "misc/juce_WindowsRegistry.h" -#endif -#ifndef __JUCE_MACADDRESS_JUCEHEADER__ - #include "network/juce_MACAddress.h" -#endif -#ifndef __JUCE_NAMEDPIPE_JUCEHEADER__ - #include "network/juce_NamedPipe.h" -#endif -#ifndef __JUCE_SOCKET_JUCEHEADER__ - #include "network/juce_Socket.h" -#endif -#ifndef __JUCE_URL_JUCEHEADER__ - #include "network/juce_URL.h" -#endif -#ifndef __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ - #include "streams/juce_BufferedInputStream.h" -#endif -#ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ - #include "streams/juce_FileInputSource.h" -#endif -#ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ - #include "streams/juce_InputSource.h" -#endif -#ifndef __JUCE_INPUTSTREAM_JUCEHEADER__ - #include "streams/juce_InputStream.h" -#endif -#ifndef __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ - #include "streams/juce_MemoryInputStream.h" -#endif -#ifndef __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ - #include "streams/juce_MemoryOutputStream.h" -#endif -#ifndef __JUCE_OUTPUTSTREAM_JUCEHEADER__ - #include "streams/juce_OutputStream.h" -#endif -#ifndef __JUCE_SUBREGIONSTREAM_JUCEHEADER__ - #include "streams/juce_SubregionStream.h" -#endif -#ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ - #include "system/juce_PlatformDefs.h" -#endif -#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ - #include "system/juce_StandardHeader.h" -#endif -#ifndef __JUCE_SYSTEMSTATS_JUCEHEADER__ - #include "system/juce_SystemStats.h" -#endif -#ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ - #include "system/juce_TargetPlatform.h" -#endif -#ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ - #include "text/juce_CharacterFunctions.h" -#endif -#ifndef __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ - #include "text/juce_CharPointer_ASCII.h" -#endif -#ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ - #include "text/juce_CharPointer_UTF16.h" -#endif -#ifndef __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ - #include "text/juce_CharPointer_UTF32.h" -#endif -#ifndef __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ - #include "text/juce_CharPointer_UTF8.h" -#endif -#ifndef __JUCE_IDENTIFIER_JUCEHEADER__ - #include "text/juce_Identifier.h" -#endif -#ifndef __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ - #include "text/juce_LocalisedStrings.h" -#endif -#ifndef __JUCE_NEWLINE_JUCEHEADER__ - #include "text/juce_NewLine.h" -#endif -#ifndef __JUCE_STRING_JUCEHEADER__ - #include "text/juce_String.h" -#endif -#ifndef __JUCE_STRINGARRAY_JUCEHEADER__ - #include "text/juce_StringArray.h" -#endif -#ifndef __JUCE_STRINGPAIRARRAY_JUCEHEADER__ - #include "text/juce_StringPairArray.h" -#endif -#ifndef __JUCE_STRINGPOOL_JUCEHEADER__ - #include "text/juce_StringPool.h" -#endif -#ifndef __JUCE_CHILDPROCESS_JUCEHEADER__ - #include "threads/juce_ChildProcess.h" -#endif -#ifndef __JUCE_CRITICALSECTION_JUCEHEADER__ - #include "threads/juce_CriticalSection.h" -#endif -#ifndef __JUCE_DYNAMICLIBRARY_JUCEHEADER__ - #include "threads/juce_DynamicLibrary.h" -#endif -#ifndef __JUCE_INTERPROCESSLOCK_JUCEHEADER__ - #include "threads/juce_InterProcessLock.h" -#endif -#ifndef __JUCE_PROCESS_JUCEHEADER__ - #include "threads/juce_Process.h" -#endif -#ifndef __JUCE_READWRITELOCK_JUCEHEADER__ - #include "threads/juce_ReadWriteLock.h" -#endif -#ifndef __JUCE_SCOPEDLOCK_JUCEHEADER__ - #include "threads/juce_ScopedLock.h" -#endif -#ifndef __JUCE_SCOPEDREADLOCK_JUCEHEADER__ - #include "threads/juce_ScopedReadLock.h" -#endif -#ifndef __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ - #include "threads/juce_ScopedWriteLock.h" -#endif -#ifndef __JUCE_SPINLOCK_JUCEHEADER__ - #include "threads/juce_SpinLock.h" -#endif -#ifndef __JUCE_THREAD_JUCEHEADER__ - #include "threads/juce_Thread.h" -#endif -#ifndef __JUCE_THREADLOCALVALUE_JUCEHEADER__ - #include "threads/juce_ThreadLocalValue.h" -#endif -#ifndef __JUCE_THREADPOOL_JUCEHEADER__ - #include "threads/juce_ThreadPool.h" -#endif -#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ - #include "threads/juce_TimeSliceThread.h" -#endif -#ifndef __JUCE_WAITABLEEVENT_JUCEHEADER__ - #include "threads/juce_WaitableEvent.h" -#endif -#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ - #include "time/juce_PerformanceCounter.h" -#endif -#ifndef __JUCE_RELATIVETIME_JUCEHEADER__ - #include "time/juce_RelativeTime.h" -#endif -#ifndef __JUCE_TIME_JUCEHEADER__ - #include "time/juce_Time.h" -#endif -#ifndef __JUCE_UNITTEST_JUCEHEADER__ - #include "unit_tests/juce_UnitTest.h" -#endif -#ifndef __JUCE_XMLDOCUMENT_JUCEHEADER__ - #include "xml/juce_XmlDocument.h" -#endif -#ifndef __JUCE_XMLELEMENT_JUCEHEADER__ - #include "xml/juce_XmlElement.h" -#endif -#ifndef __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ - #include "zip/juce_GZIPCompressorOutputStream.h" -#endif -#ifndef __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ - #include "zip/juce_GZIPDecompressorInputStream.h" -#endif -#ifndef __JUCE_ZIPFILE_JUCEHEADER__ - #include "zip/juce_ZipFile.h" -#endif -// END_AUTOINCLUDE +class StringRef; +class MemoryBlock; +class File; +class InputStream; +class OutputStream; +class DynamicObject; +class FileInputStream; +class FileOutputStream; +class XmlElement; +class JSONFormatter; + +extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger(); +extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noexcept; + +#include "memory/juce_Memory.h" +#include "maths/juce_MathsFunctions.h" +#include "memory/juce_ByteOrder.h" +#include "memory/juce_Atomic.h" +#include "text/juce_CharacterFunctions.h" + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4514 4996) +#endif + +#include "text/juce_CharPointer_UTF8.h" +#include "text/juce_CharPointer_UTF16.h" +#include "text/juce_CharPointer_UTF32.h" +#include "text/juce_CharPointer_ASCII.h" + +#if JUCE_MSVC + #pragma warning (pop) +#endif + +#include "text/juce_String.h" +#include "text/juce_StringRef.h" +#include "logging/juce_Logger.h" +#include "memory/juce_LeakedObjectDetector.h" +#include "memory/juce_ContainerDeletePolicy.h" +#include "memory/juce_HeapBlock.h" +#include "memory/juce_MemoryBlock.h" +#include "memory/juce_ReferenceCountedObject.h" +#include "memory/juce_ScopedPointer.h" +#include "memory/juce_OptionalScopedPointer.h" +#include "memory/juce_Singleton.h" +#include "memory/juce_WeakReference.h" +#include "threads/juce_ScopedLock.h" +#include "threads/juce_CriticalSection.h" +#include "maths/juce_Range.h" +#include "containers/juce_ElementComparator.h" +#include "containers/juce_ArrayAllocationBase.h" +#include "containers/juce_Array.h" +#include "containers/juce_LinkedListPointer.h" +#include "containers/juce_OwnedArray.h" +#include "containers/juce_ReferenceCountedArray.h" +#include "containers/juce_ScopedValueSetter.h" +#include "containers/juce_SortedSet.h" +#include "containers/juce_SparseSet.h" +#include "containers/juce_AbstractFifo.h" +#include "text/juce_NewLine.h" +#include "text/juce_StringPool.h" +#include "text/juce_Identifier.h" +#include "text/juce_StringArray.h" +#include "text/juce_StringPairArray.h" +#include "text/juce_TextDiff.h" +#include "text/juce_LocalisedStrings.h" +#include "misc/juce_Result.h" +#include "containers/juce_Variant.h" +#include "containers/juce_NamedValueSet.h" +#include "containers/juce_DynamicObject.h" +#include "containers/juce_HashMap.h" +#include "time/juce_RelativeTime.h" +#include "time/juce_Time.h" +#include "streams/juce_InputStream.h" +#include "streams/juce_OutputStream.h" +#include "streams/juce_BufferedInputStream.h" +#include "streams/juce_MemoryInputStream.h" +#include "streams/juce_MemoryOutputStream.h" +#include "streams/juce_SubregionStream.h" +#include "streams/juce_InputSource.h" +#include "files/juce_File.h" +#include "files/juce_DirectoryIterator.h" +#include "files/juce_FileInputStream.h" +#include "files/juce_FileOutputStream.h" +#include "files/juce_FileSearchPath.h" +#include "files/juce_MemoryMappedFile.h" +#include "files/juce_TemporaryFile.h" +#include "files/juce_FileFilter.h" +#include "files/juce_WildcardFileFilter.h" +#include "streams/juce_FileInputSource.h" +#include "logging/juce_FileLogger.h" +#include "javascript/juce_JSON.h" +#include "javascript/juce_Javascript.h" +#include "maths/juce_BigInteger.h" +#include "maths/juce_Expression.h" +#include "maths/juce_Random.h" +#include "misc/juce_Uuid.h" +#include "misc/juce_WindowsRegistry.h" +#include "system/juce_PlatformDefs.h" +#include "system/juce_SystemStats.h" +#include "threads/juce_ChildProcess.h" +#include "threads/juce_DynamicLibrary.h" +#include "threads/juce_HighResolutionTimer.h" +#include "threads/juce_InterProcessLock.h" +#include "threads/juce_Process.h" +#include "threads/juce_SpinLock.h" +#include "threads/juce_WaitableEvent.h" +#include "threads/juce_Thread.h" +#include "threads/juce_ThreadLocalValue.h" +#include "threads/juce_ThreadPool.h" +#include "threads/juce_TimeSliceThread.h" +#include "threads/juce_ReadWriteLock.h" +#include "threads/juce_ScopedReadLock.h" +#include "threads/juce_ScopedWriteLock.h" +#include "network/juce_IPAddress.h" +#include "network/juce_MACAddress.h" +#include "network/juce_NamedPipe.h" +#include "network/juce_Socket.h" +#include "network/juce_URL.h" +#include "time/juce_PerformanceCounter.h" +#include "unit_tests/juce_UnitTest.h" +#include "xml/juce_XmlDocument.h" +#include "xml/juce_XmlElement.h" +#include "zip/juce_GZIPCompressorOutputStream.h" +#include "zip/juce_GZIPDecompressorInputStream.h" +#include "zip/juce_ZipFile.h" +#include "containers/juce_PropertySet.h" +#include "memory/juce_SharedResourcePointer.h" } @@ -425,4 +280,4 @@ namespace juce #pragma warning (pop) #endif -#endif // __JUCE_CORE_JUCEHEADER__ +#endif // JUCE_CORE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/juce_core.mm b/JuceLibraryCode/modules/juce_core/juce_core.mm index 6814ebd..90a2f7c 100644 --- a/JuceLibraryCode/modules/juce_core/juce_core.mm +++ b/JuceLibraryCode/modules/juce_core/juce_core.mm @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_core/juce_module_info b/JuceLibraryCode/modules/juce_core/juce_module_info index 4a7b76e..7f44564 100644 --- a/JuceLibraryCode/modules/juce_core/juce_module_info +++ b/JuceLibraryCode/modules/juce_core/juce_module_info @@ -1,10 +1,10 @@ { "id": "juce_core", "name": "JUCE core classes", - "version": "2.0.21", + "version": "3.0.6", "description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", "website": "http://www.juce.com/juce", - "license": "GPL/Commercial", + "license": "ISC Permissive", "dependencies": [], @@ -25,12 +25,14 @@ "logging/*", "system/*", "xml/*", - "json/*", + "javascript/*", "zip/*", "unit_tests/*", "misc/*", "native/*" ], "OSXFrameworks": "Cocoa IOKit", - "iOSFrameworks": "Foundation" + "iOSFrameworks": "Foundation", + "LinuxLibs": "rt dl pthread", + "mingwLibs": "uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm" } diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp index 7d796a7..8cadfda 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp +++ b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.cpp @@ -1,41 +1,41 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -FileLogger::FileLogger (const File& logFile_, +FileLogger::FileLogger (const File& file, const String& welcomeMessage, - const int maxInitialFileSizeBytes) - : logFile (logFile_) + const int64 maxInitialFileSizeBytes) + : logFile (file) { if (maxInitialFileSizeBytes >= 0) trimFileSize (maxInitialFileSizeBytes); - if (! logFile_.exists()) - { - // do this so that the parent directories get created.. - logFile_.create(); - } + if (! file.exists()) + file.create(); // (to create the parent directories) String welcome; welcome << newLine @@ -46,23 +46,18 @@ FileLogger::FileLogger (const File& logFile_, FileLogger::logMessage (welcome); } -FileLogger::~FileLogger() -{ -} +FileLogger::~FileLogger() {} //============================================================================== void FileLogger::logMessage (const String& message) { - DBG (message); - const ScopedLock sl (logLock); - + DBG (message); FileOutputStream out (logFile, 256); out << message << newLine; } - -void FileLogger::trimFileSize (int maxFileSizeBytes) const +void FileLogger::trimFileSize (int64 maxFileSizeBytes) const { if (maxFileSizeBytes <= 0) { @@ -74,59 +69,66 @@ void FileLogger::trimFileSize (int maxFileSizeBytes) const if (fileSize > maxFileSizeBytes) { - ScopedPointer in (logFile.createInputStream()); - jassert (in != nullptr); + TemporaryFile tempFile (logFile); - if (in != nullptr) { - in->setPosition (fileSize - maxFileSizeBytes); - String content; + FileOutputStream out (tempFile.getFile()); + FileInputStream in (logFile); + if (! (out.openedOk() && in.openedOk())) + return; + + in.setPosition (fileSize - maxFileSizeBytes); + + for (;;) { - MemoryBlock contentToSave; - contentToSave.setSize ((size_t) maxFileSizeBytes + 4); - contentToSave.fillWith (0); + const char c = in.readByte(); + if (c == 0) + return; - in->read (contentToSave.getData(), maxFileSizeBytes); - in = nullptr; - - content = contentToSave.toString(); + if (c == '\n' || c == '\r') + { + out << c; + break; + } } - int newStart = 0; - - while (newStart < fileSize - && content[newStart] != '\n' - && content[newStart] != '\r') - ++newStart; - - logFile.deleteFile(); - logFile.appendText (content.substring (newStart), false, false); + out.writeFromInputStream (in, -1); } + + tempFile.overwriteTargetFileWithTemporary(); } } } //============================================================================== +File FileLogger::getSystemLogFileFolder() +{ + #if JUCE_MAC + return File ("~/Library/Logs"); + #else + return File::getSpecialLocation (File::userApplicationDataDirectory); + #endif +} + FileLogger* FileLogger::createDefaultAppLogger (const String& logFileSubDirectoryName, const String& logFileName, const String& welcomeMessage, - const int maxInitialFileSizeBytes) + const int64 maxInitialFileSizeBytes) { - #if JUCE_MAC - File logFile ("~/Library/Logs"); - logFile = logFile.getChildFile (logFileSubDirectoryName) - .getChildFile (logFileName); - - #else - File logFile (File::getSpecialLocation (File::userApplicationDataDirectory)); - - if (logFile.isDirectory()) - { - logFile = logFile.getChildFile (logFileSubDirectoryName) - .getChildFile (logFileName); - } - #endif - - return new FileLogger (logFile, welcomeMessage, maxInitialFileSizeBytes); + return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName) + .getChildFile (logFileName), + welcomeMessage, maxInitialFileSizeBytes); +} + +FileLogger* FileLogger::createDateStampedLogger (const String& logFileSubDirectoryName, + const String& logFileNameRoot, + const String& logFileNameSuffix, + const String& welcomeMessage) +{ + return new FileLogger (getSystemLogFileFolder().getChildFile (logFileSubDirectoryName) + .getChildFile (logFileNameRoot + Time::getCurrentTime().formatted ("%Y-%m-%d_%H-%M-%S")) + .withFileExtension (logFileNameSuffix) + .getNonexistentSibling(), + welcomeMessage, 0); } diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h index 1fdd329..6be1668 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h +++ b/JuceLibraryCode/modules/juce_core/logging/juce_FileLogger.h @@ -1,39 +1,38 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_FILELOGGER_JUCEHEADER__ -#define __JUCE_FILELOGGER_JUCEHEADER__ - -#include "juce_Logger.h" -#include "../files/juce_File.h" -#include "../memory/juce_ScopedPointer.h" +#ifndef JUCE_FILELOGGER_H_INCLUDED +#define JUCE_FILELOGGER_H_INCLUDED //============================================================================== /** - A simple implemenation of a Logger that writes to a file. + A simple implementation of a Logger that writes to a file. @see Logger */ @@ -59,50 +58,78 @@ public: */ FileLogger (const File& fileToWriteTo, const String& welcomeMessage, - const int maxInitialFileSizeBytes = 128 * 1024); + const int64 maxInitialFileSizeBytes = 128 * 1024); /** Destructor. */ ~FileLogger(); //============================================================================== - void logMessage (const String& message); - - File getLogFile() const { return logFile; } + /** Returns the file that this logger is writing to. */ + const File& getLogFile() const noexcept { 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] + The method might return nullptr if the file can't be created for some reason. - 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 logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as + returned by getSystemLogFileFolder). It's best to use something + like the name of your application here. + @param logFileName the name of the file to create, e.g. "MyAppLog.txt". @param welcomeMessage a message that will be written to the log when it's opened. @param maxInitialFileSizeBytes (see the FileLogger constructor for more info on this) */ static FileLogger* createDefaultAppLogger (const String& logFileSubDirectoryName, const String& logFileName, const String& welcomeMessage, - const int maxInitialFileSizeBytes = 128 * 1024); + const int64 maxInitialFileSizeBytes = 128 * 1024); + + /** Helper function to create a log file in the correct place for this platform. + + The filename used is based on the root and suffix strings provided, along with a + time and date string, meaning that a new, empty log file will be always be created + rather than appending to an exising one. + + The method might return nullptr if the file can't be created for some reason. + + @param logFileSubDirectoryName the name of the subdirectory to create inside the logs folder (as + returned by getSystemLogFileFolder). It's best to use something + like the name of your application here. + @param logFileNameRoot the start of the filename to use, e.g. "MyAppLog_". This will have + a timestamp and the logFileNameSuffix appended to it + @param logFileNameSuffix the file suffix to use, e.g. ".txt" + @param welcomeMessage a message that will be written to the log when it's opened. + */ + static FileLogger* createDateStampedLogger (const String& logFileSubDirectoryName, + const String& logFileNameRoot, + const String& logFileNameSuffix, + const String& welcomeMessage); + + //============================================================================== + /** Returns an OS-specific folder where log-files should be stored. + + 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/[logFileSubDirectoryName]/[logFileName] + + @see createDefaultAppLogger + */ + static File getSystemLogFileFolder(); + + // (implementation of the Logger virtual method) + void logMessage (const String&); private: //============================================================================== File logFile; CriticalSection logLock; - void trimFileSize (int maxFileSizeBytes) const; + void trimFileSize (int64 maxFileSizeBytes) const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileLogger); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileLogger) }; -#endif // __JUCE_FILELOGGER_JUCEHEADER__ +#endif // JUCE_FILELOGGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp index 50bef88..0b1c8d6 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp +++ b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.cpp @@ -1,48 +1,44 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -Logger::Logger() -{ -} +Logger::Logger() {} Logger::~Logger() { + // You're deleting this logger while it's still being used! + // Always call Logger::setCurrentLogger (nullptr) before deleting the active logger. + jassert (currentLogger != this); } -//============================================================================== Logger* Logger::currentLogger = nullptr; -void Logger::setCurrentLogger (Logger* const newLogger, - const bool deleteOldLogger) -{ - Logger* const oldLogger = currentLogger; - currentLogger = newLogger; - - if (deleteOldLogger) - delete oldLogger; -} +void Logger::setCurrentLogger (Logger* const newLogger) noexcept { currentLogger = newLogger; } +Logger* Logger::getCurrentLogger() noexcept { return currentLogger; } void Logger::writeToLog (const String& message) { @@ -52,12 +48,16 @@ void Logger::writeToLog (const String& message) outputDebugString (message); } -#if JUCE_LOG_ASSERTIONS -void JUCE_API logAssertion (const char* filename, const int lineNum) noexcept +#if JUCE_LOG_ASSERTIONS || JUCE_DEBUG +void JUCE_API JUCE_CALLTYPE logAssertion (const char* const filename, const int lineNum) noexcept { String m ("JUCE Assertion failure in "); - m << filename << ", line " << lineNum; + m << File::createFileWithoutCheckingPath (filename).getFileName() << ':' << lineNum; + #if JUCE_LOG_ASSERTIONS Logger::writeToLog (m); + #else + DBG (m); + #endif } #endif diff --git a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h index 6bec018..d0d3af5 100644 --- a/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h +++ b/JuceLibraryCode/modules/juce_core/logging/juce_Logger.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_LOGGER_JUCEHEADER__ -#define __JUCE_LOGGER_JUCEHEADER__ - -#include "../text/juce_String.h" +#ifndef JUCE_LOGGER_H_INCLUDED +#define JUCE_LOGGER_H_INCLUDED //============================================================================== @@ -51,14 +52,14 @@ public: //============================================================================== /** Sets the current logging class to use. - Note that the object passed in won't be deleted when no longer needed. + Note that the object passed in will not be owned or deleted by the logger, so + the caller must make sure that it is not deleted while still being used. 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* newLogger, - bool deleteOldLogger = false); + static void JUCE_CALLTYPE setCurrentLogger (Logger* newLogger) noexcept; + + /** Returns the current logger, or nullptr if none has been set. */ + static Logger* getCurrentLogger() noexcept; /** Writes a string to the current logger. @@ -84,7 +85,6 @@ protected: Logger(); /** This is overloaded by subclasses to implement custom logging behaviour. - @see setCurrentLogger */ virtual void logMessage (const String& message) = 0; @@ -94,4 +94,4 @@ private: }; -#endif // __JUCE_LOGGER_JUCEHEADER__ +#endif // JUCE_LOGGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp index 0db4198..ba19414 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -154,6 +157,12 @@ int BigInteger::toInteger() const noexcept return negative ? -n : n; } +int64 BigInteger::toInt64() const noexcept +{ + const int64 n = (((int64) (values[1] & 0x7fffffff)) << 32) | values[0]; + return negative ? -n : n; +} + BigInteger BigInteger::getBitRange (int startBit, int numBits) const { BigInteger r; @@ -942,7 +951,7 @@ String BigInteger::toString (const int base, const int minimumNumCharacters) con else { jassertfalse; // can't do the specified base! - return String::empty; + return String(); } s = s.paddedLeft ('0', minimumNumCharacters); @@ -950,10 +959,12 @@ String BigInteger::toString (const int base, const int minimumNumCharacters) con return isNegative() ? "-" + s : s; } -void BigInteger::parseString (const String& text, const int base) +void BigInteger::parseString (StringRef text, const int base) { clear(); - String::CharPointerType t (text.getCharPointer()); + String::CharPointerType t (text.text.findEndOfWhitespace()); + + setNegative (*t == (juce_wchar) '-'); if (base == 2 || base == 8 || base == 16) { @@ -994,8 +1005,6 @@ void BigInteger::parseString (const String& text, const int base) } } } - - setNegative (text.trimStart().startsWithChar ('-')); } MemoryBlock BigInteger::toMemoryBlock() const diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h index e22ebc7..4c6c25c 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_BIGINTEGER_JUCEHEADER__ -#define __JUCE_BIGINTEGER_JUCEHEADER__ - -#include "../text/juce_String.h" -#include "../memory/juce_HeapBlock.h" -class MemoryBlock; +#ifndef JUCE_BIGINTEGER_H_INCLUDED +#define JUCE_BIGINTEGER_H_INCLUDED //============================================================================== @@ -49,31 +48,28 @@ public: 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 (uint32 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 (int32 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); + BigInteger (const BigInteger&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - BigInteger (BigInteger&& other) noexcept; - BigInteger& operator= (BigInteger&& other) noexcept; + BigInteger (BigInteger&&) noexcept; + BigInteger& operator= (BigInteger&&) noexcept; #endif /** Destructor. */ @@ -81,10 +77,10 @@ public: //============================================================================== /** Copies another BigInteger onto this one. */ - BigInteger& operator= (const BigInteger& other); + BigInteger& operator= (const BigInteger&); /** Swaps the internal contents of this with another object. */ - void swapWith (BigInteger& other) noexcept; + void swapWith (BigInteger&) noexcept; //============================================================================== /** Returns the value of a specified bit in the number. @@ -98,11 +94,16 @@ public: /** Returns true if the value is 1. */ bool isOne() const noexcept; - /** Attempts to get the lowest bits of the value as an integer. + /** Attempts to get the lowest 32 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 noexcept; + /** Attempts to get the lowest 64 bits of the value as an integer. + If the value is bigger than the integer limits, this will return only the lower bits. + */ + int64 toInt64() const noexcept; + //============================================================================== /** Resets the value to 0. */ void clear(); @@ -165,14 +166,14 @@ public: 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 noexcept; + int findNextSetBit (int startIndex) const noexcept; /** 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 noexcept; + int findNextClearBit (int startIndex) const noexcept; /** Returns the index of the highest set bit in the number. If the value is zero, this will return -1. @@ -182,14 +183,14 @@ public: //============================================================================== // All the standard arithmetic ops... - BigInteger& operator+= (const BigInteger& other); - BigInteger& operator-= (const BigInteger& other); - BigInteger& operator*= (const BigInteger& other); - BigInteger& operator/= (const BigInteger& other); - BigInteger& operator|= (const BigInteger& other); - BigInteger& operator&= (const BigInteger& other); - BigInteger& operator^= (const BigInteger& other); - BigInteger& operator%= (const BigInteger& other); + BigInteger& operator+= (const BigInteger&); + BigInteger& operator-= (const BigInteger&); + BigInteger& operator*= (const BigInteger&); + BigInteger& operator/= (const BigInteger&); + BigInteger& operator|= (const BigInteger&); + BigInteger& operator&= (const BigInteger&); + BigInteger& operator^= (const BigInteger&); + BigInteger& operator%= (const BigInteger&); BigInteger& operator<<= (int numBitsToShift); BigInteger& operator>>= (int numBitsToShift); BigInteger& operator++(); @@ -198,23 +199,23 @@ public: BigInteger operator-- (int); BigInteger operator-() const; - BigInteger operator+ (const BigInteger& other) const; - BigInteger operator- (const BigInteger& other) const; - BigInteger operator* (const BigInteger& other) const; - BigInteger operator/ (const BigInteger& other) const; - BigInteger operator| (const BigInteger& other) const; - BigInteger operator& (const BigInteger& other) const; - BigInteger operator^ (const BigInteger& other) const; - BigInteger operator% (const BigInteger& other) const; + BigInteger operator+ (const BigInteger&) const; + BigInteger operator- (const BigInteger&) const; + BigInteger operator* (const BigInteger&) const; + BigInteger operator/ (const BigInteger&) const; + BigInteger operator| (const BigInteger&) const; + BigInteger operator& (const BigInteger&) const; + BigInteger operator^ (const BigInteger&) const; + BigInteger operator% (const BigInteger&) const; BigInteger operator<< (int numBitsToShift) const; BigInteger operator>> (int numBitsToShift) const; - bool operator== (const BigInteger& other) const noexcept; - bool operator!= (const BigInteger& other) const noexcept; - bool operator< (const BigInteger& other) const noexcept; - bool operator<= (const BigInteger& other) const noexcept; - bool operator> (const BigInteger& other) const noexcept; - bool operator>= (const BigInteger& other) const noexcept; + bool operator== (const BigInteger&) const noexcept; + bool operator!= (const BigInteger&) const noexcept; + bool operator< (const BigInteger&) const noexcept; + bool operator<= (const BigInteger&) const noexcept; + bool operator> (const BigInteger&) const noexcept; + bool operator>= (const BigInteger&) const noexcept; //============================================================================== /** Does a signed comparison of two BigIntegers. @@ -242,18 +243,15 @@ public: */ void divideBy (const BigInteger& divisor, BigInteger& remainder); - /** Returns the largest value that will divide both this value and the one passed-in. - */ + /** Returns the largest value that will divide both this value and the one passed-in. */ 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); @@ -288,7 +286,7 @@ public: 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); + void parseString (StringRef text, int base); //============================================================================== /** Turns the number into a block of binary data. @@ -311,16 +309,16 @@ public: private: //============================================================================== - HeapBlock values; + HeapBlock values; size_t numValues; int highestBit; bool negative; - void ensureSize (size_t numVals); + void ensureSize (size_t); void shiftLeft (int bits, int startBit); void shiftRight (int bits, int startBit); - JUCE_LEAK_DETECTOR (BigInteger); + JUCE_LEAK_DETECTOR (BigInteger) }; /** Writes a BigInteger to an OutputStream as a UTF8 decimal string. */ @@ -333,4 +331,4 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const BigInteger& #endif -#endif // __JUCE_BIGINTEGER_JUCEHEADER__ +#endif // JUCE_BIGINTEGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp index 67bb6fb..a18effa 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -44,13 +47,13 @@ public: double /*overallTarget*/, Term* /*topLevelTerm*/) const { jassertfalse; - return nullptr; + return ReferenceCountedObjectPtr(); } virtual String getName() const { jassertfalse; // You shouldn't call this for an expression that's not actually a function! - return String::empty; + return String(); } virtual void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) @@ -73,7 +76,7 @@ public: } private: - JUCE_DECLARE_NON_COPYABLE (Term); + JUCE_DECLARE_NON_COPYABLE (Term) }; @@ -95,8 +98,7 @@ struct Expression::Helpers class EvaluationError : public std::exception { public: - EvaluationError (const String& description_) - : description (description_) + EvaluationError (const String& desc) : description (desc) { DBG ("Expression::EvaluationError: " + description); } @@ -108,8 +110,8 @@ struct Expression::Helpers class Constant : public Term { public: - Constant (const double value_, const bool isResolutionTarget_) - : value (value_), isResolutionTarget (isResolutionTarget_) {} + Constant (const double val, const bool resolutionTarget) + : value (val), isResolutionTarget (resolutionTarget) {} Type getType() const noexcept { return constantType; } Term* clone() const { return new Constant (value, isResolutionTarget); } @@ -146,7 +148,7 @@ struct Expression::Helpers Type getType() const noexcept { return operatorType; } int getNumInputs() const { return 2; } - Term* getInput (int index) const { return index == 0 ? left.getObject() : (index == 1 ? right.getObject() : 0); } + Term* getInput (int index) const { return index == 0 ? left.get() : (index == 1 ? right.get() : 0); } virtual double performFunction (double left, double right) const = 0; virtual void writeOperator (String& dest) const = 0; @@ -184,14 +186,12 @@ struct Expression::Helpers { jassert (input == left || input == right); if (input != left && input != right) - return nullptr; + return TermPtr(); - const Term* const dest = findDestinationFor (topLevelTerm, this); + if (const Term* const dest = findDestinationFor (topLevelTerm, this)) + return dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm); - if (dest == nullptr) - return new Constant (overallTarget, false); - - return dest->createTermToEvaluateInput (scope, this, overallTarget, topLevelTerm); + return new Constant (overallTarget, false); } }; @@ -199,7 +199,7 @@ struct Expression::Helpers class SymbolTerm : public Term { public: - explicit SymbolTerm (const String& symbol_) : symbol (symbol_) {} + explicit SymbolTerm (const String& sym) : symbol (sym) {} TermPtr resolve (const Scope& scope, int recursionDepth) { @@ -232,10 +232,10 @@ struct Expression::Helpers class Function : public Term { public: - explicit Function (const String& functionName_) : functionName (functionName_) {} + explicit Function (const String& name) : functionName (name) {} - Function (const String& functionName_, const Array& parameters_) - : functionName (functionName_), parameters (parameters_) + Function (const String& name, const Array& params) + : functionName (name), parameters (params) {} Type getType() const noexcept { return functionType; } @@ -351,8 +351,8 @@ struct Expression::Helpers class EvaluationVisitor : public Scope::Visitor { public: - EvaluationVisitor (const TermPtr& input_, const int recursionCount_) - : input (input_), output (input_), recursionCount (recursionCount_) {} + EvaluationVisitor (const TermPtr& t, const int recursion) + : input (t), output (t), recursionCount (recursion) {} void visit (const Scope& scope) { output = input->resolve (scope, recursionCount); } @@ -361,14 +361,14 @@ struct Expression::Helpers const int recursionCount; private: - JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor); + JUCE_DECLARE_NON_COPYABLE (EvaluationVisitor) }; class SymbolVisitingVisitor : public Scope::Visitor { public: - SymbolVisitingVisitor (const TermPtr& input_, SymbolVisitor& visitor_, const int recursionCount_) - : input (input_), visitor (visitor_), recursionCount (recursionCount_) {} + SymbolVisitingVisitor (const TermPtr& t, SymbolVisitor& v, const int recursion) + : input (t), visitor (v), recursionCount (recursion) {} void visit (const Scope& scope) { input->visitAllSymbols (visitor, scope, recursionCount); } @@ -377,14 +377,14 @@ struct Expression::Helpers SymbolVisitor& visitor; const int recursionCount; - JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor); + JUCE_DECLARE_NON_COPYABLE (SymbolVisitingVisitor) }; class SymbolRenamingVisitor : public Scope::Visitor { public: - SymbolRenamingVisitor (const TermPtr& input_, const Expression::Symbol& symbol_, const String& newName_, const int recursionCount_) - : input (input_), symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {} + SymbolRenamingVisitor (const TermPtr& t, const Expression::Symbol& symbol_, const String& newName_, const int recursionCount_) + : input (t), symbol (symbol_), newName (newName_), recursionCount (recursionCount_) {} void visit (const Scope& scope) { input->renameSymbol (symbol, newName, scope, recursionCount); } @@ -394,27 +394,27 @@ struct Expression::Helpers const String newName; const int recursionCount; - JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor); + JUCE_DECLARE_NON_COPYABLE (SymbolRenamingVisitor) }; - SymbolTerm* getSymbol() const { return static_cast (left.getObject()); } + SymbolTerm* getSymbol() const { return static_cast (left.get()); } - JUCE_DECLARE_NON_COPYABLE (DotOperator); + JUCE_DECLARE_NON_COPYABLE (DotOperator) }; //============================================================================== class Negate : public Term { public: - explicit Negate (const TermPtr& input_) : input (input_) + explicit Negate (const TermPtr& t) : input (t) { - jassert (input_ != nullptr); + jassert (t != nullptr); } Type getType() const noexcept { return operatorType; } int getInputIndexFor (const Term* possibleInput) const { return possibleInput == input ? 0 : -1; } int getNumInputs() const { return 1; } - Term* getInput (int index) const { return index == 0 ? input.getObject() : nullptr; } + Term* getInput (int index) const { return index == 0 ? input.get() : nullptr; } Term* clone() const { return new Negate (input->clone()); } TermPtr resolve (const Scope& scope, int recursionDepth) @@ -425,10 +425,10 @@ struct Expression::Helpers String getName() const { return "-"; } TermPtr negated() { return input; } - TermPtr createTermToEvaluateInput (const Scope& scope, const Term* input_, double overallTarget, Term* topLevelTerm) const + TermPtr createTermToEvaluateInput (const Scope& scope, const Term* t, double overallTarget, Term* topLevelTerm) const { - (void) input_; - jassert (input_ == input); + (void) t; + jassert (t == input); const Term* const dest = findDestinationFor (topLevelTerm, this); @@ -440,8 +440,8 @@ struct Expression::Helpers { if (input->getOperatorPrecedence() > 0) return "-(" + input->toString() + ")"; - else - return "-" + input->toString(); + + return "-" + input->toString(); } private: @@ -464,13 +464,13 @@ struct Expression::Helpers { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) - return nullptr; + return TermPtr(); return new Subtract (newDest, (input == left ? right : left)->clone()); } private: - JUCE_DECLARE_NON_COPYABLE (Add); + JUCE_DECLARE_NON_COPYABLE (Add) }; //============================================================================== @@ -489,16 +489,16 @@ struct Expression::Helpers { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) - return nullptr; + return TermPtr(); if (input == left) return new Add (newDest, right->clone()); - else - return new Subtract (left->clone(), newDest); + + return new Subtract (left->clone(), newDest); } private: - JUCE_DECLARE_NON_COPYABLE (Subtract); + JUCE_DECLARE_NON_COPYABLE (Subtract) }; //============================================================================== @@ -517,13 +517,13 @@ struct Expression::Helpers { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) - return nullptr; + return TermPtr(); return new Divide (newDest, (input == left ? right : left)->clone()); } private: - JUCE_DECLARE_NON_COPYABLE (Multiply); + JUCE_DECLARE_NON_COPYABLE (Multiply) }; //============================================================================== @@ -542,16 +542,16 @@ struct Expression::Helpers { const TermPtr newDest (createDestinationTerm (scope, input, overallTarget, topLevelTerm)); if (newDest == nullptr) - return nullptr; + return TermPtr(); if (input == left) return new Multiply (newDest, right->clone()); - else - return new Divide (left->clone(), newDest); + + return new Divide (left->clone(), newDest); } private: - JUCE_DECLARE_NON_COPYABLE (Divide); + JUCE_DECLARE_NON_COPYABLE (Divide) }; //============================================================================== @@ -635,7 +635,7 @@ struct Expression::Helpers private: const Symbol& symbol; - JUCE_DECLARE_NON_COPYABLE (SymbolCheckVisitor); + JUCE_DECLARE_NON_COPYABLE (SymbolCheckVisitor) }; //============================================================================== @@ -648,7 +648,7 @@ struct Expression::Helpers private: Array& list; - JUCE_DECLARE_NON_COPYABLE (SymbolListVisitor); + JUCE_DECLARE_NON_COPYABLE (SymbolListVisitor) }; //============================================================================== @@ -814,15 +814,15 @@ struct Expression::Helpers char opType; if (readOperator ("+-", &opType)) { - TermPtr term (readUnaryExpression()); + TermPtr e (readUnaryExpression()); - if (term == nullptr) + if (e == nullptr) throw ParseError ("Expected expression after \"" + String::charToString ((juce_wchar) (uint8) opType) + "\""); if (opType == '-') - term = term->negated(); + e = e->negated(); - return term; + return e; } return readPrimaryExpression(); @@ -878,7 +878,8 @@ struct Expression::Helpers throw ParseError ("Expected \")\""); } - else if (readOperator (".")) + + if (readOperator (".")) { TermPtr rhs (readSymbolOrFunction()); @@ -890,29 +891,28 @@ struct Expression::Helpers return new DotOperator (new SymbolTerm (identifier), rhs); } - else // just a symbol.. - { - jassert (identifier.trim() == identifier); - return new SymbolTerm (identifier); - } + + // just a symbol.. + jassert (identifier.trim() == identifier); + return new SymbolTerm (identifier); } - return nullptr; + return TermPtr(); } TermPtr readParenthesisedExpression() { if (! readOperator ("(")) - return nullptr; + return TermPtr(); const TermPtr e (readExpression()); if (e == nullptr || ! readOperator (")")) - return nullptr; + return TermPtr(); return e; } - JUCE_DECLARE_NON_COPYABLE (Parser); + JUCE_DECLARE_NON_COPYABLE (Parser) }; }; @@ -1132,7 +1132,10 @@ Expression::Scope::~Scope() {} Expression Expression::Scope::getSymbolValue (const String& symbol) const { - throw Helpers::EvaluationError ("Unknown symbol: " + symbol); + if (symbol.isNotEmpty()) + throw Helpers::EvaluationError ("Unknown symbol: " + symbol); + + return Expression(); } double Expression::Scope::evaluateFunction (const String& functionName, const double* parameters, int numParams) const @@ -1147,7 +1150,8 @@ double Expression::Scope::evaluateFunction (const String& functionName, const do return v; } - else if (functionName == "max") + + if (functionName == "max") { double v = parameters[0]; for (int i = 1; i < numParams; ++i) @@ -1155,12 +1159,13 @@ double Expression::Scope::evaluateFunction (const String& functionName, const do return v; } - else if (numParams == 1) + + if (numParams == 1) { - if (functionName == "sin") return sin (parameters[0]); - else if (functionName == "cos") return cos (parameters[0]); - else if (functionName == "tan") return tan (parameters[0]); - else if (functionName == "abs") return std::abs (parameters[0]); + if (functionName == "sin") return sin (parameters[0]); + if (functionName == "cos") return cos (parameters[0]); + if (functionName == "tan") return tan (parameters[0]); + if (functionName == "abs") return std::abs (parameters[0]); } } @@ -1174,5 +1179,5 @@ void Expression::Scope::visitRelativeScope (const String& scopeName, Visitor&) c String Expression::Scope::getScopeUID() const { - return String::empty; + return String(); } diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h index da2c5e5..8939129 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_EXPRESSION_JUCEHEADER__ -#define __JUCE_EXPRESSION_JUCEHEADER__ - -#include "../memory/juce_ReferenceCountedObject.h" -#include "../containers/juce_Array.h" -#include "../memory/juce_ScopedPointer.h" +#ifndef JUCE_EXPRESSION_H_INCLUDED +#define JUCE_EXPRESSION_H_INCLUDED //============================================================================== @@ -60,14 +59,14 @@ public: explicit Expression (double constant); /** Creates a copy of an expression. */ - Expression (const Expression& other); + Expression (const Expression&); /** Copies another expression. */ - Expression& operator= (const Expression& other); + Expression& operator= (const Expression&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - Expression (Expression&& other) noexcept; - Expression& operator= (Expression&& other) noexcept; + Expression (Expression&&) noexcept; + Expression& operator= (Expression&&) noexcept; #endif /** Creates an expression by parsing a string. @@ -79,14 +78,14 @@ public: /** Returns a string version of the expression. */ String toString() const; - /** Returns an expression which is an addtion operation of two existing expressions. */ - Expression operator+ (const Expression& other) const; + /** Returns an expression which is an addition operation of two existing expressions. */ + Expression operator+ (const Expression&) const; /** Returns an expression which is a subtraction operation of two existing expressions. */ - Expression operator- (const Expression& other) const; + Expression operator- (const Expression&) const; /** Returns an expression which is a multiplication operation of two existing expressions. */ - Expression operator* (const Expression& other) const; + Expression operator* (const Expression&) const; /** Returns an expression which is a division operation of two existing expressions. */ - Expression operator/ (const Expression& other) const; + Expression operator/ (const Expression&) const; /** Returns an expression which performs a negation operation on an existing expression. */ Expression operator-() const; @@ -261,11 +260,11 @@ private: struct Helpers; friend class Term; friend struct Helpers; - friend class ScopedPointer; + friend struct ContainerDeletePolicy; friend class ReferenceCountedObjectPtr; ReferenceCountedObjectPtr term; explicit Expression (Term*); }; -#endif // __JUCE_EXPRESSION_JUCEHEADER__ +#endif // JUCE_EXPRESSION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h index b436f52..3469c6c 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_MATHSFUNCTIONS_JUCEHEADER__ -#define __JUCE_MATHSFUNCTIONS_JUCEHEADER__ +#ifndef JUCE_MATHSFUNCTIONS_H_INCLUDED +#define JUCE_MATHSFUNCTIONS_H_INCLUDED //============================================================================== /* @@ -52,27 +55,21 @@ typedef unsigned int uint32; 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 +#ifndef DOXYGEN + /** A macro for creating 64-bit literals. + Historically, this was needed to support portability with MSVC6, and is kept here + so that old code will still compile, but nowadays every compiler will support the + LL and ULL suffixes, so you should use those in preference to this macro. + */ + #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. */ @@ -285,7 +282,7 @@ inline int numElementsInArray (Type (&array)[N]) template inline Type juce_hypot (Type a, Type b) noexcept { - #if JUCE_WINDOWS + #if JUCE_MSVC return static_cast (_hypot (a, b)); #else return static_cast (hypot (a, b)); @@ -298,15 +295,19 @@ inline int64 abs64 (const int64 n) noexcept return (n >= 0) ? n : -n; } +#if JUCE_MSVC && ! defined (DOXYGEN) // The MSVC libraries omit these functions for some reason... + template Type asinh (Type x) noexcept { return std::log (x + std::sqrt (x * x + (Type) 1)); } + template Type acosh (Type x) noexcept { return std::log (x + std::sqrt (x * x - (Type) 1)); } + template Type atanh (Type x) noexcept { return (std::log (x + (Type) 1) - std::log (((Type) 1) - x)) / (Type) 2; } +#endif + //============================================================================== /** 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. - +/** A predefined value for Pi, at single-precision. @see double_Pi */ const float float_Pi = 3.14159265358979323846f; @@ -331,7 +332,9 @@ inline bool juce_isfinite (FloatingPointType value) //============================================================================== #if JUCE_MSVC #pragma optimize ("t", off) - #pragma float_control (precise, on, push) + #ifndef __INTEL_COMPILER + #pragma float_control (precise, on, push) + #endif #endif /** Fast floating-point-to-integer conversion. @@ -347,6 +350,10 @@ inline bool juce_isfinite (FloatingPointType value) template inline int roundToInt (const FloatType value) noexcept { + #ifdef __INTEL_COMPILER + #pragma float_control (precise, on, push) + #endif + union { int asInt[2]; double asDouble; } n; n.asDouble = ((double) value) + 6755399441055744.0; @@ -358,7 +365,9 @@ inline int roundToInt (const FloatType value) noexcept } #if JUCE_MSVC - #pragma float_control (pop) + #ifndef __INTEL_COMPILER + #pragma float_control (pop) + #endif #pragma optimize ("", on) // resets optimisations to the project defaults #endif @@ -369,6 +378,10 @@ inline int roundToInt (const FloatType value) noexcept */ inline int roundToIntAccurate (const double value) noexcept { + #ifdef __INTEL_COMPILER + #pragma float_control (pop) + #endif + return roundToInt (value + 1.5e-8); } @@ -429,13 +442,20 @@ inline int nextPowerOfTwo (int n) noexcept The divisor must be greater than zero. */ template -int negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept +IntegerType negativeAwareModulo (IntegerType dividend, const IntegerType divisor) noexcept { jassert (divisor > 0); dividend %= divisor; return (dividend < 0) ? (dividend + divisor) : dividend; } +/** Returns the square of its argument. */ +template +NumericType square (NumericType n) noexcept +{ + return n * n; +} + //============================================================================== #if (JUCE_INTEL && JUCE_32BIT) || defined (DOXYGEN) /** This macro can be applied to a float variable to check whether it contains a denormalised @@ -504,4 +524,4 @@ namespace TypeHelpers //============================================================================== -#endif // __JUCE_MATHSFUNCTIONS_JUCEHEADER__ +#endif // JUCE_MATHSFUNCTIONS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp index ef6447a..a196256 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Random.cpp @@ -1,35 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -Random::Random (const int64 seedValue) noexcept - : seed (seedValue) +Random::Random (const int64 seedValue) noexcept : seed (seedValue) { } -Random::Random() - : seed (1) +Random::Random() : seed (1) { setSeedRandomly(); } @@ -69,7 +70,7 @@ Random& Random::getSystemRandom() noexcept //============================================================================== int Random::nextInt() noexcept { - seed = (seed * literal64bit (0x5deece66d) + 11) & literal64bit (0xffffffffffff); + seed = (seed * 0x5deece66dLL + 11) & 0xffffffffffffLL; return (int) (seed >> 16); } @@ -80,6 +81,11 @@ int Random::nextInt (const int maxValue) noexcept return (int) ((((unsigned int) nextInt()) * (uint64) maxValue) >> 32); } +int Random::nextInt (Range range) noexcept +{ + return range.getStart() + nextInt (range.getLength()); +} + int64 Random::nextInt64() noexcept { return (((int64) nextInt()) << 32) | (int64) (uint64) (uint32) nextInt(); @@ -92,12 +98,12 @@ bool Random::nextBool() noexcept float Random::nextFloat() noexcept { - return static_cast (nextInt()) / (float) 0xffffffff; + return static_cast (nextInt()) / (std::numeric_limits::max() + 1.0f); } double Random::nextDouble() noexcept { - return static_cast (nextInt()) / (double) 0xffffffff; + return static_cast (nextInt()) / (std::numeric_limits::max() + 1.0); } BigInteger Random::nextLargeNumber (const BigInteger& maximumValue) @@ -113,6 +119,20 @@ BigInteger Random::nextLargeNumber (const BigInteger& maximumValue) return n; } +void Random::fillBitsRandomly (void* const buffer, size_t bytes) +{ + int* d = static_cast (buffer); + + for (; bytes >= sizeof (int); bytes -= sizeof (int)) + *d++ = nextInt(); + + if (bytes > 0) + { + const int lastBytes = nextInt(); + memcpy (d, &lastBytes, bytes); + } +} + void Random::fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits) { arrayToChange.setBit (startBit + numBits - 1, true); // to force the array to pre-allocate space @@ -146,24 +166,20 @@ public: { beginTest ("Random"); - for (int j = 10; --j >= 0;) + Random r = getRandom(); + + for (int i = 2000; --i >= 0;) { - Random r; - r.setSeedRandomly(); + expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); + expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); + expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); + expect (r.nextInt (1) == 0); - for (int i = 20; --i >= 0;) - { - expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); - expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); - expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); - expect (r.nextInt (1) == 0); + int n = r.nextInt (50) + 1; + expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); - int n = r.nextInt (50) + 1; - expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); - - n = r.nextInt (0x7ffffffe) + 1; - expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); - } + n = r.nextInt (0x7ffffffe) + 1; + expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); } } }; diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Random.h b/JuceLibraryCode/modules/juce_core/maths/juce_Random.h index b2cc04f..19e6d6e 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Random.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Random.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_RANDOM_JUCEHEADER__ -#define __JUCE_RANDOM_JUCEHEADER__ - -#include "juce_BigInteger.h" +#ifndef JUCE_RANDOM_H_INCLUDED +#define JUCE_RANDOM_H_INCLUDED //============================================================================== @@ -58,7 +59,6 @@ public: ~Random() noexcept; /** Returns the next random 32 bit integer. - @returns a random integer from the full range 0x80000000 to 0x7fffffff */ int nextInt() noexcept; @@ -69,34 +69,37 @@ public: */ int nextInt (int maxValue) noexcept; - /** Returns the next 64-bit random number. + /** Returns the next random number, limited to a given range. + @returns a random integer between the range start (inclusive) and its end (exclusive). + */ + int nextInt (Range range) noexcept; + /** Returns the next 64-bit random number. @returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff */ int64 nextInt64() noexcept; /** Returns the next random floating-point number. - @returns a random value in the range 0 to 1.0 */ float nextFloat() noexcept; /** Returns the next random floating-point number. - @returns a random value in the range 0 to 1.0 */ double nextDouble() noexcept; - /** Returns the next random boolean value. - */ + /** Returns the next random boolean value. */ bool nextBool() noexcept; /** Returns a BigInteger containing a random number. - @returns a random value in the range 0 to (maximumValue - 1). */ BigInteger nextLargeNumber (const BigInteger& maximumValue); + /** Fills a block of memory with random values. */ + void fillBitsRandomly (void* bufferToFill, size_t sizeInBytes); + /** Sets a range of bits in a BigInteger to random values. */ void fillBitsRandomly (BigInteger& arrayToChange, int startBit, int numBits); @@ -104,6 +107,9 @@ public: /** Resets this Random object to a given seed value. */ void setSeed (int64 newSeed) noexcept; + /** Returns the RNG's current seed. */ + int64 getSeed() const noexcept { return seed; } + /** 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. @@ -130,8 +136,8 @@ private: //============================================================================== int64 seed; - JUCE_LEAK_DETECTOR (Random); + JUCE_LEAK_DETECTOR (Random) }; -#endif // __JUCE_RANDOM_JUCEHEADER__ +#endif // JUCE_RANDOM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Range.h b/JuceLibraryCode/modules/juce_core/maths/juce_Range.h index 14a4699..0aa9be4 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Range.h +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Range.h @@ -1,36 +1,42 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_RANGE_JUCEHEADER__ -#define __JUCE_RANGE_JUCEHEADER__ +#ifndef JUCE_RANGE_H_INCLUDED +#define JUCE_RANGE_H_INCLUDED //============================================================================== /** A general-purpose range object, that simply represents any linear range with a start and end point. + Note that when checking whether values fall within the range, the start value is + considered to be inclusive, and the end of the range exclusive. + 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. */ @@ -40,14 +46,13 @@ class Range public: //============================================================================== /** Constructs an empty range. */ - Range() noexcept - : start (ValueType()), end (ValueType()) + Range() noexcept : start(), end() { } /** Constructs a range with given start and end values. */ - Range (const ValueType start_, const ValueType end_) noexcept - : start (start_), end (jmax (start_, end_)) + Range (const ValueType startValue, const ValueType endValue) noexcept + : start (startValue), end (jmax (startValue, endValue)) { } @@ -58,23 +63,18 @@ public: } /** Copies another range object. */ - Range& operator= (const Range& other) noexcept + Range& operator= (Range other) noexcept { start = other.start; end = other.end; return *this; } - /** Destructor. */ - ~Range() noexcept - { - } - /** Returns the range that lies between two positions (in either order). */ static Range between (const ValueType position1, const ValueType position2) noexcept { - return (position1 < position2) ? Range (position1, position2) - : Range (position2, position1); + return position1 < position2 ? Range (position1, position2) + : Range (position2, position1); } /** Returns a range with the specified start position and a length of zero. */ @@ -143,7 +143,7 @@ public: return Range (jmin (start, newEnd), newEnd); } - /** Returns a range with the same length as this one, but moved to have the given start position. */ + /** Returns a range with the same length as this one, but moved to have the given end position. */ Range movedToEndAt (const ValueType newEnd) const noexcept { return Range (start + (newEnd - end), newEnd); @@ -167,7 +167,7 @@ public: //============================================================================== /** Adds an amount to the start and end of the range. */ - inline const Range& operator+= (const ValueType amountToAdd) noexcept + inline Range operator+= (const ValueType amountToAdd) noexcept { start += amountToAdd; end += amountToAdd; @@ -175,7 +175,7 @@ public: } /** Subtracts an amount from the start and end of the range. */ - inline const Range& operator-= (const ValueType amountToSubtract) noexcept + inline Range operator-= (const ValueType amountToSubtract) noexcept { start -= amountToSubtract; end -= amountToSubtract; @@ -197,8 +197,8 @@ public: return Range (start - amountToSubtract, end - amountToSubtract); } - bool operator== (const Range& other) const noexcept { return start == other.start && end == other.end; } - bool operator!= (const Range& other) const noexcept { return start != other.start || end != other.end; } + bool operator== (Range other) const noexcept { return start == other.start && end == other.end; } + bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; } //============================================================================== /** Returns true if the given position lies inside this range. */ @@ -213,33 +213,43 @@ public: return jlimit (start, end, value); } - /** Returns true if the given range lies entirely inside this range. */ - bool contains (const Range& other) const noexcept + /** Returns true if the given range lies entirely inside this range. + When making this comparison, the start value is considered to be inclusive, + and the end of the range exclusive. + */ + bool contains (Range other) const noexcept { return start <= other.start && end >= other.end; } /** Returns true if the given range intersects this one. */ - bool intersects (const Range& other) const noexcept + bool intersects (Range other) const noexcept { 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. */ - Range getIntersectionWith (const Range& other) const noexcept + Range getIntersectionWith (Range other) const noexcept { return Range (jmax (start, other.start), jmin (end, other.end)); } /** Returns the smallest range that contains both this one and the other one. */ - Range getUnionWith (const Range& other) const noexcept + Range getUnionWith (Range other) const noexcept { return Range (jmin (start, other.start), jmax (end, other.end)); } + /** Returns the smallest range that contains both this one and the given value. */ + Range getUnionWith (const ValueType valueToInclude) const noexcept + { + return Range (jmin (valueToInclude, start), + jmax (valueToInclude, end)); + } + /** Returns a given range, after moving it forwards or backwards to fit it within this range. @@ -250,7 +260,7 @@ public: will be the new range, shifted forwards or backwards so that it doesn't extend beyond this one, but keeping its original length. */ - Range constrainRange (const Range& rangeToConstrain) const noexcept + Range constrainRange (Range rangeToConstrain) const noexcept { const ValueType otherLen = rangeToConstrain.getLength(); return getLength() <= otherLen @@ -258,10 +268,30 @@ public: : rangeToConstrain.movedToStartAt (jlimit (start, end - otherLen, rangeToConstrain.getStart())); } + /** Scans an array of values for its min and max, and returns these as a Range. */ + static Range findMinAndMax (const ValueType* values, int numValues) noexcept + { + if (numValues <= 0) + return Range(); + + const ValueType first (*values++); + Range r (first, first); + + while (--numValues > 0) // (> 0 rather than >= 0 because we've already taken the first sample) + { + const ValueType v (*values++); + + if (r.end < v) r.end = v; + if (v < r.start) r.start = v; + } + + return r; + } + private: //============================================================================== ValueType start, end; }; -#endif // __JUCE_RANGE_JUCEHEADER__ +#endif // JUCE_RANGE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h b/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h index 7bc4dd6..d0dd6be 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Atomic.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_ATOMIC_JUCEHEADER__ -#define __JUCE_ATOMIC_JUCEHEADER__ +#ifndef JUCE_ATOMIC_H_INCLUDED +#define JUCE_ATOMIC_H_INCLUDED //============================================================================== @@ -45,7 +48,7 @@ public: } /** Creates a new value, with a given initial value. */ - inline Atomic (const Type initialValue) noexcept + inline explicit Atomic (const Type initialValue) noexcept : value (initialValue) { } @@ -150,10 +153,13 @@ public: volatile Type value; private: - static inline Type castFrom32Bit (int32 value) noexcept { return *(Type*) &value; } - static inline Type castFrom64Bit (int64 value) noexcept { return *(Type*) &value; } - static inline int32 castTo32Bit (Type value) noexcept { return *(int32*) &value; } - static inline int64 castTo64Bit (Type value) noexcept { return *(int64*) &value; } + template + static inline Dest castTo (Source value) noexcept { union { Dest d; Source s; } u; u.s = value; return u.d; } + + static inline Type castFrom32Bit (int32 value) noexcept { return castTo (value); } + static inline Type castFrom64Bit (int64 value) noexcept { return castTo (value); } + static inline int32 castTo32Bit (Type value) noexcept { return castTo (value); } + static inline int64 castTo64Bit (Type value) noexcept { return castTo (value); } Type operator++ (int); // better to just use pre-increment with atomics.. Type operator-- (int); @@ -181,8 +187,8 @@ private: /* The following code is in the header so that the atomics can be inlined where possible... */ -#if JUCE_IOS || (JUCE_MAC && (JUCE_PPC || defined (__clang__) || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) - #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier +#if JUCE_MAC && (JUCE_PPC || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2)) + #define JUCE_ATOMICS_MAC_LEGACY 1 // Older OSX builds using gcc4.1 or earlier #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 #define JUCE_MAC_ATOMICS_VOLATILE @@ -190,8 +196,8 @@ private: #define JUCE_MAC_ATOMICS_VOLATILE volatile #endif - #if JUCE_PPC || JUCE_IOS - // None of these atomics are available for PPC or for iPhoneOS 3.1 or earlier!! + #if JUCE_PPC + // None of these atomics are available for PPC or for iOS 3.1 or earlier!! template static Type OSAtomicAdd64Barrier (Type b, JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return *a += b; } template static Type OSAtomicIncrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return ++*a; } template static Type OSAtomicDecrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) noexcept { jassertfalse; return --*a; } @@ -201,7 +207,7 @@ private: #endif //============================================================================== -#elif JUCE_GCC +#elif JUCE_GCC || JUCE_CLANG #define JUCE_ATOMICS_GCC 1 // GCC with intrinsics #if JUCE_IOS || JUCE_ANDROID // (64-bit ops will compile but not link on these mobile OSes) @@ -252,6 +258,7 @@ private: #endif #endif + #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4311) // (truncation warning) @@ -261,7 +268,7 @@ private: template inline Type Atomic::get() const noexcept { - #if JUCE_ATOMICS_MAC + #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value)) : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value)); #elif JUCE_ATOMICS_WINDOWS @@ -276,7 +283,7 @@ inline Type Atomic::get() const noexcept template inline Type Atomic::exchange (const Type newValue) noexcept { - #if JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC + #if JUCE_ATOMICS_MAC_LEGACY || JUCE_ATOMICS_GCC Type currentVal = value; while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } return currentVal; @@ -289,7 +296,7 @@ inline Type Atomic::exchange (const Type newValue) noexcept template inline Type Atomic::operator+= (const Type amountToAdd) noexcept { - #if JUCE_ATOMICS_MAC + #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amountToAdd), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : (Type) OSAtomicAdd64Barrier ((int64_t) amountToAdd, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS @@ -309,35 +316,37 @@ inline Type Atomic::operator-= (const Type amountToSubtract) noexcept template inline Type Atomic::operator++() noexcept { - #if JUCE_ATOMICS_MAC + #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : (Type) OSAtomicIncrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedIncrement ((volatile long*) &value) : (Type) juce_InterlockedIncrement64 ((volatile __int64*) &value); #elif JUCE_ATOMICS_GCC - return (Type) __sync_add_and_fetch (&value, 1); + return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) 1) + : (Type) __sync_add_and_fetch ((int64_t*) &value, 1); #endif } template inline Type Atomic::operator--() noexcept { - #if JUCE_ATOMICS_MAC + #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : (Type) OSAtomicDecrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS return sizeof (Type) == 4 ? (Type) juce_InterlockedDecrement ((volatile long*) &value) : (Type) juce_InterlockedDecrement64 ((volatile __int64*) &value); #elif JUCE_ATOMICS_GCC - return (Type) __sync_add_and_fetch (&value, -1); + return sizeof (Type) == 4 ? (Type) __sync_add_and_fetch (&value, (Type) -1) + : (Type) __sync_add_and_fetch ((int64_t*) &value, -1); #endif } template inline bool Atomic::compareAndSetBool (const Type newValue, const Type valueToCompare) noexcept { - #if JUCE_ATOMICS_MAC + #if JUCE_ATOMICS_MAC_LEGACY return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); #elif JUCE_ATOMICS_WINDOWS @@ -351,7 +360,7 @@ inline bool Atomic::compareAndSetBool (const Type newValue, const Type val template inline Type Atomic::compareAndSetValue (const Type newValue, const Type valueToCompare) noexcept { - #if JUCE_ATOMICS_MAC + #if JUCE_ATOMICS_MAC_LEGACY for (;;) // Annoying workaround for only having a bool CAS operation.. { if (compareAndSetBool (newValue, valueToCompare)) @@ -374,7 +383,7 @@ inline Type Atomic::compareAndSetValue (const Type newValue, const Type va template inline void Atomic::memoryBarrier() noexcept { - #if JUCE_ATOMICS_MAC + #if JUCE_ATOMICS_MAC_LEGACY OSMemoryBarrier(); #elif JUCE_ATOMICS_GCC __sync_synchronize(); @@ -387,4 +396,4 @@ inline void Atomic::memoryBarrier() noexcept #pragma warning (pop) #endif -#endif // __JUCE_ATOMIC_JUCEHEADER__ +#endif // JUCE_ATOMIC_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h b/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h index aca5bbf..4d3ba0e 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ByteOrder.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_BYTEORDER_JUCEHEADER__ -#define __JUCE_BYTEORDER_JUCEHEADER__ +#ifndef JUCE_BYTEORDER_H_INCLUDED +#define JUCE_BYTEORDER_H_INCLUDED //============================================================================== @@ -36,67 +39,73 @@ class JUCE_API ByteOrder public: //============================================================================== /** Swaps the upper and lower bytes of a 16-bit integer. */ - static uint16 swap (uint16 value); + static uint16 swap (uint16 value) noexcept; /** Reverses the order of the 4 bytes in a 32-bit integer. */ - static uint32 swap (uint32 value); + static uint32 swap (uint32 value) noexcept; /** Reverses the order of the 8 bytes in a 64-bit integer. */ - static uint64 swap (uint64 value); + static uint64 swap (uint64 value) noexcept; //============================================================================== /** Swaps the byte order of a 16-bit int if the CPU is big-endian */ - static uint16 swapIfBigEndian (uint16 value); + static uint16 swapIfBigEndian (uint16 value) noexcept; /** Swaps the byte order of a 32-bit int if the CPU is big-endian */ - static uint32 swapIfBigEndian (uint32 value); + static uint32 swapIfBigEndian (uint32 value) noexcept; /** Swaps the byte order of a 64-bit int if the CPU is big-endian */ - static uint64 swapIfBigEndian (uint64 value); + static uint64 swapIfBigEndian (uint64 value) noexcept; /** Swaps the byte order of a 16-bit int if the CPU is little-endian */ - static uint16 swapIfLittleEndian (uint16 value); + static uint16 swapIfLittleEndian (uint16 value) noexcept; /** Swaps the byte order of a 32-bit int if the CPU is little-endian */ - static uint32 swapIfLittleEndian (uint32 value); + static uint32 swapIfLittleEndian (uint32 value) noexcept; /** Swaps the byte order of a 64-bit int if the CPU is little-endian */ - static uint64 swapIfLittleEndian (uint64 value); + static uint64 swapIfLittleEndian (uint64 value) noexcept; //============================================================================== /** Turns 4 bytes into a little-endian integer. */ - static uint32 littleEndianInt (const void* bytes); + static uint32 littleEndianInt (const void* bytes) noexcept; + + /** Turns 8 bytes into a little-endian integer. */ + static uint64 littleEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a little-endian integer. */ - static uint16 littleEndianShort (const void* bytes); + static uint16 littleEndianShort (const void* bytes) noexcept; /** Turns 4 bytes into a big-endian integer. */ - static uint32 bigEndianInt (const void* bytes); + static uint32 bigEndianInt (const void* bytes) noexcept; + + /** Turns 8 bytes into a big-endian integer. */ + static uint64 bigEndianInt64 (const void* bytes) noexcept; /** Turns 2 bytes into a big-endian integer. */ - static uint16 bigEndianShort (const void* bytes); + static uint16 bigEndianShort (const void* bytes) noexcept; //============================================================================== /** Converts 3 little-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ - static int littleEndian24Bit (const char* bytes); + static int littleEndian24Bit (const char* bytes) noexcept; /** Converts 3 big-endian bytes into a signed 24-bit value (which is sign-extended to 32 bits). */ - static int bigEndian24Bit (const char* bytes); + static int bigEndian24Bit (const char* bytes) noexcept; /** Copies a 24-bit number to 3 little-endian bytes. */ - static void littleEndian24BitToChars (int value, char* destBytes); + static void littleEndian24BitToChars (int value, char* destBytes) noexcept; /** Copies a 24-bit number to 3 big-endian bytes. */ - static void bigEndian24BitToChars (int value, char* destBytes); + static void bigEndian24BitToChars (int value, char* destBytes) noexcept; //============================================================================== /** Returns true if the current CPU is big-endian. */ - static bool isBigEndian(); + static bool isBigEndian() noexcept; private: - ByteOrder(); + ByteOrder() JUCE_DELETED_FUNCTION; - JUCE_DECLARE_NON_COPYABLE (ByteOrder); + JUCE_DECLARE_NON_COPYABLE (ByteOrder) }; @@ -105,7 +114,7 @@ private: #pragma intrinsic (_byteswap_ulong) #endif -inline uint16 ByteOrder::swap (uint16 n) +inline uint16 ByteOrder::swap (uint16 n) noexcept { #if JUCE_USE_INTRINSICSxxx // agh - the MS compiler has an internal error when you try to use this intrinsic! return static_cast (_byteswap_ushort (n)); @@ -114,16 +123,16 @@ inline uint16 ByteOrder::swap (uint16 n) #endif } -inline uint32 ByteOrder::swap (uint32 n) +inline uint32 ByteOrder::swap (uint32 n) noexcept { #if JUCE_MAC || JUCE_IOS return OSSwapInt32 (n); - #elif JUCE_GCC && JUCE_INTEL + #elif JUCE_GCC && JUCE_INTEL && ! JUCE_NO_INLINE_ASM asm("bswap %%eax" : "=a"(n) : "a"(n)); return n; #elif JUCE_USE_INTRINSICS return _byteswap_ulong (n); - #elif JUCE_MSVC + #elif JUCE_MSVC && ! JUCE_NO_INLINE_ASM __asm { mov eax, n bswap eax @@ -137,7 +146,7 @@ inline uint32 ByteOrder::swap (uint32 n) #endif } -inline uint64 ByteOrder::swap (uint64 value) +inline uint64 ByteOrder::swap (uint64 value) noexcept { #if JUCE_MAC || JUCE_IOS return OSSwapInt64 (value); @@ -149,35 +158,39 @@ inline uint64 ByteOrder::swap (uint64 value) } #if JUCE_LITTLE_ENDIAN - inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) { return v; } - inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) { return v; } - inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) { return v; } - inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) { return swap (v); } - inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) { return swap (v); } - inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) { return swap (v); } - inline uint32 ByteOrder::littleEndianInt (const void* const bytes) { return *static_cast (bytes); } - inline uint16 ByteOrder::littleEndianShort (const void* const bytes) { return *static_cast (bytes); } - inline uint32 ByteOrder::bigEndianInt (const void* const bytes) { return swap (*static_cast (bytes)); } - inline uint16 ByteOrder::bigEndianShort (const void* const bytes) { return swap (*static_cast (bytes)); } - inline bool ByteOrder::isBigEndian() { return false; } + inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return v; } + inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return v; } + inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return v; } + inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return swap (v); } + inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return swap (v); } + inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return swap (v); } + inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return *static_cast (bytes); } + inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return *static_cast (bytes); } + inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return *static_cast (bytes); } + inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } + inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } + inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } + inline bool ByteOrder::isBigEndian() noexcept { return false; } #else - inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) { return swap (v); } - inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) { return swap (v); } - inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) { return swap (v); } - inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) { return v; } - inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) { return v; } - inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) { return v; } - inline uint32 ByteOrder::littleEndianInt (const void* const bytes) { return swap (*static_cast (bytes)); } - inline uint16 ByteOrder::littleEndianShort (const void* const bytes) { return swap (*static_cast (bytes)); } - inline uint32 ByteOrder::bigEndianInt (const void* const bytes) { return *static_cast (bytes); } - inline uint16 ByteOrder::bigEndianShort (const void* const bytes) { return *static_cast (bytes); } - inline bool ByteOrder::isBigEndian() { return true; } + inline uint16 ByteOrder::swapIfBigEndian (const uint16 v) noexcept { return swap (v); } + inline uint32 ByteOrder::swapIfBigEndian (const uint32 v) noexcept { return swap (v); } + inline uint64 ByteOrder::swapIfBigEndian (const uint64 v) noexcept { return swap (v); } + inline uint16 ByteOrder::swapIfLittleEndian (const uint16 v) noexcept { return v; } + inline uint32 ByteOrder::swapIfLittleEndian (const uint32 v) noexcept { return v; } + inline uint64 ByteOrder::swapIfLittleEndian (const uint64 v) noexcept { return v; } + inline uint32 ByteOrder::littleEndianInt (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } + inline uint64 ByteOrder::littleEndianInt64 (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } + inline uint16 ByteOrder::littleEndianShort (const void* const bytes) noexcept { return swap (*static_cast (bytes)); } + inline uint32 ByteOrder::bigEndianInt (const void* const bytes) noexcept { return *static_cast (bytes); } + inline uint64 ByteOrder::bigEndianInt64 (const void* const bytes) noexcept { return *static_cast (bytes); } + inline uint16 ByteOrder::bigEndianShort (const void* const bytes) noexcept { return *static_cast (bytes); } + inline bool ByteOrder::isBigEndian() noexcept { return true; } #endif -inline int ByteOrder::littleEndian24Bit (const char* const bytes) { return (((int) bytes[2]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[0]); } -inline int ByteOrder::bigEndian24Bit (const char* const bytes) { return (((int) bytes[0]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[2]); } -inline void ByteOrder::littleEndian24BitToChars (const int value, char* const destBytes) { destBytes[0] = (char)(value & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)((value >> 16) & 0xff); } -inline void ByteOrder::bigEndian24BitToChars (const int value, char* const destBytes) { destBytes[0] = (char)((value >> 16) & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)(value & 0xff); } +inline int ByteOrder::littleEndian24Bit (const char* const bytes) noexcept { return (((int) bytes[2]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[0]); } +inline int ByteOrder::bigEndian24Bit (const char* const bytes) noexcept { return (((int) bytes[0]) << 16) | (((int) (uint8) bytes[1]) << 8) | ((int) (uint8) bytes[2]); } +inline void ByteOrder::littleEndian24BitToChars (const int value, char* const destBytes) noexcept { destBytes[0] = (char)(value & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)((value >> 16) & 0xff); } +inline void ByteOrder::bigEndian24BitToChars (const int value, char* const destBytes) noexcept { destBytes[0] = (char)((value >> 16) & 0xff); destBytes[1] = (char)((value >> 8) & 0xff); destBytes[2] = (char)(value & 0xff); } -#endif // __JUCE_BYTEORDER_JUCEHEADER__ +#endif // JUCE_BYTEORDER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h b/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h new file mode 100644 index 0000000..60cddaa --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ContainerDeletePolicy.h @@ -0,0 +1,53 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_CONTAINERDELETEPOLICY_H_INCLUDED +#define JUCE_CONTAINERDELETEPOLICY_H_INCLUDED + +//============================================================================== +/** + Used by container classes as an indirect way to delete an object of a + particular type. + + The generic implementation of this class simply calls 'delete', but you can + create a specialised version of it for a particular class if you need to + delete that type of object in a more appropriate way. + + @see ScopedPointer, OwnedArray +*/ +template +struct ContainerDeletePolicy +{ + static void destroy (ObjectType* object) + { + delete object; + } +}; + + +#endif // JUCE_CONTAINERDELETEPOLICY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h b/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h index 4f6a66e..3731f0b 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_HeapBlock.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_HEAPBLOCK_JUCEHEADER__ -#define __JUCE_HEAPBLOCK_JUCEHEADER__ +#ifndef JUCE_HEAPBLOCK_H_INCLUDED +#define JUCE_HEAPBLOCK_H_INCLUDED #ifndef DOXYGEN namespace HeapBlockHelper @@ -102,7 +105,8 @@ public: 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. + If you want an array of zero values, you can use the calloc() method or the + other constructor that takes an InitialisationState parameter. */ explicit HeapBlock (const size_t numElements) : data (static_cast (std::malloc (numElements * sizeof (ElementType)))) @@ -110,8 +114,20 @@ public: throwOnAllocationFailure(); } - /** Destructor. + /** Creates a HeapBlock containing a number of elements. + The initialiseToZero parameter determines whether the new memory should be cleared, + or left uninitialised. + */ + HeapBlock (const size_t numElements, const bool initialiseToZero) + : data (static_cast (initialiseToZero + ? std::calloc (numElements, sizeof (ElementType)) + : std::malloc (numElements * sizeof (ElementType)))) + { + throwOnAllocationFailure(); + } + + /** Destructor. This will free the data, if any has been allocated. */ ~HeapBlock() @@ -222,15 +238,12 @@ public: This does the same job as either malloc() or calloc(), depending on the initialiseToZero parameter. */ - void allocate (const size_t newNumElements, const bool initialiseToZero) + void allocate (const size_t newNumElements, bool initialiseToZero) { std::free (data); - - if (initialiseToZero) - data = static_cast (std::calloc (newNumElements, sizeof (ElementType))); - else - data = static_cast (std::malloc (newNumElements * sizeof (ElementType))); - + data = static_cast (initialiseToZero + ? std::calloc (newNumElements, sizeof (ElementType)) + : std::malloc (newNumElements * sizeof (ElementType))); throwOnAllocationFailure(); } @@ -241,11 +254,8 @@ public: */ void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType)) { - if (data == nullptr) - data = static_cast (std::malloc (newNumElements * elementSize)); - else - data = static_cast (std::realloc (data, newNumElements * elementSize)); - + data = static_cast (data == nullptr ? std::malloc (newNumElements * elementSize) + : std::realloc (data, newNumElements * elementSize)); throwOnAllocationFailure(); } @@ -276,6 +286,9 @@ public: zeromem (data, sizeof (ElementType) * numElements); } + /** This typedef can be used to get the type of the heapblock's elements. */ + typedef ElementType Type; + private: //============================================================================== ElementType* data; @@ -285,10 +298,11 @@ private: HeapBlockHelper::ThrowOnFail::check (data); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HeapBlock); - - JUCE_PREVENT_HEAP_ALLOCATION; // Creating a 'new HeapBlock' would be missing the point! + #if ! (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) + JUCE_DECLARE_NON_COPYABLE (HeapBlock) + JUCE_PREVENT_HEAP_ALLOCATION // Creating a 'new HeapBlock' would be missing the point! + #endif }; -#endif // __JUCE_HEAPBLOCK_JUCEHEADER__ +#endif // JUCE_HEAPBLOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h b/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h index a707314..248e7bc 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_LeakedObjectDetector.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_LEAKEDOBJECTDETECTOR_JUCEHEADER__ -#define __JUCE_LEAKEDOBJECTDETECTOR_JUCEHEADER__ - -#include "../text/juce_String.h" -#include "juce_Atomic.h" +#ifndef JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED +#define JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED //============================================================================== @@ -127,8 +127,9 @@ private: void blahBlah(); private: - JUCE_LEAK_DETECTOR (MyClass); - };@endcode + JUCE_LEAK_DETECTOR (MyClass) + }; + @endcode @see JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR, LeakedObjectDetector */ @@ -142,4 +143,4 @@ private: #endif -#endif // __JUCE_LEAKEDOBJECTDETECTOR_JUCEHEADER__ +#endif // JUCE_LEAKEDOBJECTDETECTOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h b/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h index f621e48..57d1644 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Memory.h @@ -1,38 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_MEMORY_JUCEHEADER__ -#define __JUCE_MEMORY_JUCEHEADER__ - -//============================================================================== -#if JUCE_MINGW - #ifndef alloca - /** This allocator is not defined in mingw gcc. */ - #define alloca __builtin_alloca - #endif -#endif +#ifndef JUCE_MEMORY_H_INCLUDED +#define JUCE_MEMORY_H_INCLUDED //============================================================================== /** Fills a block of memory with zeros. */ @@ -54,8 +49,8 @@ inline void deleteAndZero (Type& pointer) { delete poi This can be useful to avoid casting pointers to a char* and back when you want to move them by a specific number of bytes, */ -template -inline Type* addBytesToPointer (Type* pointer, int bytes) noexcept { return (Type*) (((char*) pointer) + bytes); } +template +inline Type* addBytesToPointer (Type* pointer, IntegerType bytes) noexcept { return (Type*) (((char*) pointer) + bytes); } /** A handy function which returns the difference between any two pointers, in bytes. The address of the second pointer is subtracted from the first, and the difference in bytes is returned. @@ -67,7 +62,40 @@ inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept { r nullptr if the pointer is null. */ template -inline Type* createCopyIfNotNull (Type* pointer) { return pointer != nullptr ? new Type (*pointer) : nullptr; } +inline Type* createCopyIfNotNull (const Type* pointer) { return pointer != nullptr ? new Type (*pointer) : nullptr; } + +//============================================================================== +#if JUCE_MAC || JUCE_IOS || DOXYGEN + + /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. + You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack. + */ + class JUCE_API ScopedAutoReleasePool + { + public: + ScopedAutoReleasePool(); + ~ScopedAutoReleasePool(); + + private: + void* pool; + + JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool) + }; + + /** A macro that can be used to easily declare a local ScopedAutoReleasePool + object for RAII-based obj-C autoreleasing. + Because this may use the \@autoreleasepool syntax, you must follow the macro with + a set of braces to mark the scope of the pool. + */ +#if (JUCE_COMPILER_SUPPORTS_ARC && defined (__OBJC__)) || DOXYGEN + #define JUCE_AUTORELEASEPOOL @autoreleasepool +#else + #define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__); +#endif + +#else + #define JUCE_AUTORELEASEPOOL +#endif //============================================================================== /* In a Windows DLL build, we'll expose some malloc/free functions that live inside the DLL, and use these for @@ -75,15 +103,15 @@ inline Type* createCopyIfNotNull (Type* pointer) { return pointer != nullptr avoiding problems when an object is created in one module and passed across to another where it is deleted. By piggy-backing on the JUCE_LEAK_DETECTOR macro, these allocators can be injected into most juce classes. */ -#if JUCE_MSVC && defined (JUCE_DLL) && ! DOXYGEN +#if JUCE_MSVC && (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD)) && ! (JUCE_DISABLE_DLL_ALLOCATORS || DOXYGEN) extern JUCE_API void* juceDLL_malloc (size_t); extern JUCE_API void juceDLL_free (void*); #define JUCE_LEAK_DETECTOR(OwnerClass) public:\ - static void* operator new (size_t sz) { return juce::juceDLL_malloc ((int) sz); } \ - static void* operator new (size_t, void* p) { return p; } \ - static void operator delete (void* p) { juce::juceDLL_free (p); } \ - static void operator delete (void*, void*) {} + static void* operator new (size_t sz) { return juce::juceDLL_malloc (sz); } \ + static void* operator new (size_t, void* p) { return p; } \ + static void operator delete (void* p) { juce::juceDLL_free (p); } \ + static void operator delete (void*, void*) {} #endif //============================================================================== @@ -95,4 +123,4 @@ inline Type* createCopyIfNotNull (Type* pointer) { return pointer != nullptr #endif -#endif // __JUCE_MEMORY_JUCEHEADER__ +#endif // JUCE_MEMORY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp index 83bfd88..a827037 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -53,9 +56,9 @@ MemoryBlock::MemoryBlock (const MemoryBlock& other) } MemoryBlock::MemoryBlock (const void* const dataToInitialiseFrom, const size_t sizeInBytes) - : size (jmax ((size_t) 0, sizeInBytes)) + : size (sizeInBytes) { - jassert (sizeInBytes >= 0); + jassert (((ssize_t) sizeInBytes) >= 0); if (size > 0) { @@ -85,14 +88,14 @@ MemoryBlock& MemoryBlock::operator= (const MemoryBlock& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS MemoryBlock::MemoryBlock (MemoryBlock&& other) noexcept - : data (static_cast &&> (other.data)), + : data (static_cast &&> (other.data)), size (other.size) { } MemoryBlock& MemoryBlock::operator= (MemoryBlock&& other) noexcept { - data = static_cast &&> (other.data); + data = static_cast &&> (other.data); size = other.size; return *this; } @@ -124,8 +127,7 @@ void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero) { if (newSize <= 0) { - data.free(); - size = 0; + reset(); } else { @@ -146,6 +148,12 @@ void MemoryBlock::setSize (const size_t newSize, const bool initialiseToZero) } } +void MemoryBlock::reset() +{ + data.free(); + size = 0; +} + void MemoryBlock::ensureSize (const size_t minimumSize, const bool initialiseToZero) { if (size < minimumSize) @@ -175,6 +183,16 @@ void MemoryBlock::append (const void* const srcData, const size_t numBytes) } } +void MemoryBlock::replaceWith (const void* const srcData, const size_t numBytes) +{ + if (numBytes > 0) + { + jassert (srcData != nullptr); // this must not be null! + setSize (numBytes); + memcpy (data, srcData, numBytes); + } +} + void MemoryBlock::insert (const void* const srcData, const size_t numBytes, size_t insertPosition) { if (numBytes > 0) @@ -216,12 +234,12 @@ void MemoryBlock::copyFrom (const void* const src, int offset, size_t num) noexc if (offset < 0) { d -= offset; - num -= offset; + num += (size_t) -offset; offset = 0; } - if (offset + num > size) - num = size - offset; + if ((size_t) offset + num > size) + num = size - (size_t) offset; if (num > 0) memcpy (data + offset, d, num); @@ -235,14 +253,13 @@ void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcep { zeromem (d, (size_t) -offset); d -= offset; - - num += offset; + num -= (size_t) -offset; offset = 0; } - if (offset + num > size) + if ((size_t) offset + num > size) { - const size_t newNum = size - offset; + const size_t newNum = size - (size_t) offset; zeromem (d + newNum, num - newNum); num = newNum; } @@ -253,7 +270,7 @@ void MemoryBlock::copyTo (void* const dst, int offset, size_t num) const noexcep String MemoryBlock::toString() const { - return String (CharPointer_UTF8 (data), size); + return String::fromUTF8 (data, (int) size); } //============================================================================== @@ -262,12 +279,12 @@ int MemoryBlock::getBitRange (const size_t bitRangeStart, size_t numBits) const int res = 0; size_t byte = bitRangeStart >> 3; - int offsetInByte = (int) bitRangeStart & 7; + size_t offsetInByte = bitRangeStart & 7; size_t bitsSoFar = 0; while (numBits > 0 && (size_t) byte < size) { - const int bitsThisTime = jmin ((int) numBits, 8 - offsetInByte); + const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte); const int mask = (0xff >> (8 - bitsThisTime)) << offsetInByte; res |= (((data[byte] & mask) >> offsetInByte) << bitsSoFar); @@ -284,17 +301,17 @@ int MemoryBlock::getBitRange (const size_t bitRangeStart, size_t numBits) const void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int bitsToSet) noexcept { size_t byte = bitRangeStart >> 3; - int offsetInByte = (int) bitRangeStart & 7; - unsigned int mask = ~((((unsigned int) 0xffffffff) << (32 - numBits)) >> (32 - numBits)); + size_t offsetInByte = bitRangeStart & 7; + uint32 mask = ~((((uint32) 0xffffffff) << (32 - numBits)) >> (32 - numBits)); while (numBits > 0 && (size_t) byte < size) { - const int bitsThisTime = jmin ((int) numBits, 8 - offsetInByte); + const size_t bitsThisTime = jmin (numBits, 8 - offsetInByte); - const unsigned int tempMask = (mask << offsetInByte) | ~((((unsigned int) 0xffffffff) >> offsetInByte) << offsetInByte); - const unsigned int tempBits = (unsigned int) bitsToSet << offsetInByte; + const uint32 tempMask = (mask << offsetInByte) | ~((((uint32) 0xffffffff) >> offsetInByte) << offsetInByte); + const uint32 tempBits = (uint32) bitsToSet << offsetInByte; - data[byte] = (char) ((data[byte] & tempMask) | tempBits); + data[byte] = (char) (((uint32) data[byte] & tempMask) | tempBits); ++byte; numBits -= bitsThisTime; @@ -305,11 +322,11 @@ void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int b } //============================================================================== -void MemoryBlock::loadFromHexString (const String& hex) +void MemoryBlock::loadFromHexString (StringRef hex) { ensureSize ((size_t) hex.length() >> 1); char* dest = data; - String::CharPointerType t (hex.getCharPointer()); + String::CharPointerType t (hex.text); for (;;) { @@ -323,22 +340,11 @@ void MemoryBlock::loadFromHexString (const String& hex) { const juce_wchar c = t.getAndAdvance(); - if (c >= '0' && c <= '9') - { - byte |= c - '0'; - break; - } - else if (c >= 'a' && c <= 'z') - { - byte |= c - ('a' - 10); - break; - } - else if (c >= 'A' && c <= 'Z') - { - byte |= c - ('A' - 10); - break; - } - else if (c == 0) + if (c >= '0' && c <= '9') { byte |= c - '0'; break; } + if (c >= 'a' && c <= 'z') { byte |= c - ('a' - 10); break; } + if (c >= 'A' && c <= 'Z') { byte |= c - ('A' - 10); break; } + + if (c == 0) { setSize (static_cast (dest - data)); return; @@ -351,7 +357,7 @@ void MemoryBlock::loadFromHexString (const String& hex) } //============================================================================== -const char* const MemoryBlock::encodingTable = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; +static const char base64EncodingTable[] = ".ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+"; String MemoryBlock::toBase64Encoding() const { @@ -359,50 +365,52 @@ String MemoryBlock::toBase64Encoding() const String destString ((unsigned int) size); // store the length, followed by a '.', and then the data. const int initialLen = destString.length(); - destString.preallocateBytes (sizeof (String::CharPointerType::CharType) * (initialLen + 2 + numChars)); + destString.preallocateBytes (sizeof (String::CharPointerType::CharType) * (size_t) initialLen + 2 + numChars); String::CharPointerType d (destString.getCharPointer()); d += initialLen; d.write ('.'); for (size_t i = 0; i < numChars; ++i) - d.write ((juce_wchar) (uint8) encodingTable [getBitRange (i * 6, 6)]); + d.write ((juce_wchar) (uint8) base64EncodingTable [getBitRange (i * 6, 6)]); d.writeNull(); return destString; } -bool MemoryBlock::fromBase64Encoding (const String& s) +static const char base64DecodingTable[] = { - const int startPos = s.indexOfChar ('.') + 1; + 63, 0, 0, 0, 0, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 +}; - if (startPos <= 0) +bool MemoryBlock::fromBase64Encoding (StringRef s) +{ + String::CharPointerType dot (CharacterFunctions::find (s.text, (juce_wchar) '.')); + + if (dot.isEmpty()) return false; - const int numBytesNeeded = s.substring (0, startPos - 1).getIntValue(); + const int numBytesNeeded = String (s.text, dot).getIntValue(); setSize ((size_t) numBytesNeeded, true); - const int numChars = s.length() - startPos; - - String::CharPointerType srcChars (s.getCharPointer()); - srcChars += startPos; + String::CharPointerType srcChars (dot + 1); int pos = 0; - for (int i = 0; i < numChars; ++i) + for (;;) { - const char c = (char) srcChars.getAndAdvance(); + int c = (int) srcChars.getAndAdvance(); - for (int j = 0; j < 64; ++j) + if (c == 0) + return true; + + c -= 43; + if (isPositiveAndBelow (c, numElementsInArray (base64DecodingTable))) { - if (encodingTable[j] == c) - { - setBitRange ((size_t) pos, 6, j); - pos += 6; - break; - } + setBitRange ((size_t) pos, 6, base64DecodingTable [c]); + pos += 6; } } - - return true; } diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h index 5410c3d..30bb3ca 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_MemoryBlock.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_MEMORYBLOCK_JUCEHEADER__ -#define __JUCE_MEMORYBLOCK_JUCEHEADER__ - -#include "../text/juce_String.h" -#include "../memory/juce_HeapBlock.h" +#ifndef JUCE_MEMORYBLOCK_H_INCLUDED +#define JUCE_MEMORYBLOCK_H_INCLUDED //============================================================================== @@ -51,7 +51,7 @@ public: bool initialiseToZero = false); /** Creates a copy of another memory block. */ - MemoryBlock (const MemoryBlock& other); + MemoryBlock (const MemoryBlock&); /** Creates a memory block using a copy of a block of data. @@ -64,31 +64,27 @@ public: ~MemoryBlock() noexcept; /** 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); + MemoryBlock& operator= (const MemoryBlock&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - MemoryBlock (MemoryBlock&& other) noexcept; - MemoryBlock& operator= (MemoryBlock&& other) noexcept; + MemoryBlock (MemoryBlock&&) noexcept; + MemoryBlock& operator= (MemoryBlock&&) noexcept; #endif //============================================================================== /** 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 noexcept; /** Compares two memory blocks. - @returns true if the two blocks are different sizes or have different contents. */ bool operator!= (const MemoryBlock& other) const noexcept; - /** Returns true if the data in this MemoryBlock matches the raw bytes passed-in. - */ + /** Returns true if the data in this MemoryBlock matches the raw bytes passed-in. */ bool matches (const void* data, size_t dataSize) const noexcept; //============================================================================== @@ -100,7 +96,6 @@ public: void* getData() const noexcept { return data; } /** Returns a byte from the memory block. - This returns a reference, so you can also use it to set a byte. */ template @@ -113,9 +108,9 @@ public: /** 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. + Any data that is present in both the old and new sizes will be retained. + When enlarging the block, the new space that is allocated at the end can either be + cleared, or left uninitialised. @param newSize the new desired size for the block @param initialiseNewSpaceToZero if the block gets enlarged, this determines @@ -138,9 +133,11 @@ public: void ensureSize (const size_t minimumSize, bool initialiseNewSpaceToZero = false); + /** Frees all the blocks data, setting its size to 0. */ + void reset(); + //============================================================================== /** Fills the entire memory block with a repeated byte value. - This is handy for clearing a block of memory to zero. */ void fillWith (uint8 valueToUse) noexcept; @@ -150,6 +147,11 @@ public: */ void append (const void* data, size_t numBytes); + /** Resizes this block to the given size and fills its contents from the supplied buffer. + The data pointer must not be null. + */ + void replaceWith (const void* data, size_t numBytes); + /** Inserts some data into the block. The dataToInsert pointer must not be null. This block's size will be increased accordingly. If the insert position lies outside the valid range of the block, it will be clipped to @@ -207,7 +209,7 @@ public: @see String::toHexString() */ - void loadFromHexString (const String& sourceHexString); + void loadFromHexString (StringRef sourceHexString); //============================================================================== /** Sets a number of bits in the memory block, treating it as a long binary sequence. */ @@ -236,17 +238,16 @@ public: @see toBase64Encoding */ - bool fromBase64Encoding (const String& encodedString); + bool fromBase64Encoding (StringRef encodedString); private: //============================================================================== - HeapBlock data; + HeapBlock data; size_t size; - static const char* const encodingTable; - JUCE_LEAK_DETECTOR (MemoryBlock); + JUCE_LEAK_DETECTOR (MemoryBlock) }; -#endif // __JUCE_MEMORYBLOCK_JUCEHEADER__ +#endif // JUCE_MEMORYBLOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h index 113b74b..a633dca 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_OptionalScopedPointer.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_OPTIONALSCOPEDPOINTER_JUCEHEADER__ -#define __JUCE_OPTIONALSCOPEDPOINTER_JUCEHEADER__ - -#include "juce_ScopedPointer.h" +#ifndef JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED +#define JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED //============================================================================== @@ -106,6 +107,9 @@ public: /** Returns the object that this pointer is managing. */ inline operator ObjectType*() const noexcept { return object; } + /** Returns the object that this pointer is managing. */ + inline ObjectType* get() const noexcept { return object; } + /** Returns the object that this pointer is managing. */ inline ObjectType& operator*() const noexcept { return *object; } @@ -127,6 +131,41 @@ public: object.release(); } + /** Makes this OptionalScopedPointer point at a new object, specifying whether the + OptionalScopedPointer will take ownership of the object. + + If takeOwnership is true, then the OptionalScopedPointer will act like a ScopedPointer, + deleting the object when it is itself deleted. If this parameter is false, then the + OptionalScopedPointer just holds a normal pointer to the object, and won't delete it. + */ + void set (ObjectType* newObject, bool takeOwnership) + { + if (object != newObject) + { + clear(); + object = newObject; + } + + shouldDelete = takeOwnership; + } + + /** Makes this OptionalScopedPointer point at a new object, and take ownership of that object. */ + void setOwned (ObjectType* newObject) + { + set (newObject, true); + } + + /** Makes this OptionalScopedPointer point at a new object, but will not take ownership of that object. */ + void setNonOwned (ObjectType* newObject) + { + set (newObject, false); + } + + /** Returns true if the target object will be deleted when this pointer + object is deleted. + */ + bool willDeleteObject() const noexcept { return shouldDelete; } + //============================================================================== /** Swaps this object with another OptionalScopedPointer. The two objects simply exchange their states. @@ -144,4 +183,4 @@ private: }; -#endif // __JUCE_OPTIONALSCOPEDPOINTER_JUCEHEADER__ +#endif // JUCE_OPTIONALSCOPEDPOINTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h b/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h index b038570..6464d85 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ReferenceCountedObject.h @@ -1,37 +1,38 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ -#define __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ - -#include "juce_Atomic.h" +#ifndef JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED +#define JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED //============================================================================== /** - Adds reference-counting to an object. + A base class which provides methods for reference-counting. To add reference-counting to a class, derive it from this class, and use the ReferenceCountedObjectPtr class to point to it. @@ -70,16 +71,15 @@ public: This is done automatically by the smart pointer, but is public just in case it's needed for nefarious purposes. */ - inline void incReferenceCount() noexcept + void incReferenceCount() noexcept { ++refCount; } /** Decreases the object's reference count. - If the count gets to zero, the object will be deleted. */ - inline void decReferenceCount() noexcept + void decReferenceCount() noexcept { jassert (getReferenceCount() > 0); @@ -87,16 +87,24 @@ public: delete this; } + /** Decreases the object's reference count. + If the count gets to zero, the object will not be deleted, but this method + will return true, allowing the caller to take care of deletion. + */ + bool decReferenceCountWithoutDeleting() noexcept + { + jassert (getReferenceCount() > 0); + return --refCount == 0; + } + /** Returns the object's current reference count. */ - inline int getReferenceCount() const noexcept { return refCount.get(); } + int getReferenceCount() const noexcept { return refCount.get(); } protected: //============================================================================== /** Creates the reference-counted object (with an initial ref count of zero). */ - ReferenceCountedObject() - { - } + ReferenceCountedObject() {} /** Destructor. */ virtual ~ReferenceCountedObject() @@ -116,6 +124,9 @@ protected: private: //============================================================================== Atomic refCount; + + friend struct ContainerDeletePolicy; + JUCE_DECLARE_NON_COPYABLE (ReferenceCountedObject) }; @@ -123,7 +134,7 @@ private: /** Adds reference-counting to an object. - This is efectively a version of the ReferenceCountedObject class, but which + This is effectively a version of the ReferenceCountedObject class, but which uses a non-atomic counter, and so is not thread-safe (but which will be more efficient). For more details on how to use it, see the ReferenceCountedObject class notes. @@ -139,16 +150,15 @@ public: This is done automatically by the smart pointer, but is public just in case it's needed for nefarious purposes. */ - inline void incReferenceCount() noexcept + void incReferenceCount() noexcept { ++refCount; } /** Decreases the object's reference count. - If the count gets to zero, the object will be deleted. */ - inline void decReferenceCount() noexcept + void decReferenceCount() noexcept { jassert (getReferenceCount() > 0); @@ -156,8 +166,18 @@ public: delete this; } + /** Decreases the object's reference count. + If the count gets to zero, the object will not be deleted, but this method + will return true, allowing the caller to take care of deletion. + */ + bool decReferenceCountWithoutDeleting() noexcept + { + jassert (getReferenceCount() > 0); + return --refCount == 0; + } + /** Returns the object's current reference count. */ - inline int getReferenceCount() const noexcept { return refCount; } + int getReferenceCount() const noexcept { return refCount; } protected: @@ -175,6 +195,9 @@ protected: private: //============================================================================== int refCount; + + friend struct ContainerDeletePolicy; + JUCE_DECLARE_NON_COPYABLE (SingleThreadedReferenceCountedObject) }; @@ -183,13 +206,20 @@ private: A smart-pointer class which points to a reference-counted object. The template parameter specifies the class of the object you want to point to - the easiest - way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject, - but if you need to, you could roll your own reference-countable class by implementing a pair of - mathods called incReferenceCount() and decReferenceCount(). + way to make a class reference-countable is to simply make it inherit from ReferenceCountedObject + or SingleThreadedReferenceCountedObject, but if you need to, you can roll your own reference-countable + class by implementing a set of mathods called incReferenceCount(), decReferenceCount(), and + decReferenceCountWithoutDeleting(). See ReferenceCountedObject for examples of how these methods + should behave. When using this class, you'll probably want to create a typedef to abbreviate the full templated name - e.g. - @code typedef ReferenceCountedObjectPtr MyClassPtr;@endcode + @code + struct MyClass : public ReferenceCountedObject + { + typedef ReferenceCountedObjectPtr Ptr; + ... + @endcode @see ReferenceCountedObject, ReferenceCountedObjectArray */ @@ -202,54 +232,40 @@ public: //============================================================================== /** Creates a pointer to a null object. */ - inline ReferenceCountedObjectPtr() noexcept + ReferenceCountedObjectPtr() noexcept : referencedObject (nullptr) { } /** Creates a pointer to an object. - - This will increment the object's reference-count if it is non-null. + This will increment the object's reference-count. */ - inline ReferenceCountedObjectPtr (ReferenceCountedObjectClass* const refCountedObject) noexcept + ReferenceCountedObjectPtr (ReferencedType* refCountedObject) noexcept : referencedObject (refCountedObject) { - if (refCountedObject != nullptr) - refCountedObject->incReferenceCount(); + incIfNotNull (refCountedObject); + } + + /** Copies another pointer. + This will increment the object's reference-count. + */ + ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept + : referencedObject (other.referencedObject) + { + incIfNotNull (referencedObject); } /** Copies another pointer. This will increment the object's reference-count (if it is non-null). */ - inline ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept - : referencedObject (other.referencedObject) + template + ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept + : referencedObject (static_cast (other.get())) { - if (referencedObject != nullptr) - referencedObject->incReferenceCount(); - } - - #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - /** Takes-over the object from another pointer. */ - inline ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept - : referencedObject (other.referencedObject) - { - other.referencedObject = nullptr; - } - #endif - - /** Copies another pointer. - This will increment the object's reference-count (if it is non-null). - */ - template - inline ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept - : referencedObject (static_cast (other.getObject())) - { - if (referencedObject != nullptr) - referencedObject->incReferenceCount(); + incIfNotNull (referencedObject); } /** 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. */ @@ -258,18 +274,42 @@ public: return operator= (other.referencedObject); } + /** 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. + */ + template + ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other) + { + return operator= (static_cast (other.get())); + } + /** 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. */ - template - ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other) + ReferenceCountedObjectPtr& operator= (ReferencedType* const newObject) { - return operator= (static_cast (other.getObject())); + if (referencedObject != newObject) + { + incIfNotNull (newObject); + ReferencedType* const oldObject = referencedObject; + referencedObject = newObject; + decIfNotNull (oldObject); + } + + return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + /** Takes-over the object from another pointer. */ + ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept + : referencedObject (other.referencedObject) + { + other.referencedObject = nullptr; + } + /** Takes-over the object from another pointer. */ ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other) { @@ -278,108 +318,98 @@ public: } #endif - /** 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) - { - if (newObject != nullptr) - newObject->incReferenceCount(); - - ReferenceCountedObjectClass* const oldObject = referencedObject; - referencedObject = newObject; - - if (oldObject != nullptr) - oldObject->decReferenceCount(); - } - - return *this; - } - /** Destructor. - - This will decrement the object's reference-count, and may delete it if it - gets to zero. + This will decrement the object's reference-count, which will cause the + object to be deleted when the ref-count hits zero. */ - inline ~ReferenceCountedObjectPtr() + ~ReferenceCountedObjectPtr() { - if (referencedObject != nullptr) - referencedObject->decReferenceCount(); + decIfNotNull (referencedObject); } + //============================================================================== + /** Returns the object that this pointer references. + The pointer returned may be null, of course. + */ + operator ReferencedType*() const noexcept { return referencedObject; } + /** Returns the object that this pointer references. - The pointer returned may be zero, of course. + The pointer returned may be null, of course. */ - inline operator ReferenceCountedObjectClass*() const noexcept - { - return referencedObject; - } + ReferencedType* get() const noexcept { return referencedObject; } + + /** Returns the object that this pointer references. + The pointer returned may be null, of course. + */ + ReferencedType* getObject() const noexcept { return referencedObject; } // the -> operator is called on the referenced object - inline ReferenceCountedObjectClass* operator->() const noexcept - { - return referencedObject; - } - - /** Returns the object that this pointer references. - The pointer returned may be zero, of course. - */ - inline ReferenceCountedObjectClass* getObject() const noexcept + ReferencedType* operator->() const noexcept { + jassert (referencedObject != nullptr); // null pointer method call! return referencedObject; } private: //============================================================================== - ReferenceCountedObjectClass* referencedObject; + ReferencedType* referencedObject; + + static void incIfNotNull (ReferencedType* o) noexcept + { + if (o != nullptr) + o->incReferenceCount(); + } + + static void decIfNotNull (ReferencedType* o) noexcept + { + if (o != nullptr && o->decReferenceCountWithoutDeleting()) + ContainerDeletePolicy::destroy (o); + } }; +//============================================================================== /** Compares two ReferenceCountedObjectPointers. */ template bool operator== (const ReferenceCountedObjectPtr& object1, ReferenceCountedObjectClass* const object2) noexcept { - return object1.getObject() == object2; + return object1.get() == object2; } /** Compares two ReferenceCountedObjectPointers. */ template bool operator== (const ReferenceCountedObjectPtr& object1, const ReferenceCountedObjectPtr& object2) noexcept { - return object1.getObject() == object2.getObject(); + return object1.get() == object2.get(); } /** Compares two ReferenceCountedObjectPointers. */ template -bool operator== (ReferenceCountedObjectClass* object1, ReferenceCountedObjectPtr& object2) noexcept +bool operator== (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr& object2) noexcept { - return object1 == object2.getObject(); + return object1 == object2.get(); } /** Compares two ReferenceCountedObjectPointers. */ template bool operator!= (const ReferenceCountedObjectPtr& object1, const ReferenceCountedObjectClass* object2) noexcept { - return object1.getObject() != object2; + return object1.get() != object2; } /** Compares two ReferenceCountedObjectPointers. */ template -bool operator!= (const ReferenceCountedObjectPtr& object1, ReferenceCountedObjectPtr& object2) noexcept +bool operator!= (const ReferenceCountedObjectPtr& object1, const ReferenceCountedObjectPtr& object2) noexcept { - return object1.getObject() != object2.getObject(); + return object1.get() != object2.get(); } /** Compares two ReferenceCountedObjectPointers. */ template -bool operator!= (ReferenceCountedObjectClass* object1, ReferenceCountedObjectPtr& object2) noexcept +bool operator!= (ReferenceCountedObjectClass* object1, const ReferenceCountedObjectPtr& object2) noexcept { - return object1 != object2.getObject(); + return object1 != object2.get(); } -#endif // __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ +#endif // JUCE_REFERENCECOUNTEDOBJECT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h index 40d0626..a7fcff0 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_ScopedPointer.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SCOPEDPOINTER_JUCEHEADER__ -#define __JUCE_SCOPEDPOINTER_JUCEHEADER__ +#ifndef JUCE_SCOPEDPOINTER_H_INCLUDED +#define JUCE_SCOPEDPOINTER_H_INCLUDED //============================================================================== /** @@ -94,7 +97,7 @@ public: /** Destructor. This will delete the object that this ScopedPointer currently refers to. */ - inline ~ScopedPointer() { delete object; } + inline ~ScopedPointer() { ContainerDeletePolicy::destroy (object); } /** Changes this ScopedPointer to point to a new object. @@ -116,7 +119,7 @@ public: ObjectType* const oldObject = object; object = objectToTransferFrom.object; objectToTransferFrom.object = nullptr; - delete oldObject; + ContainerDeletePolicy::destroy (oldObject); } return *this; @@ -127,7 +130,7 @@ public: If this ScopedPointer already points to an object, that object will first be deleted. - The pointer that you pass is may be null. + The pointer that you pass in may be a nullptr. */ ScopedPointer& operator= (ObjectType* const newObjectToTakePossessionOf) { @@ -135,12 +138,27 @@ public: { ObjectType* const oldObject = object; object = newObjectToTakePossessionOf; - delete oldObject; + ContainerDeletePolicy::destroy (oldObject); } return *this; } + #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + ScopedPointer (ScopedPointer&& other) noexcept + : object (other.object) + { + other.object = nullptr; + } + + ScopedPointer& operator= (ScopedPointer&& other) noexcept + { + object = other.object; + other.object = nullptr; + return *this; + } + #endif + //============================================================================== /** Returns the object that this ScopedPointer refers to. */ inline operator ObjectType*() const noexcept { return object; } @@ -168,7 +186,7 @@ public: { // Two ScopedPointers should never be able to refer to the same object - if // this happens, you must have done something dodgy! - jassert (object != other.object); + jassert (object != other.object || this == other.getAddress()); std::swap (object, other.object); } @@ -186,25 +204,23 @@ private: const ScopedPointer* getAddress() const noexcept { return this; } #if ! JUCE_MSVC // (MSVC can't deal with multiple copy constructors) - /* These are private to stop people accidentally copying a const ScopedPointer (the compiler - would let you do so by implicitly casting the source to its raw object pointer). + /* The copy constructors are private to stop people accidentally copying a const ScopedPointer + (the compiler would let you do so by implicitly casting the source to its raw object pointer). - A side effect of this is that you may hit a puzzling compiler error when you write something - like this: + A side effect of this is that in a compiler that doesn't support C++11, you may hit an + error when you write something like this: ScopedPointer m = new MyClass(); // Compile error: copy constructor is private. Even though the compiler would normally ignore the assignment here, it can't do so when the - copy constructor is private. It's very easy to fis though - just write it like this: + copy constructor is private. It's very easy to fix though - just write it like this: ScopedPointer m (new MyClass()); // Compiles OK - It's good practice to always use the latter form when writing your object declarations anyway, - rather than writing them as assignments and assuming (or hoping) that the compiler will be - smart enough to replace your construction + assignment with a single constructor. + It's probably best to use the latter form when writing your object declarations anyway, as + this is a better representation of the code that you actually want the compiler to produce. */ - ScopedPointer (const ScopedPointer&); - ScopedPointer& operator= (const ScopedPointer&); + JUCE_DECLARE_NON_COPYABLE (ScopedPointer) #endif }; @@ -227,4 +243,11 @@ bool operator!= (const ScopedPointer& pointer1, ObjectType* const po return static_cast (pointer1) != pointer2; } -#endif // __JUCE_SCOPEDPOINTER_JUCEHEADER__ +//============================================================================== +#ifndef DOXYGEN +// NB: This is just here to prevent any silly attempts to call deleteAndZero() on a ScopedPointer. +template +void deleteAndZero (ScopedPointer&) { static_jassert (sizeof (Type) == 12345); } +#endif + +#endif // JUCE_SCOPEDPOINTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h b/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h new file mode 100644 index 0000000..53d314b --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/memory/juce_SharedResourcePointer.h @@ -0,0 +1,152 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_SHAREDRESOURCEPOINTER_H_INCLUDED +#define JUCE_SHAREDRESOURCEPOINTER_H_INCLUDED + + +//============================================================================== +/** + A smart-pointer that automatically creates and manages the lifetime of a + shared static instance of a class. + + The SharedObjectType template type indicates the class to use for the shared + object - the only requirements on this class are that it must have a public + default constructor and destructor. + + The SharedResourcePointer offers a pattern that differs from using a singleton or + static instance of an object, because it uses reference-counting to make sure that + the underlying shared object is automatically created/destroyed according to the + number of SharedResourcePointer objects that exist. When the last one is deleted, + the underlying object is also immediately destroyed. This allows you to use scoping + to manage the lifetime of a shared resource. + + Note: the construction/deletion of the shared object must not involve any + code that makes recursive calls to a SharedResourcePointer, or you'll cause + a deadlock. + + Example: + @code + // An example of a class that contains the shared data you want to use. + struct MySharedData + { + // There's no need to ever create an instance of this class directly yourself, + // but it does need a public constructor that does the initialisation. + MySharedData() + { + sharedStuff = generateHeavyweightStuff(); + } + + Array sharedStuff; + }; + + struct DataUserClass + { + DataUserClass() + { + // Multiple instances of the DataUserClass will all have the same + // shared common instance of MySharedData referenced by their sharedData + // member variables. + useSharedStuff (sharedData->sharedStuff); + } + + // By keeping this pointer as a member variable, the shared resource + // is guaranteed to be available for as long as the DataUserClass object. + SharedResourcePointer sharedData; + }; + + @endcode + */ +template +class SharedResourcePointer +{ +public: + /** Creates an instance of the shared object. + If other SharedResourcePointer objects for this type already exist, then + this one will simply point to the same shared object that they are already + using. Otherwise, if this is the first SharedResourcePointer to be created, + then a shared object will be created automatically. + */ + SharedResourcePointer() + { + SharedObjectHolder& holder = getSharedObjectHolder(); + const SpinLock::ScopedLockType sl (holder.lock); + + if (++(holder.refCount) == 1) + holder.sharedInstance = new SharedObjectType(); + + sharedObject = holder.sharedInstance; + } + + /** Destructor. + If no other SharedResourcePointer objects exist, this will also delete + the shared object to which it refers. + */ + ~SharedResourcePointer() + { + SharedObjectHolder& holder = getSharedObjectHolder(); + const SpinLock::ScopedLockType sl (holder.lock); + + if (--(holder.refCount) == 0) + holder.sharedInstance = nullptr; + } + + /** Returns the shared object. */ + operator SharedObjectType*() const noexcept { return sharedObject; } + + /** Returns the shared object. */ + SharedObjectType& get() const noexcept { return *sharedObject; } + + /** Returns the object that this pointer references. + The pointer returned may be zero, of course. + */ + SharedObjectType& getObject() const noexcept { return *sharedObject; } + + SharedObjectType* operator->() const noexcept { return sharedObject; } + +private: + struct SharedObjectHolder : public ReferenceCountedObject + { + SpinLock lock; + ScopedPointer sharedInstance; + int refCount; + }; + + static SharedObjectHolder& getSharedObjectHolder() noexcept + { + static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { 0 }; + return *reinterpret_cast (holder); + } + + SharedObjectType* sharedObject; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedResourcePointer) +}; + + +#endif // JUCE_SHAREDRESOURCEPOINTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h b/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h index b9b6e82..00ea0af 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_Singleton.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SINGLETON_JUCEHEADER__ -#define __JUCE_SINGLETON_JUCEHEADER__ +#ifndef JUCE_SINGLETON_H_INCLUDED +#define JUCE_SINGLETON_H_INCLUDED //============================================================================== @@ -286,4 +289,4 @@ -#endif // __JUCE_SINGLETON_JUCEHEADER__ +#endif // JUCE_SINGLETON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h b/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h index 030e641..b56808a 100644 --- a/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h +++ b/JuceLibraryCode/modules/juce_core/memory/juce_WeakReference.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_WEAKREFERENCE_JUCEHEADER__ -#define __JUCE_WEAKREFERENCE_JUCEHEADER__ - -#include "juce_ReferenceCountedObject.h" +#ifndef JUCE_WEAKREFERENCE_H_INCLUDED +#define JUCE_WEAKREFERENCE_H_INCLUDED //============================================================================== @@ -133,7 +134,7 @@ public: class SharedPointer : public ReferenceCountingType { public: - explicit SharedPointer (ObjectType* const owner_) noexcept : owner (owner_) {} + explicit SharedPointer (ObjectType* const obj) noexcept : owner (obj) {} inline ObjectType* get() const noexcept { return owner; } void clearPointer() noexcept { owner = nullptr; } @@ -141,7 +142,7 @@ public: private: ObjectType* volatile owner; - JUCE_DECLARE_NON_COPYABLE (SharedPointer); + JUCE_DECLARE_NON_COPYABLE (SharedPointer) }; typedef ReferenceCountedObjectPtr SharedRef; @@ -195,7 +196,7 @@ public: private: SharedRef sharedPointer; - JUCE_DECLARE_NON_COPYABLE (Master); + JUCE_DECLARE_NON_COPYABLE (Master) }; private: @@ -208,4 +209,4 @@ private: }; -#endif // __JUCE_WEAKREFERENCE_JUCEHEADER__ +#endif // JUCE_WEAKREFERENCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp index d25dba9..1734907 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Result.cpp @@ -1,28 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ +Result::Result() noexcept {} + Result::Result (const String& message) noexcept : errorMessage (message) { @@ -62,11 +67,6 @@ bool Result::operator!= (const Result& other) const noexcept return errorMessage != other.errorMessage; } -Result Result::ok() noexcept -{ - return Result (String::empty); -} - Result Result::fail (const String& errorMessage) noexcept { return Result (errorMessage.isEmpty() ? "Unknown Error" : errorMessage); diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Result.h b/JuceLibraryCode/modules/juce_core/misc/juce_Result.h index 03225c8..7c320ac 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Result.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Result.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_RESULT_JUCEHEADER__ -#define __JUCE_RESULT_JUCEHEADER__ - -#include "../text/juce_String.h" +#ifndef JUCE_RESULT_H_INCLUDED +#define JUCE_RESULT_H_INCLUDED //============================================================================== @@ -62,7 +63,7 @@ class JUCE_API Result public: //============================================================================== /** Creates and returns a 'successful' result. */ - static Result ok() noexcept; + static Result ok() noexcept { return Result(); } /** Creates a 'failure' result. If you pass a blank error message in here, a default "Unknown Error" message @@ -96,12 +97,12 @@ public: const String& getErrorMessage() const noexcept; //============================================================================== - Result (const Result& other); - Result& operator= (const Result& other); + Result (const Result&); + Result& operator= (const Result&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - Result (Result&& other) noexcept; - Result& operator= (Result&& other) noexcept; + Result (Result&&) noexcept; + Result& operator= (Result&&) noexcept; #endif bool operator== (const Result& other) const noexcept; @@ -110,6 +111,9 @@ public: private: String errorMessage; + // The default constructor is not for public use! + // Instead, use Result::ok() or Result::fail() + Result() noexcept; explicit Result (const String&) noexcept; // These casts are private to prevent people trying to use the Result object in numeric contexts @@ -118,4 +122,4 @@ private: }; -#endif // __JUCE_RESULT_JUCEHEADER__ +#endif // JUCE_RESULT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp index ecd8c44..eb74964 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp @@ -1,54 +1,41 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -namespace -{ - int64 getRandomSeedFromMACAddresses() - { - Array result; - MACAddress::findAllAddresses (result); - - Random r; - for (int i = 0; i < result.size(); ++i) - r.combineSeed (result[i].toInt64()); - - return r.nextInt64(); - } -} - -//============================================================================== Uuid::Uuid() { - // The normal random seeding is pretty good, but we'll throw some MAC addresses - // into the mix too, to make it very very unlikely that two UUIDs will ever be the same.. - - static Random r1 (getRandomSeedFromMACAddresses()); - Random r2; + Random r; for (size_t i = 0; i < sizeof (uuid); ++i) - uuid[i] = (uint8) (r1.nextInt() ^ r2.nextInt()); + uuid[i] = (uint8) (r.nextInt (256)); + + // To make it RFC 4122 compliant, need to force a few bits... + uuid[6] = (uuid[6] & 0x0f) | 0x40; + uuid[8] = (uuid[8] & 0x3f) | 0x80; } Uuid::~Uuid() noexcept {} @@ -67,6 +54,11 @@ Uuid& Uuid::operator= (const Uuid& other) noexcept bool Uuid::operator== (const Uuid& other) const noexcept { return memcmp (uuid, other.uuid, sizeof (uuid)) == 0; } bool Uuid::operator!= (const Uuid& other) const noexcept { return ! operator== (other); } +Uuid Uuid::null() noexcept +{ + return Uuid ((const uint8*) nullptr); +} + bool Uuid::isNull() const noexcept { for (size_t i = 0; i < sizeof (uuid); ++i) @@ -76,9 +68,23 @@ bool Uuid::isNull() const noexcept return true; } +String Uuid::getHexRegion (int start, int length) const +{ + return String::toHexString (uuid + start, length, 0); +} + String Uuid::toString() const { - return String::toHexString (uuid, sizeof (uuid), 0); + return getHexRegion (0, 16); +} + +String Uuid::toDashedString() const +{ + return getHexRegion (0, 4) + + "-" + getHexRegion (4, 2) + + "-" + getHexRegion (6, 2) + + "-" + getHexRegion (8, 2) + + "-" + getHexRegion (10, 6); } Uuid::Uuid (const String& uuidString) @@ -95,7 +101,7 @@ Uuid& Uuid::operator= (const String& uuidString) return *this; } -Uuid::Uuid (const uint8* const rawData) +Uuid::Uuid (const uint8* const rawData) noexcept { operator= (rawData); } diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h index 8a1fc39..cd7af0d 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.h @@ -1,41 +1,42 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_UUID_JUCEHEADER__ -#define __JUCE_UUID_JUCEHEADER__ - -#include "../text/juce_String.h" +#ifndef JUCE_UUID_H_INCLUDED +#define JUCE_UUID_H_INCLUDED //============================================================================== /** 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. + This class generates very random unique numbers. It's vanishingly unlikely + that two identical UUIDs would ever be created by chance. The values are + formatted to meet the RFC 4122 version 4 standard. The class includes methods for saving the ID as a string or as raw binary data. */ @@ -43,24 +44,27 @@ class JUCE_API Uuid { public: //============================================================================== - /** Creates a new unique ID. */ + /** Creates a new unique ID, compliant with RFC 4122 version 4. */ Uuid(); /** Destructor. */ ~Uuid() noexcept; /** Creates a copy of another UUID. */ - Uuid (const Uuid& other) noexcept; + Uuid (const Uuid&) noexcept; /** Copies another UUID. */ - Uuid& operator= (const Uuid& other) noexcept; + Uuid& operator= (const Uuid&) noexcept; //============================================================================== /** Returns true if the ID is zero. */ bool isNull() const noexcept; - bool operator== (const Uuid& other) const noexcept; - bool operator!= (const Uuid& other) const noexcept; + /** Returns a null Uuid object. */ + static Uuid null() noexcept; + + bool operator== (const Uuid&) const noexcept; + bool operator!= (const Uuid&) const noexcept; //============================================================================== /** Returns a stringified version of this UUID. @@ -72,6 +76,11 @@ public: */ String toString() const; + /** Returns a stringified version of this UUID, separating it into sections with dashes. + @returns a string in the format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + */ + String toDashedString() const; + /** Creates an ID from an encoded string version. @see toString */ @@ -94,7 +103,7 @@ public: /** Creates a UUID from a 16-byte array. @see getRawData */ - Uuid (const uint8* rawData); + Uuid (const uint8* rawData) noexcept; /** Sets this UUID from 16-bytes of raw data. */ Uuid& operator= (const uint8* rawData) noexcept; @@ -103,9 +112,10 @@ public: private: //============================================================================== uint8 uuid[16]; + String getHexRegion (int, int) const; - JUCE_LEAK_DETECTOR (Uuid); + JUCE_LEAK_DETECTOR (Uuid) }; -#endif // __JUCE_UUID_JUCEHEADER__ +#endif // JUCE_UUID_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h b/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h index 0e82fe5..a1fb2a2 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h +++ b/JuceLibraryCode/modules/juce_core/misc/juce_WindowsRegistry.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_WINDOWSREGISTRY_JUCEHEADER__ -#define __JUCE_WINDOWSREGISTRY_JUCEHEADER__ +#ifndef JUCE_WINDOWSREGISTRY_H_INCLUDED +#define JUCE_WINDOWSREGISTRY_H_INCLUDED #if JUCE_WINDOWS || DOXYGEN @@ -32,59 +35,72 @@ Contains some static helper functions for manipulating the MS Windows registry (Only available on Windows, of course!) */ -class WindowsRegistry +class JUCE_API WindowsRegistry { public: + /** These values can be used to specify whether the 32- or 64-bit registry should be used. + When running on a 32-bit OS, there is no 64-bit registry, so the mode will be ignored. + */ + enum WoW64Mode + { + /** Default handling: 32-bit apps will use the 32-bit registry, and 64-bit apps + will use the 64-bit registry. */ + WoW64_Default = 0, + + /** Always use the 64-bit registry store. (KEY_WOW64_64KEY). */ + WoW64_64bit = 0x100, + + /** Always use the 32-bit registry store. (KEY_WOW64_32KEY). */ + WoW64_32bit = 0x200 + }; + //============================================================================== /** 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 String getValue (const String& regValuePath, - const String& defaultValue = String::empty); - - /** Returns a string from the WOW64 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 String getValueWow64 (const String& regValuePath, - const String& defaultValue = String::empty); + static String JUCE_CALLTYPE getValue (const String& regValuePath, + const String& defaultValue = String::empty, + WoW64Mode mode = WoW64_Default); /** Reads a binary block 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" @returns a DWORD indicating the type of the key. */ - static uint32 getBinaryValue (const String& regValuePath, MemoryBlock& resultData); + static uint32 JUCE_CALLTYPE getBinaryValue (const String& regValuePath, MemoryBlock& resultData, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a string. This will take care of creating any groups needed to get to the given registry value. */ - static bool setValue (const String& regValuePath, const String& value); + static bool JUCE_CALLTYPE setValue (const String& regValuePath, const String& value, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a DWORD. This will take care of creating any groups needed to get to the given registry value. */ - static bool setValue (const String& regValuePath, uint32 value); + static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint32 value, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a QWORD. This will take care of creating any groups needed to get to the given registry value. */ - static bool setValue (const String& regValuePath, uint64 value); + static bool JUCE_CALLTYPE setValue (const String& regValuePath, uint64 value, WoW64Mode mode = WoW64_Default); /** Sets a registry value as a binary block. This will take care of creating any groups needed to get to the given registry value. */ - static bool setValue (const String& regValuePath, const MemoryBlock& value); + static bool JUCE_CALLTYPE setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode = WoW64_Default); /** Returns true if the given value exists in the registry. */ - static bool valueExists (const String& regValuePath); + static bool JUCE_CALLTYPE valueExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); + + /** Returns true if the given key exists in the registry. */ + static bool JUCE_CALLTYPE keyExists (const String& regValuePath, WoW64Mode mode = WoW64_Default); /** Deletes a registry value. */ - static void deleteValue (const String& regValuePath); + static void JUCE_CALLTYPE deleteValue (const String& regValuePath, WoW64Mode mode = WoW64_Default); /** Deletes a registry key (which is registry-talk for 'folder'). */ - static void deleteKey (const String& regKeyPath); + static void JUCE_CALLTYPE deleteKey (const String& regKeyPath, WoW64Mode mode = WoW64_Default); /** Creates a file association in the registry. @@ -101,18 +117,25 @@ public: for all users (you might not have permission to do this unless running in an installer). If true, it will register the association in HKEY_CURRENT_USER. + @param mode the WoW64 mode to use for choosing the database */ - static bool registerFileAssociation (const String& fileExtension, - const String& symbolicDescription, - const String& fullDescription, - const File& targetExecutable, - int iconResourceNumber, - bool registerForCurrentUserOnly); + static bool JUCE_CALLTYPE registerFileAssociation (const String& fileExtension, + const String& symbolicDescription, + const String& fullDescription, + const File& targetExecutable, + int iconResourceNumber, + bool registerForCurrentUserOnly, + WoW64Mode mode = WoW64_Default); + + // DEPRECATED: use the other methods with a WoW64Mode parameter of WoW64_64bit instead. + JUCE_DEPRECATED (static String getValueWow64 (const String&, const String& defaultValue = String::empty)); + JUCE_DEPRECATED (static bool valueExistsWow64 (const String&)); + JUCE_DEPRECATED (static bool keyExistsWow64 (const String&)); private: - WindowsRegistry(); - JUCE_DECLARE_NON_COPYABLE (WindowsRegistry); + WindowsRegistry() JUCE_DELETED_FUNCTION; + JUCE_DECLARE_NON_COPYABLE (WindowsRegistry) }; #endif -#endif // __JUCE_WINDOWSREGISTRY_JUCEHEADER__ +#endif // JUCE_WINDOWSREGISTRY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/native/java/JuceAppActivity.java b/JuceLibraryCode/modules/juce_core/native/java/JuceAppActivity.java index e64abf3..882d0e0 100644 --- a/JuceLibraryCode/modules/juce_core/native/java/JuceAppActivity.java +++ b/JuceLibraryCode/modules/juce_core/native/java/JuceAppActivity.java @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-10 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -30,12 +29,19 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Context; import android.content.Intent; +import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; import android.view.*; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; import android.graphics.*; import android.opengl.*; import android.text.ClipboardManager; +import android.text.InputType; +import android.util.DisplayMetrics; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -44,6 +50,9 @@ import java.net.URL; import java.net.HttpURLConnection; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; +import android.media.AudioManager; +import android.media.MediaScannerConnection; +import android.media.MediaScannerConnection.MediaScannerConnectionClient; //============================================================================== public final class JuceAppActivity extends Activity @@ -61,6 +70,8 @@ public final class JuceAppActivity extends Activity viewHolder = new ViewHolder (this); setContentView (viewHolder); + + setVolumeControlStream (AudioManager.STREAM_MUSIC); } @Override @@ -70,6 +81,34 @@ public final class JuceAppActivity extends Activity super.onDestroy(); } + @Override + protected final void onPause() + { + if (viewHolder != null) + viewHolder.onPause(); + + suspendApp(); + super.onPause(); + } + + @Override + protected final void onResume() + { + super.onResume(); + + if (viewHolder != null) + viewHolder.onResume(); + + resumeApp(); + } + + @Override + public void onConfigurationChanged (Configuration cfg) + { + super.onConfigurationChanged (cfg); + setContentView (viewHolder); + } + private void callAppLauncher() { launchApp (getApplicationInfo().publicSourceDir, @@ -79,7 +118,9 @@ public final class JuceAppActivity extends Activity //============================================================================== private native void launchApp (String appFile, String appDataDir); private native void quitApp(); - private native void setScreenSize (int screenWidth, int screenHeight); + private native void suspendApp(); + private native void resumeApp(); + private native void setScreenSize (int screenWidth, int screenHeight, int dpi); //============================================================================== public native void deliverMessage (long value); @@ -101,9 +142,9 @@ public final class JuceAppActivity extends Activity //============================================================================== private ViewHolder viewHolder; - public final ComponentPeerView createNewView (boolean opaque) + public final ComponentPeerView createNewView (boolean opaque, long host) { - ComponentPeerView v = new ComponentPeerView (this, opaque); + ComponentPeerView v = new ComponentPeerView (this, opaque, host); viewHolder.addView (v); return v; } @@ -127,7 +168,7 @@ public final class JuceAppActivity extends Activity protected final void onLayout (boolean changed, int left, int top, int right, int bottom) { - setScreenSize (getWidth(), getHeight()); + setScreenSize (getWidth(), getHeight(), getDPI()); if (isFirstResize) { @@ -136,6 +177,35 @@ public final class JuceAppActivity extends Activity } } + public final void onPause() + { + for (int i = getChildCount(); --i >= 0;) + { + View v = getChildAt (i); + + if (v instanceof ComponentPeerView) + ((ComponentPeerView) v).onPause(); + } + } + + public final void onResume() + { + for (int i = getChildCount(); --i >= 0;) + { + View v = getChildAt (i); + + if (v instanceof ComponentPeerView) + ((ComponentPeerView) v).onResume(); + } + } + + private final int getDPI() + { + DisplayMetrics metrics = new DisplayMetrics(); + getWindowManager().getDefaultDisplay().getMetrics (metrics); + return metrics.densityDpi; + } + private boolean isFirstResize = true; } @@ -242,9 +312,10 @@ public final class JuceAppActivity extends Activity public final class ComponentPeerView extends ViewGroup implements View.OnFocusChangeListener { - public ComponentPeerView (Context context, boolean opaque_) + public ComponentPeerView (Context context, boolean opaque_, long host) { super (context); + this.host = host; setWillNotDraw (false); opaque = opaque_; @@ -255,13 +326,12 @@ public final class JuceAppActivity extends Activity } //============================================================================== - private native void handlePaint (Canvas canvas); + private native void handlePaint (long host, Canvas canvas); @Override - public void draw (Canvas canvas) + public void onDraw (Canvas canvas) { - super.draw (canvas); - handlePaint (canvas); + handlePaint (host, canvas); } @Override @@ -271,33 +341,126 @@ public final class JuceAppActivity extends Activity } private boolean opaque; + private long host; //============================================================================== - private native void handleMouseDown (float x, float y, long time); - private native void handleMouseDrag (float x, float y, long time); - private native void handleMouseUp (float x, float y, long time); + private native void handleMouseDown (long host, int index, float x, float y, long time); + private native void handleMouseDrag (long host, int index, float x, float y, long time); + private native void handleMouseUp (long host, int index, float x, float y, long time); @Override public boolean onTouchEvent (MotionEvent event) { - switch (event.getAction()) + int action = event.getAction(); + long time = event.getEventTime(); + + switch (action & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: handleMouseDown (event.getX(), event.getY(), event.getEventTime()); return true; - case MotionEvent.ACTION_MOVE: handleMouseDrag (event.getX(), event.getY(), event.getEventTime()); return true; + case MotionEvent.ACTION_DOWN: + handleMouseDown (host, event.getPointerId(0), event.getX(), event.getY(), time); + return true; + case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: handleMouseUp (event.getX(), event.getY(), event.getEventTime()); return true; - default: break; + case MotionEvent.ACTION_UP: + handleMouseUp (host, event.getPointerId(0), event.getX(), event.getY(), time); + return true; + + case MotionEvent.ACTION_MOVE: + { + int n = event.getPointerCount(); + for (int i = 0; i < n; ++i) + handleMouseDrag (host, event.getPointerId(i), event.getX(i), event.getY(i), time); + + return true; + } + + case MotionEvent.ACTION_POINTER_UP: + { + int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + handleMouseUp (host, event.getPointerId(i), event.getX(i), event.getY(i), time); + return true; + } + + case MotionEvent.ACTION_POINTER_DOWN: + { + int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + handleMouseDown (host, event.getPointerId(i), event.getX(i), event.getY(i), time); + return true; + } + + default: + break; } return false; } + //============================================================================== + private native void handleKeyDown (long host, int keycode, int textchar); + private native void handleKeyUp (long host, int keycode, int textchar); + + public void showKeyboard (String type) + { + InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE); + + if (imm != null) + { + if (type.length() > 0) + { + imm.showSoftInput (this, android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT); + imm.setInputMethod (getWindowToken(), type); + } + else + { + imm.hideSoftInputFromWindow (getWindowToken(), 0); + } + } + } + + @Override + public boolean onKeyDown (int keyCode, KeyEvent event) + { + switch (keyCode) + { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + return super.onKeyDown (keyCode, event); + + default: break; + } + + handleKeyDown (host, keyCode, event.getUnicodeChar()); + return true; + } + + @Override + public boolean onKeyUp (int keyCode, KeyEvent event) + { + handleKeyUp (host, keyCode, event.getUnicodeChar()); + return true; + } + + // this is here to make keyboard entry work on a Galaxy Tab2 10.1 + @Override + public InputConnection onCreateInputConnection (EditorInfo outAttrs) + { + outAttrs.actionLabel = ""; + outAttrs.hintText = ""; + outAttrs.initialCapsMode = 0; + outAttrs.initialSelEnd = outAttrs.initialSelStart = -1; + outAttrs.label = ""; + outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI; + outAttrs.inputType = InputType.TYPE_NULL; + + return new BaseInputConnection (this, false); + } + //============================================================================== @Override protected void onSizeChanged (int w, int h, int oldw, int oldh) { super.onSizeChanged (w, h, oldw, oldh); - viewSizeChanged(); + viewSizeChanged (host); } @Override @@ -307,16 +470,16 @@ public final class JuceAppActivity extends Activity requestTransparentRegion (getChildAt (i)); } - private native void viewSizeChanged(); + private native void viewSizeChanged (long host); @Override public void onFocusChange (View v, boolean hasFocus) { if (v == this) - focusChanged (hasFocus); + focusChanged (host, hasFocus); } - private native void focusChanged (boolean hasFocus); + private native void focusChanged (long host, boolean hasFocus); public void setViewName (String newName) {} @@ -328,6 +491,28 @@ public final class JuceAppActivity extends Activity return true; //xxx needs to check overlapping views } + public final void onPause() + { + for (int i = getChildCount(); --i >= 0;) + { + View v = getChildAt (i); + + if (v instanceof OpenGLView) + ((OpenGLView) v).onPause(); + } + } + + public final void onResume() + { + for (int i = getChildCount(); --i >= 0;) + { + View v = getChildAt (i); + + if (v instanceof OpenGLView) + ((OpenGLView) v).onResume(); + } + } + public OpenGLView createGLView() { OpenGLView glView = new OpenGLView (getContext()); @@ -386,7 +571,7 @@ public final class JuceAppActivity extends Activity bounds.right++; final int w = bounds.width(); - final int h = bounds.height(); + final int h = Math.max (1, bounds.height()); Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888); @@ -409,10 +594,34 @@ public final class JuceAppActivity extends Activity //============================================================================== public static class HTTPStream { - public HTTPStream (HttpURLConnection connection_) throws IOException + public HTTPStream (HttpURLConnection connection_, + int[] statusCode, StringBuffer responseHeaders) throws IOException { connection = connection_; - inputStream = new BufferedInputStream (connection.getInputStream()); + + try + { + inputStream = new BufferedInputStream (connection.getInputStream()); + } + catch (IOException e) + { + if (connection.getResponseCode() < org.apache.http.HttpStatus.SC_BAD_REQUEST) + throw e; + } + finally + { + statusCode[0] = connection.getResponseCode(); + } + + if (statusCode[0] >= org.apache.http.HttpStatus.SC_BAD_REQUEST) + inputStream = connection.getErrorStream(); + else + inputStream = connection.getInputStream(); + + for (java.util.Map.Entry> entry : connection.getHeaderFields().entrySet()) + if (entry.getKey() != null && entry.getValue() != null) + responseHeaders.append (entry.getKey() + ": " + + android.text.TextUtils.join (",", entry.getValue()) + "\n"); } public final void release() @@ -454,30 +663,31 @@ public final class JuceAppActivity extends Activity private long position; } - public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData, - String headers, int timeOutMs, - java.lang.StringBuffer responseHeaders) + public static final HTTPStream createHTTPStream (String address, + boolean isPost, byte[] postData, String headers, + int timeOutMs, int[] statusCode, + StringBuffer responseHeaders) { try { - HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection()); - + HttpURLConnection connection = (HttpURLConnection) (new URL(address) + .openConnection()); if (connection != null) { try { if (isPost) { - connection.setConnectTimeout (timeOutMs); - connection.setDoOutput (true); - connection.setChunkedStreamingMode (0); - + connection.setRequestMethod("POST"); + connection.setConnectTimeout(timeOutMs); + connection.setDoOutput(true); + connection.setChunkedStreamingMode(0); OutputStream out = connection.getOutputStream(); - out.write (postData); + out.write(postData); out.flush(); } - return new HTTPStream (connection); + return new HTTPStream (connection, statusCode, responseHeaders); } catch (Throwable e) { @@ -485,8 +695,7 @@ public final class JuceAppActivity extends Activity } } } - catch (Throwable e) - {} + catch (Throwable e) {} return null; } @@ -495,4 +704,43 @@ public final class JuceAppActivity extends Activity { startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url))); } + + public static final String getLocaleValue (boolean isRegion) + { + java.util.Locale locale = java.util.Locale.getDefault(); + + return isRegion ? locale.getDisplayCountry (java.util.Locale.US) + : locale.getDisplayLanguage (java.util.Locale.US); + } + + //============================================================================== + private final class SingleMediaScanner implements MediaScannerConnectionClient + { + public SingleMediaScanner (Context context, String filename) + { + file = filename; + msc = new MediaScannerConnection (context, this); + msc.connect(); + } + + @Override + public void onMediaScannerConnected() + { + msc.scanFile (file, null); + } + + @Override + public void onScanCompleted (String path, Uri uri) + { + msc.disconnect(); + } + + private MediaScannerConnection msc; + private String file; + } + + public final void scanFile (String filename) + { + new SingleMediaScanner (this, filename); + } } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h index 8ceaf1b..cdaa0ef 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_BasicNativeHeaders.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_BASICNATIVEHEADERS_JUCEHEADER__ -#define __JUCE_BASICNATIVEHEADERS_JUCEHEADER__ +#ifndef JUCE_BASICNATIVEHEADERS_H_INCLUDED +#define JUCE_BASICNATIVEHEADERS_H_INCLUDED #include "../system/juce_TargetPlatform.h" #undef T @@ -62,6 +65,9 @@ #include #include #include + #include + #include + #include //============================================================================== #elif JUCE_WINDOWS @@ -80,17 +86,17 @@ #define STRICT 1 #define WIN32_LEAN_AND_MEAN 1 - #define _WIN32_WINNT 0x0600 + #if JUCE_MINGW + #define _WIN32_WINNT 0x0501 + #else + #define _WIN32_WINNT 0x0600 + #endif #define _UNICODE 1 #define UNICODE 1 #ifndef _WIN32_IE - #define _WIN32_IE 0x0400 + #define _WIN32_IE 0x0500 #endif - #if JUCE_MINGW - #include - #endif - #include #include #include @@ -106,7 +112,9 @@ #include #include - #if ! JUCE_MINGW + #if JUCE_MINGW + #include + #else #include #include #endif @@ -121,7 +129,6 @@ #if JUCE_MSVC && ! JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES #pragma comment (lib, "kernel32.lib") #pragma comment (lib, "user32.lib") - #pragma comment (lib, "shell32.lib") #pragma comment (lib, "wininet.lib") #pragma comment (lib, "advapi32.lib") #pragma comment (lib, "ws2_32.lib") @@ -144,17 +151,17 @@ #endif #endif - /* Used with DynamicLibrary to simplify importing functions + /* Used with DynamicLibrary to simplify importing functions from a win32 DLL. + dll: the DynamicLibrary object functionName: function to import localFunctionName: name you want to use to actually call it (must be different) returnType: the return type - object: the DynamicLibrary to use params: list of params (bracketed) */ - #define JUCE_DLL_FUNCTION(functionName, localFunctionName, returnType, object, params) \ + #define JUCE_LOAD_WINAPI_FUNCTION(dll, functionName, localFunctionName, returnType, params) \ typedef returnType (WINAPI *type##localFunctionName) params; \ - type##localFunctionName localFunctionName = (type##localFunctionName)object.getFunction (#functionName); + type##localFunctionName localFunctionName = (type##localFunctionName) dll.getFunction (#functionName); //============================================================================== #elif JUCE_LINUX @@ -184,6 +191,7 @@ #include #include #include + #include //============================================================================== #elif JUCE_ANDROID @@ -212,4 +220,4 @@ #undef direct #undef check -#endif // __JUCE_BASICNATIVEHEADERS_JUCEHEADER__ +#endif // JUCE_BASICNATIVEHEADERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp index d75d20c..45903b1 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp @@ -1,56 +1,31 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -bool File::copyInternal (const File& dest) const -{ - FileInputStream in (*this); - - if (dest.deleteFile()) - { - { - FileOutputStream out (dest); - - if (out.failedToOpen()) - return false; - - if (out.writeFromInputStream (in, -1) == getSize()) - return true; - } - - dest.deleteFile(); - } - - return false; -} - -void File::findFileSystemRoots (Array& destArray) -{ - destArray.add (File ("/")); -} - -//============================================================================== bool File::isOnCDRomDrive() const { return false; @@ -66,170 +41,62 @@ bool File::isOnRemovableDrive() const return false; } -bool File::isHidden() const -{ - return getFileName().startsWithChar ('.'); -} - -//============================================================================== -namespace -{ - File juce_readlink (const String& file, const File& defaultFile) - { - const int size = 8192; - HeapBlock buffer; - buffer.malloc (size + 4); - - const size_t numBytes = readlink (file.toUTF8(), buffer, size); - - if (numBytes > 0 && numBytes <= size) - return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); - - return defaultFile; - } -} - -File File::getLinkedTarget() const -{ - return juce_readlink (getFullPathName().toUTF8(), *this); -} - -//============================================================================== -File File::getSpecialLocation (const SpecialLocationType type) -{ - switch (type) - { - case userHomeDirectory: - case userDocumentsDirectory: - case userMusicDirectory: - case userMoviesDirectory: - case userApplicationDataDirectory: - case userDesktopDirectory: - return File (android.appDataDir); - - case commonApplicationDataDirectory: - return File (android.appDataDir); - - case globalApplicationsDirectory: - return File ("/system/app"); - - case tempDirectory: - //return File (AndroidStatsHelpers::getSystemProperty ("java.io.tmpdir")); - return File (android.appDataDir).getChildFile (".temp"); - - case invokedExecutableFile: - case currentExecutableFile: - case currentApplicationFile: - case hostApplicationPath: - return juce_getExecutableFile(); - - default: - jassertfalse; // unknown type? - break; - } - - return File::nonexistent; -} - -//============================================================================== String File::getVersion() const { return String::empty; } -//============================================================================== +File File::getSpecialLocation (const SpecialLocationType type) +{ + switch (type) + { + case userHomeDirectory: + case userDocumentsDirectory: + case userMusicDirectory: + case userMoviesDirectory: + case userPicturesDirectory: + case userApplicationDataDirectory: + case userDesktopDirectory: + return File (android.appDataDir); + + case commonApplicationDataDirectory: + case commonDocumentsDirectory: + return File (android.appDataDir); + + case globalApplicationsDirectory: + return File ("/system/app"); + + case tempDirectory: + return File (android.appDataDir).getChildFile (".temp"); + + case invokedExecutableFile: + case currentExecutableFile: + case currentApplicationFile: + case hostApplicationPath: + return juce_getExecutableFile(); + + default: + jassertfalse; // unknown type? + break; + } + + return File(); +} + bool File::moveToTrash() const { if (! exists()) return true; // TODO - return false; } -//============================================================================== -class DirectoryIterator::NativeIterator::Pimpl -{ -public: - Pimpl (const File& directory, const String& wildCard_) - : parentDir (File::addTrailingSeparator (directory.getFullPathName())), - wildCard (wildCard_), - dir (opendir (directory.getFullPathName().toUTF8())) - { - } - - ~Pimpl() - { - if (dir != 0) - closedir (dir); - } - - bool next (String& filenameFound, - bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) - { - if (dir != 0) - { - const char* wildcardUTF8 = nullptr; - - for (;;) - { - struct dirent* const de = readdir (dir); - - if (de == nullptr) - break; - - if (wildcardUTF8 == nullptr) - wildcardUTF8 = wildCard.toUTF8(); - - if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) - { - filenameFound = CharPointer_UTF8 (de->d_name); - - updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); - - if (isHidden != 0) - *isHidden = filenameFound.startsWithChar ('.'); - - return true; - } - } - } - - return false; - } - -private: - String parentDir, wildCard; - DIR* dir; - - JUCE_DECLARE_NON_COPYABLE (Pimpl); -}; - - -DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) - : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) -{ -} - -DirectoryIterator::NativeIterator::~NativeIterator() -{ -} - -bool DirectoryIterator::NativeIterator::next (String& filenameFound, - bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) -{ - return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); -} - - -//============================================================================== -bool Process::openDocument (const String& fileName, const String& parameters) +JUCE_API bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { const LocalRef t (javaString (fileName)); android.activity.callVoidMethod (JuceAppActivity.launchURL, t.get()); + return true; } void File::revealToUser() const diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h index 4f9bd7b..044679e 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_JNIHelpers.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_ANDROID_JNIHELPERS_JUCEHEADER__ -#define __JUCE_ANDROID_JNIHELPERS_JUCEHEADER__ +#ifndef JUCE_ANDROID_JNIHELPERS_H_INCLUDED +#define JUCE_ANDROID_JNIHELPERS_H_INCLUDED #if ! (defined (JUCE_ANDROID_ACTIVITY_CLASSNAME) && defined (JUCE_ANDROID_ACTIVITY_CLASSPATH)) #error "The JUCE_ANDROID_ACTIVITY_CLASSNAME and JUCE_ANDROID_ACTIVITY_CLASSPATH macros must be set!" @@ -38,7 +41,7 @@ class GlobalRef { public: inline GlobalRef() noexcept : obj (0) {} - inline explicit GlobalRef (jobject obj_) : obj (retain (obj_)) {} + inline explicit GlobalRef (jobject o) : obj (retain (o)) {} inline GlobalRef (const GlobalRef& other) : obj (retain (other.obj)) {} ~GlobalRef() { clear(); } @@ -97,9 +100,9 @@ private: //============================================================================== jobject obj; - static inline jobject retain (jobject obj_) + static inline jobject retain (jobject obj) { - return obj_ == 0 ? 0 : getEnv()->NewGlobalRef (obj_); + return obj == 0 ? 0 : getEnv()->NewGlobalRef (obj); } }; @@ -108,7 +111,7 @@ template class LocalRef { public: - explicit inline LocalRef (JavaType obj_) noexcept : obj (obj_) {} + explicit inline LocalRef (JavaType o) noexcept : obj (o) {} inline LocalRef (const LocalRef& other) noexcept : obj (retain (other.obj)) {} ~LocalRef() { clear(); } @@ -132,9 +135,9 @@ public: private: JavaType obj; - static JavaType retain (JavaType obj_) + static JavaType retain (JavaType obj) { - return obj_ == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj_); + return obj == 0 ? 0 : (JavaType) getEnv()->NewLocalRef (obj); } }; @@ -172,7 +175,7 @@ namespace class JNIClassBase { public: - explicit JNIClassBase (const char* classPath_); + explicit JNIClassBase (const char* classPath); virtual ~JNIClassBase(); inline operator jclass() const noexcept { return classRef; } @@ -196,7 +199,7 @@ private: void initialise (JNIEnv*); void release (JNIEnv*); - JUCE_DECLARE_NON_COPYABLE (JNIClassBase); + JUCE_DECLARE_NON_COPYABLE (JNIClassBase) }; //============================================================================== @@ -239,7 +242,7 @@ public: //============================================================================== GlobalRef activity; String appFile, appDataDir; - int screenWidth, screenHeight; + int screenWidth, screenHeight, dpi; }; extern AndroidSystem android; @@ -248,7 +251,7 @@ extern AndroidSystem android; class ThreadLocalJNIEnvHolder { public: - ThreadLocalJNIEnvHolder() + ThreadLocalJNIEnvHolder() noexcept : jvm (nullptr) { zeromem (threads, sizeof (threads)); @@ -266,18 +269,19 @@ public: addEnv (env); } - JNIEnv* attach() + JNIEnv* attach() noexcept { - JNIEnv* env = nullptr; - jvm->AttachCurrentThread (&env, nullptr); + if (JNIEnv* env = attachToCurrentThread()) + { + SpinLock::ScopedLockType sl (addRemoveLock); + return addEnv (env); + } - if (env != nullptr) - addEnv (env); - - return env; + jassertfalse; + return nullptr; } - void detach() + void detach() noexcept { jvm->DetachCurrentThread(); @@ -291,13 +295,43 @@ public: JNIEnv* getOrAttach() noexcept { - JNIEnv* env = get(); + if (JNIEnv* env = get()) + return env; - if (env == nullptr) - env = attach(); + SpinLock::ScopedLockType sl (addRemoveLock); - jassert (env != nullptr); - return env; + if (JNIEnv* env = get()) + return env; + + if (JNIEnv* env = attachToCurrentThread()) + return addEnv (env); + + return nullptr; + } + +private: + JavaVM* jvm; + enum { maxThreads = 32 }; + pthread_t threads [maxThreads]; + JNIEnv* envs [maxThreads]; + SpinLock addRemoveLock; + + JNIEnv* addEnv (JNIEnv* env) noexcept + { + const pthread_t thisThread = pthread_self(); + + for (int i = 0; i < maxThreads; ++i) + { + if (threads[i] == 0) + { + envs[i] = env; + threads[i] = thisThread; + return env; + } + } + + jassertfalse; // too many threads! + return nullptr; } JNIEnv* get() const noexcept @@ -311,34 +345,11 @@ public: return nullptr; } - enum { maxThreads = 32 }; - -private: - JavaVM* jvm; - pthread_t threads [maxThreads]; - JNIEnv* envs [maxThreads]; - SpinLock addRemoveLock; - - void addEnv (JNIEnv* env) + JNIEnv* attachToCurrentThread() { - SpinLock::ScopedLockType sl (addRemoveLock); - - if (get() == nullptr) - { - const pthread_t thisThread = pthread_self(); - - for (int i = 0; i < maxThreads; ++i) - { - if (threads[i] == 0) - { - envs[i] = env; - threads[i] = thisThread; - return; - } - } - } - - jassertfalse; // too many threads! + JNIEnv* env = nullptr; + jvm->AttachCurrentThread (&env, nullptr); + return env; } }; @@ -346,7 +357,7 @@ extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ - METHOD (createNewView, "createNewView", "(Z)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ + METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \ METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \ METHOD (postMessage, "postMessage", "(J)V") \ METHOD (finish, "finish", "()V") \ @@ -354,11 +365,13 @@ extern ThreadLocalJNIEnvHolder threadLocalJNIEnvHolder; METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \ METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \ METHOD (renderGlyph, "renderGlyph", "(CLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \ - STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ + STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \ METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \ METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \ + STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \ + METHOD (scanFile, "scanFile", "(Ljava/lang/String;)V") DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH); #undef JNI_CLASS_MEMBERS @@ -399,4 +412,4 @@ DECLARE_JNI_CLASS (Matrix, "android/graphics/Matrix"); DECLARE_JNI_CLASS (RectClass, "android/graphics/Rect"); #undef JNI_CLASS_MEMBERS -#endif // __JUCE_ANDROID_JNIHELPERS_JUCEHEADER__ +#endif // JUCE_ANDROID_JNIHELPERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp index 36c1685..d26adc1 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Misc.cpp @@ -1,29 +1,32 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ void Logger::outputDebugString (const String& text) { - __android_log_print (ANDROID_LOG_INFO, "JUCE", text.toUTF8()); + __android_log_print (ANDROID_LOG_INFO, "JUCE", "%s", text.toUTF8().getAddress()); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp index 415e735..1b86dee 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Network.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -51,10 +54,10 @@ void MACAddress::findAllAddresses (Array& result) } -bool Process::openEmailWithAttachments (const String& targetEmailAddress, - const String& emailSubject, - const String& bodyText, - const StringArray& filesToAttach) +JUCE_API bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) { // TODO return false; @@ -65,10 +68,10 @@ bool Process::openEmailWithAttachments (const String& targetEmailAddress, class WebInputStream : public InputStream { public: - //============================================================================== WebInputStream (String address, bool isPost, const MemoryBlock& postData, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers, int timeOutMs, StringPairArray* responseHeaders) + : statusCode (0) { if (! address.contains ("://")) address = "http://" + address; @@ -85,6 +88,13 @@ public: LocalRef responseHeaderBuffer (env->NewObject (StringBuffer, StringBuffer.constructor)); + // Annoyingly, the android HTTP functions will choke on this call if you try to do it on the message + // thread. You'll need to move your networking code to a background thread to keep it happy.. + jassert (Thread::getCurrentThread() != nullptr); + + jintArray statusCodeArray = env->NewIntArray (1); + jassert (statusCodeArray != 0); + stream = GlobalRef (env->CallStaticObjectMethod (JuceAppActivity, JuceAppActivity.createHTTPStream, javaString (address).get(), @@ -92,8 +102,14 @@ public: postDataArray, javaString (headers).get(), (jint) timeOutMs, + statusCodeArray, responseHeaderBuffer.get())); + jint* const statusCodeElements = env->GetIntArrayElements (statusCodeArray, 0); + statusCode = statusCodeElements[0]; + env->ReleaseIntArrayElements (statusCodeArray, statusCodeElements, 0); + env->DeleteLocalRef (statusCodeArray); + if (postDataArray != 0) env->DeleteLocalRef (postDataArray); @@ -129,12 +145,14 @@ public: } //============================================================================== - bool isExhausted() { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } - int64 getTotalLength() { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } - int64 getPosition() { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } - bool setPosition (int64 wantedPos) { return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); } + bool isError() const { return stream == nullptr; } - int read (void* buffer, int bytesToRead) + bool isExhausted() override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.isExhausted); } + int64 getTotalLength() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getTotalLength) : 0; } + int64 getPosition() override { return stream != nullptr ? stream.callLongMethod (HTTPStream.getPosition) : 0; } + bool setPosition (int64 wantedPos) override { return stream != nullptr && stream.callBooleanMethod (HTTPStream.setPosition, (jlong) wantedPos); } + + int read (void* buffer, int bytesToRead) override { jassert (buffer != nullptr && bytesToRead >= 0); @@ -156,18 +174,8 @@ public: //============================================================================== GlobalRef stream; + int statusCode; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; - -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->stream != 0 ? wi.release() : nullptr; -} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp index b64064a..8223b89 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_SystemStats.cpp @@ -1,37 +1,39 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -JNIClassBase::JNIClassBase (const char* classPath_) - : classPath (classPath_), classRef (0) +JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0) { getClasses().add (this); } JNIClassBase::~JNIClassBase() { - getClasses().removeValue (this); + getClasses().removeFirstMatchingValue (this); } Array& JNIClassBase::getClasses() @@ -109,7 +111,7 @@ JNIEnv* getEnv() noexcept { DBG ("*** Call to getEnv() when system not initialised"); jassertfalse; - exit (0); + std::exit (EXIT_FAILURE); } #endif @@ -122,14 +124,14 @@ extern "C" jint JNI_OnLoad (JavaVM*, void*) } //============================================================================== -AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0) +AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160) { } -void AndroidSystem::initialise (JNIEnv* env, jobject activity_, - jstring appFile_, jstring appDataDir_) +void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir) { screenWidth = screenHeight = 0; + dpi = 160; JNIClassBase::initialiseAllClasses (env); threadLocalJNIEnvHolder.initialise (env); @@ -137,9 +139,9 @@ void AndroidSystem::initialise (JNIEnv* env, jobject activity_, systemInitialised = true; #endif - activity = GlobalRef (activity_); - appFile = juceString (env, appFile_); - appDataDir = juceString (env, appDataDir_); + activity = GlobalRef (act); + appFile = juceString (env, file); + appDataDir = juceString (env, dataDir); } void AndroidSystem::shutdown (JNIEnv* env) @@ -158,20 +160,35 @@ AndroidSystem android; //============================================================================== namespace AndroidStatsHelpers { - //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;") DECLARE_JNI_CLASS (SystemClass, "java/lang/System"); #undef JNI_CLASS_MEMBERS - //============================================================================== String getSystemProperty (const String& name) { return juceString (LocalRef ((jstring) getEnv()->CallStaticObjectMethod (SystemClass, SystemClass.getProperty, javaString (name).get()))); } + + String getLocaleValue (bool isRegion) + { + return juceString (LocalRef ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, + JuceAppActivity.getLocaleValue, + isRegion))); + } + + #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) + DECLARE_JNI_CLASS (BuildClass, "android/os/Build"); + #undef JNI_CLASS_MEMBERS + + String getAndroidOsBuildValue (const char* fieldName) + { + return juceString (LocalRef ((jstring) getEnv()->GetStaticObjectField ( + BuildClass, getEnv()->GetStaticFieldID (BuildClass, fieldName, "Ljava/lang/String;")))); + } } //============================================================================== @@ -185,6 +202,12 @@ String SystemStats::getOperatingSystemName() return "Android " + AndroidStatsHelpers::getSystemProperty ("os.version"); } +String SystemStats::getDeviceDescription() +{ + return AndroidStatsHelpers::getAndroidOsBuildValue ("MODEL") + + "-" + AndroidStatsHelpers::getAndroidOsBuildValue ("SERIAL"); +} + bool SystemStats::isOperatingSystem64Bit() { #if JUCE_64BIT @@ -224,16 +247,13 @@ int SystemStats::getPageSize() //============================================================================== String SystemStats::getLogonName() { - const char* user = getenv ("USER"); + if (const char* user = getenv ("USER")) + return CharPointer_UTF8 (user); - if (user == 0) - { - struct passwd* const pw = getpwuid (getuid()); - if (pw != 0) - user = pw->pw_name; - } + if (struct passwd* const pw = getpwuid (getuid())) + return CharPointer_UTF8 (pw->pw_name); - return CharPointer_UTF8 (user); + return String::empty; } String SystemStats::getFullUserName() @@ -250,15 +270,14 @@ String SystemStats::getComputerName() return String::empty; } -//============================================================================== -SystemStats::CPUFlags::CPUFlags() -{ - // TODO - hasMMX = false; - hasSSE = false; - hasSSE2 = false; - has3DNow = false; +String SystemStats::getUserLanguage() { return AndroidStatsHelpers::getLocaleValue (false); } +String SystemStats::getUserRegion() { return AndroidStatsHelpers::getLocaleValue (true); } +String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } + +//============================================================================== +void CPUInformation::initialise() noexcept +{ numCpus = jmax (1, sysconf (_SC_NPROCESSORS_ONLN)); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp index f8d9e74..981be9a 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Threads.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -30,7 +33,7 @@ //============================================================================== // sets the process to 0=low priority, 1=normal, 2=high, 3=realtime -void Process::setPriority (ProcessPriority prior) +JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) { // TODO @@ -59,12 +62,6 @@ void Process::setPriority (ProcessPriority prior) pthread_setschedparam (pthread_self(), policy, ¶m); } -void Process::terminate() -{ - // TODO - exit (0); -} - JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() { return false; @@ -75,5 +72,5 @@ JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() return juce_isRunningUnderDebugger(); } -void Process::raisePrivilege() {} -void Process::lowerPrivilege() {} +JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() {} +JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() {} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp new file mode 100644 index 0000000..2ce98b3 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_CommonFile.cpp @@ -0,0 +1,153 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +bool File::copyInternal (const File& dest) const +{ + FileInputStream in (*this); + + if (dest.deleteFile()) + { + { + FileOutputStream out (dest); + + if (out.failedToOpen()) + return false; + + if (out.writeFromInputStream (in, -1) == getSize()) + return true; + } + + dest.deleteFile(); + } + + return false; +} + +void File::findFileSystemRoots (Array& destArray) +{ + destArray.add (File ("/")); +} + +bool File::isHidden() const +{ + return getFileName().startsWithChar ('.'); +} + +static String getLinkedFile (StringRef file) +{ + HeapBlock buffer (8194); + const int numBytes = (int) readlink (file.text, buffer, 8192); + return String::fromUTF8 (buffer, jmax (0, numBytes)); +}; + +bool File::isLink() const +{ + return getLinkedFile (getFullPathName()).isNotEmpty(); +} + +File File::getLinkedTarget() const +{ + String f (getLinkedFile (getFullPathName())); + + if (f.isNotEmpty()) + return getSiblingFile (f); + + return *this; +} + +//============================================================================== +class DirectoryIterator::NativeIterator::Pimpl +{ +public: + Pimpl (const File& directory, const String& wc) + : parentDir (File::addTrailingSeparator (directory.getFullPathName())), + wildCard (wc), dir (opendir (directory.getFullPathName().toUTF8())) + { + } + + ~Pimpl() + { + if (dir != nullptr) + closedir (dir); + } + + bool next (String& filenameFound, + bool* const isDir, bool* const isHidden, int64* const fileSize, + Time* const modTime, Time* const creationTime, bool* const isReadOnly) + { + if (dir != nullptr) + { + const char* wildcardUTF8 = nullptr; + + for (;;) + { + struct dirent* const de = readdir (dir); + + if (de == nullptr) + break; + + if (wildcardUTF8 == nullptr) + wildcardUTF8 = wildCard.toUTF8(); + + if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) + { + filenameFound = CharPointer_UTF8 (de->d_name); + + updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); + + if (isHidden != nullptr) + *isHidden = filenameFound.startsWithChar ('.'); + + return true; + } + } + } + + return false; + } + +private: + String parentDir, wildCard; + DIR* dir; + + JUCE_DECLARE_NON_COPYABLE (Pimpl) +}; + +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +{ +} + +DirectoryIterator::NativeIterator::~NativeIterator() {} + +bool DirectoryIterator::NativeIterator::next (String& filenameFound, + bool* isDir, bool* isHidden, int64* fileSize, + Time* modTime, Time* creationTime, bool* isReadOnly) +{ + return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); +} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp index 37cb07c..d0dd54d 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -31,35 +34,6 @@ enum U_SMB_SUPER_MAGIC = 0x517B // linux/smb_fs.h }; -//============================================================================== -bool File::copyInternal (const File& dest) const -{ - FileInputStream in (*this); - - if (dest.deleteFile()) - { - { - FileOutputStream out (dest); - - if (out.failedToOpen()) - return false; - - if (out.writeFromInputStream (in, -1) == getSize()) - return true; - } - - dest.deleteFile(); - } - - return false; -} - -void File::findFileSystemRoots (Array& destArray) -{ - destArray.add (File ("/")); -} - -//============================================================================== bool File::isOnCDRomDrive() const { struct statfs buf; @@ -82,11 +56,7 @@ bool File::isOnHardDisk() const case U_SMB_SUPER_MAGIC: // Network Samba return false; - default: - // Assume anything else is a hard-disk (but note it could - // be a RAM disk. There isn't a good way of determining - // this for sure) - return true; + default: break; } } @@ -100,109 +70,100 @@ bool File::isOnRemovableDrive() const return false; } -bool File::isHidden() const +String File::getVersion() const { - return getFileName().startsWithChar ('.'); + return String(); // xxx not yet implemented } //============================================================================== -namespace +static File resolveXDGFolder (const char* const type, const char* const fallbackFolder) { - File juce_readlink (const String& file, const File& defaultFile) + StringArray confLines; + File ("~/.config/user-dirs.dirs").readLines (confLines); + + for (int i = 0; i < confLines.size(); ++i) { - const int size = 8192; - HeapBlock buffer; - buffer.malloc (size + 4); + const String line (confLines[i].trimStart()); - const size_t numBytes = readlink (file.toUTF8(), buffer, size); + if (line.startsWith (type)) + { + // eg. resolve XDG_MUSIC_DIR="$HOME/Music" to /home/user/Music + const File f (line.replace ("$HOME", File ("~").getFullPathName()) + .fromFirstOccurrenceOf ("=", false, false) + .trim().unquoted()); - if (numBytes > 0 && numBytes <= size) - return File (file).getSiblingFile (String::fromUTF8 (buffer, (int) numBytes)); - - return defaultFile; + if (f.isDirectory()) + return f; + } } + + return File (fallbackFolder); } -File File::getLinkedTarget() const -{ - return juce_readlink (getFullPathName().toUTF8(), *this); -} - -//============================================================================== -const char* juce_Argv0 = nullptr; // referenced from juce_Application.cpp +const char* const* juce_argv = nullptr; +int juce_argc = 0; File File::getSpecialLocation (const SpecialLocationType type) { switch (type) { - case userHomeDirectory: - { - const char* homeDir = getenv ("HOME"); - - if (homeDir == nullptr) + case userHomeDirectory: { - struct passwd* const pw = getpwuid (getuid()); - if (pw != nullptr) - homeDir = pw->pw_dir; + if (const char* homeDir = getenv ("HOME")) + return File (CharPointer_UTF8 (homeDir)); + + if (struct passwd* const pw = getpwuid (getuid())) + return File (CharPointer_UTF8 (pw->pw_dir)); + + return File(); } - return File (CharPointer_UTF8 (homeDir)); - } + case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~"); + case userMusicDirectory: return resolveXDGFolder ("XDG_MUSIC_DIR", "~"); + case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~"); + case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~"); + case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop"); + case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~"); + case commonDocumentsDirectory: + case commonApplicationDataDirectory: return File ("/var"); + case globalApplicationsDirectory: return File ("/usr"); - case userDocumentsDirectory: - case userMusicDirectory: - case userMoviesDirectory: - case userApplicationDataDirectory: - return File ("~"); - - case userDesktopDirectory: - return File ("~/Desktop"); - - case commonApplicationDataDirectory: - return File ("/var"); - - case globalApplicationsDirectory: - return File ("/usr"); - - case tempDirectory: - { - File tmp ("/var/tmp"); - - if (! tmp.isDirectory()) + case tempDirectory: { - tmp = "/tmp"; + File tmp ("/var/tmp"); if (! tmp.isDirectory()) - tmp = File::getCurrentWorkingDirectory(); + { + tmp = "/tmp"; + + if (! tmp.isDirectory()) + tmp = File::getCurrentWorkingDirectory(); + } + + return tmp; } - return tmp; + case invokedExecutableFile: + if (juce_argv != nullptr && juce_argc > 0) + return File (CharPointer_UTF8 (juce_argv[0])); + // deliberate fall-through... + + case currentExecutableFile: + case currentApplicationFile: + return juce_getExecutableFile(); + + case hostApplicationPath: + { + const File f ("/proc/self/exe"); + return f.isLink() ? f.getLinkedTarget() : juce_getExecutableFile(); + } + + default: + jassertfalse; // unknown type? + break; } - case invokedExecutableFile: - if (juce_Argv0 != nullptr) - return File (CharPointer_UTF8 (juce_Argv0)); - // deliberate fall-through... - - case currentExecutableFile: - case currentApplicationFile: - return juce_getExecutableFile(); - - case hostApplicationPath: - return juce_readlink ("/proc/self/exe", juce_getExecutableFile()); - - default: - jassertfalse; // unknown type? - break; - } - - return File::nonexistent; -} - -//============================================================================== -String File::getVersion() const -{ - return String::empty; // xxx not yet implemented + return File(); } //============================================================================== @@ -224,82 +185,15 @@ bool File::moveToTrash() const } //============================================================================== -class DirectoryIterator::NativeIterator::Pimpl +static bool isFileExecutable (const String& filename) { -public: - Pimpl (const File& directory, const String& wildCard_) - : parentDir (File::addTrailingSeparator (directory.getFullPathName())), - wildCard (wildCard_), - dir (opendir (directory.getFullPathName().toUTF8())) - { - } + juce_statStruct info; - ~Pimpl() - { - if (dir != nullptr) - closedir (dir); - } - - bool next (String& filenameFound, - bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) - { - if (dir != nullptr) - { - const char* wildcardUTF8 = nullptr; - - for (;;) - { - struct dirent* const de = readdir (dir); - - if (de == nullptr) - break; - - if (wildcardUTF8 == nullptr) - wildcardUTF8 = wildCard.toUTF8(); - - if (fnmatch (wildcardUTF8, de->d_name, FNM_CASEFOLD) == 0) - { - filenameFound = CharPointer_UTF8 (de->d_name); - - updateStatInfoForFile (parentDir + filenameFound, isDir, fileSize, modTime, creationTime, isReadOnly); - - if (isHidden != nullptr) - *isHidden = filenameFound.startsWithChar ('.'); - - return true; - } - } - } - - return false; - } - -private: - String parentDir, wildCard; - DIR* dir; - - JUCE_DECLARE_NON_COPYABLE (Pimpl); -}; - -DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) - : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) -{ + return juce_stat (filename, info) + && S_ISREG (info.st_mode) + && access (filename.toUTF8(), X_OK) == 0; } -DirectoryIterator::NativeIterator::~NativeIterator() -{ -} - -bool DirectoryIterator::NativeIterator::next (String& filenameFound, - bool* const isDir, bool* const isHidden, int64* const fileSize, - Time* const modTime, Time* const creationTime, bool* const isReadOnly) -{ - return pimpl->next (filenameFound, isDir, isHidden, fileSize, modTime, creationTime, isReadOnly); -} - - -//============================================================================== bool Process::openDocument (const String& fileName, const String& parameters) { String cmdString (fileName.replace (" ", "\\ ",false)); @@ -307,11 +201,13 @@ bool Process::openDocument (const String& fileName, const String& parameters) if (URL::isProbablyAWebsiteURL (fileName) || cmdString.startsWithIgnoreCase ("file:") - || URL::isProbablyAnEmailAddress (fileName)) + || URL::isProbablyAnEmailAddress (fileName) + || File::createFileWithoutCheckingPath (fileName).isDirectory() + || ! isFileExecutable (fileName)) { // create a command that tries to launch a bunch of likely browsers - const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", "konqueror", "opera" }; - + static const char* const browserNames[] = { "xdg-open", "/etc/alternatives/x-www-browser", "firefox", "mozilla", + "google-chrome", "chromium-browser", "opera", "konqueror" }; StringArray cmdLines; for (int i = 0; i < numElementsInArray (browserNames); ++i) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp index 2e9aa41..7543a77 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -43,7 +46,10 @@ void MACAddress::findAllAddresses (Array& result) && (ifr.ifr_flags & IFF_LOOPBACK) == 0 && ioctl (s, SIOCGIFHWADDR, &ifr) == 0) { - result.addIfNotAlreadyThere (MACAddress ((const uint8*) ifr.ifr_hwaddr.sa_data)); + MACAddress ma ((const uint8*) ifr.ifr_hwaddr.sa_data); + + if (! ma.isNull()) + result.addIfNotAlreadyThere (ma); } } @@ -52,13 +58,12 @@ void MACAddress::findAllAddresses (Array& result) } -bool Process::openEmailWithAttachments (const String& targetEmailAddress, - const String& emailSubject, - const String& bodyText, - const StringArray& filesToAttach) +bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& /* targetEmailAddress */, + const String& /* emailSubject */, + const String& /* bodyText */, + const StringArray& /* filesToAttach */) { jassertfalse; // xxx todo - return false; } @@ -70,11 +75,11 @@ public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) - : socketHandle (-1), levelsOfRedirection (0), + : statusCode (0), socketHandle (-1), levelsOfRedirection (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { - createConnection (progressCallback, progressCallbackContext); + statusCode = createConnection (progressCallback, progressCallbackContext); if (responseHeaders != nullptr && ! isError()) { @@ -95,17 +100,17 @@ public: } //============================================================================== - bool isError() const { return socketHandle < 0; } - bool isExhausted() { return finished; } - int64 getPosition() { return position; } + bool isError() const { return socketHandle < 0; } + bool isExhausted() override { return finished; } + int64 getPosition() override { return position; } - int64 getTotalLength() + int64 getTotalLength() override { //xxx to do return -1; } - int read (void* buffer, int bytesToRead) + int read (void* buffer, int bytesToRead) override { if (finished || isError()) return 0; @@ -124,11 +129,12 @@ public: const int bytesRead = jmax (0, (int) recv (socketHandle, buffer, bytesToRead, MSG_WAITALL)); if (bytesRead == 0) finished = true; + position += bytesRead; return bytesRead; } - bool setPosition (int64 wantedPos) + bool setPosition (int64 wantedPos) override { if (isError()) return false; @@ -141,7 +147,7 @@ public: { closeSocket(); position = 0; - createConnection (0, 0); + statusCode = createConnection (0, 0); } skipNextBytes (wantedPos - position); @@ -151,6 +157,8 @@ public: } //============================================================================== + int statusCode; + private: int socketHandle, levelsOfRedirection; StringArray headerLines; @@ -170,7 +178,7 @@ private: levelsOfRedirection = 0; } - void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) + int createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { closeSocket(); @@ -186,7 +194,7 @@ private: String hostName, hostPath; int hostPort; if (! decomposeURL (address, hostName, hostPath, hostPort)) - return; + return 0; String serverName, proxyName, proxyPath; int proxyPort = 0; @@ -196,7 +204,7 @@ private: if (proxyURL.startsWithIgnoreCase ("http://")) { if (! decomposeURL (proxyURL, proxyName, proxyPath, proxyPort)) - return; + return 0; serverName = proxyName; port = proxyPort; @@ -207,21 +215,23 @@ private: port = hostPort; } - struct addrinfo hints = { 0 }; + struct addrinfo hints; + zerostruct (hints); + hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; struct addrinfo* result = nullptr; if (getaddrinfo (serverName.toUTF8(), String (port).toUTF8(), &hints, &result) != 0 || result == 0) - return; + return 0; socketHandle = socket (result->ai_family, result->ai_socktype, 0); if (socketHandle == -1) { freeaddrinfo (result); - return; + return 0; } int receiveBufferSize = 16384; @@ -236,7 +246,7 @@ private: { closeSocket(); freeaddrinfo (result); - return; + return 0; } freeaddrinfo (result); @@ -245,29 +255,31 @@ private: const MemoryBlock requestHeader (createRequestHeader (hostName, hostPort, proxyName, proxyPort, hostPath, address, headers, postData, isPost)); - if (! sendHeader (socketHandle, requestHeader, timeOutTime, progressCallback, progressCallbackContext)) + if (! sendHeader (socketHandle, requestHeader, timeOutTime, + progressCallback, progressCallbackContext)) { closeSocket(); - return; + return 0; } } String responseHeader (readResponse (socketHandle, timeOutTime)); + position = 0; if (responseHeader.isNotEmpty()) { - headerLines.clear(); - headerLines.addLines (responseHeader); + headerLines = StringArray::fromLines (responseHeader); - const int statusCode = responseHeader.fromFirstOccurrenceOf (" ", false, false) - .substring (0, 3).getIntValue(); + const int status = responseHeader.fromFirstOccurrenceOf (" ", false, false) + .substring (0, 3).getIntValue(); //int contentLength = findHeaderItem (lines, "Content-Length:").getIntValue(); //bool isChunked = findHeaderItem (lines, "Transfer-Encoding:").equalsIgnoreCase ("chunked"); String location (findHeaderItem (headerLines, "Location:")); - if (statusCode >= 300 && statusCode < 400 && location.isNotEmpty()) + if (status >= 300 && status < 400 + && location.isNotEmpty() && location != address) { if (! location.startsWithIgnoreCase ("http://")) location = "http://" + location; @@ -275,61 +287,49 @@ private: if (++levelsOfRedirection <= 3) { address = location; - createConnection (progressCallback, progressCallbackContext); - return; + return createConnection (progressCallback, progressCallbackContext); } } else { levelsOfRedirection = 0; - return; + return status; } } closeSocket(); + return 0; } //============================================================================== - static String readResponse (const int socketHandle, const uint32 timeOutTime) + String readResponse (const int socketHandle, const uint32 timeOutTime) { - int bytesRead = 0, numConsecutiveLFs = 0; - MemoryBlock buffer (1024, true); + int numConsecutiveLFs = 0; + MemoryOutputStream buffer; - while (numConsecutiveLFs < 2 && bytesRead < 32768 - && Time::getMillisecondCounter() <= timeOutTime) + while (numConsecutiveLFs < 2 + && buffer.getDataSize() < 32768 + && Time::getMillisecondCounter() <= timeOutTime + && ! (finished || isError())) { - fd_set readbits; - FD_ZERO (&readbits); - FD_SET (socketHandle, &readbits); + char c = 0; + if (read (&c, 1) != 1) + return String(); - struct timeval tv; - tv.tv_sec = jmax (1, (int) (timeOutTime - Time::getMillisecondCounter()) / 1000); - tv.tv_usec = 0; + buffer.writeByte (c); - if (select (socketHandle + 1, &readbits, 0, 0, &tv) <= 0) - return String::empty; // (timeout) - - buffer.ensureSize (bytesRead + 8, true); - char* const dest = (char*) buffer.getData() + bytesRead; - - if (recv (socketHandle, dest, 1, 0) == -1) - return String::empty; - - const char lastByte = *dest; - ++bytesRead; - - if (lastByte == '\n') + if (c == '\n') ++numConsecutiveLFs; - else if (lastByte != '\r') + else if (c != '\r') numConsecutiveLFs = 0; } - const String header (CharPointer_UTF8 ((const char*) buffer.getData())); + const String header (buffer.toString().trimEnd()); if (header.startsWithIgnoreCase ("HTTP/")) - return header.trimEnd(); + return header; - return String::empty; + return String(); } static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value) @@ -341,9 +341,6 @@ private: static void writeHost (MemoryOutputStream& dest, const bool isPost, const String& path, const String& host, const int port) { dest << (isPost ? "POST " : "GET ") << path << " HTTP/1.0\r\nHost: " << host; - - if (port > 0) - dest << ':' << port; } static MemoryBlock createRequestHeader (const String& hostName, const int hostPort, @@ -362,7 +359,7 @@ private: writeValueIfNotPresent (header, userHeaders, "User-Agent:", "JUCE/" JUCE_STRINGIFY(JUCE_MAJOR_VERSION) "." JUCE_STRINGIFY(JUCE_MINOR_VERSION) "." JUCE_STRINGIFY(JUCE_BUILDNUMBER)); - writeValueIfNotPresent (header, userHeaders, "Connection:", "Close"); + writeValueIfNotPresent (header, userHeaders, "Connection:", "close"); if (isPost) writeValueIfNotPresent (header, userHeaders, "Content-Length:", String ((int) postData.getSize())); @@ -440,19 +437,8 @@ private: if (lines[i].startsWithIgnoreCase (itemName)) return lines[i].substring (itemName.length()).trim(); - return String::empty; + return String(); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; - -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->isError() ? nullptr : wi.release(); -} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp index c70b127..939edb4 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -58,16 +61,26 @@ namespace LinuxStatsHelpers File ("/proc/cpuinfo").readLines (lines); for (int i = lines.size(); --i >= 0;) // (NB - it's important that this runs in reverse order) - if (lines[i].startsWithIgnoreCase (key)) + if (lines[i].upToFirstOccurrenceOf (":", false, false).trim().equalsIgnoreCase (key)) return lines[i].fromFirstOccurrenceOf (":", false, false).trim(); - return String::empty; + return String(); } } +String SystemStats::getDeviceDescription() +{ + return LinuxStatsHelpers::getCpuInfo ("Hardware"); +} + String SystemStats::getCpuVendor() { - return LinuxStatsHelpers::getCpuInfo ("vendor_id"); + String v (LinuxStatsHelpers::getCpuInfo ("vendor_id")); + + if (v.isEmpty()) + v = LinuxStatsHelpers::getCpuInfo ("model name"); + + return v; } int SystemStats::getCpuSpeedInMegaherz() @@ -80,7 +93,7 @@ int SystemStats::getMemorySizeInMegabytes() struct sysinfo sysi; if (sysinfo (&sysi) == 0) - return (sysi.totalram * sysi.mem_unit / (1024 * 1024)); + return sysi.totalram * sysi.mem_unit / (1024 * 1024); return 0; } @@ -93,16 +106,13 @@ int SystemStats::getPageSize() //============================================================================== String SystemStats::getLogonName() { - const char* user = getenv ("USER"); + if (const char* user = getenv ("USER")) + return CharPointer_UTF8 (user); - if (user == nullptr) - { - struct passwd* const pw = getpwuid (getuid()); - if (pw != nullptr) - user = pw->pw_name; - } + if (struct passwd* const pw = getpwuid (getuid())) + return CharPointer_UTF8 (pw->pw_name); - return CharPointer_UTF8 (user); + return String(); } String SystemStats::getFullUserName() @@ -116,16 +126,29 @@ String SystemStats::getComputerName() if (gethostname (name, sizeof (name) - 1) == 0) return name; - return String::empty; + return String(); } +static String getLocaleValue (nl_item key) +{ + const char* oldLocale = ::setlocale (LC_ALL, ""); + String result (String::fromUTF8 (nl_langinfo (key))); + ::setlocale (LC_ALL, oldLocale); + return result; +} + +String SystemStats::getUserLanguage() { return getLocaleValue (_NL_IDENTIFICATION_LANGUAGE); } +String SystemStats::getUserRegion() { return getLocaleValue (_NL_IDENTIFICATION_TERRITORY); } +String SystemStats::getDisplayLanguage() { return getUserLanguage() + "-" + getUserRegion(); } + //============================================================================== -SystemStats::CPUFlags::CPUFlags() +void CPUInformation::initialise() noexcept { const String flags (LinuxStatsHelpers::getCpuInfo ("flags")); hasMMX = flags.contains ("mmx"); hasSSE = flags.contains ("sse"); hasSSE2 = flags.contains ("sse2"); + hasSSE3 = flags.contains ("sse3"); has3DNow = flags.contains ("3dnow"); numCpus = LinuxStatsHelpers::getCpuInfo ("processor").getIntValue() + 1; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp index 6e999b2..a75094a 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Threads.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -29,7 +32,7 @@ */ //============================================================================== -void Process::setPriority (const ProcessPriority prior) +JUCE_API void JUCE_CALLTYPE Process::setPriority (const ProcessPriority prior) { const int policy = (prior <= NormalPriority) ? SCHED_OTHER : SCHED_RR; const int minp = sched_get_priority_min (policy); @@ -49,13 +52,11 @@ void Process::setPriority (const ProcessPriority prior) pthread_setschedparam (pthread_self(), policy, ¶m); } -void Process::terminate() -{ - exit (0); -} - JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() { + #if JUCE_BSD + return false; + #else static char testResult = 0; if (testResult == 0) @@ -70,6 +71,7 @@ JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger() } return testResult < 0; + #endif } JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() @@ -77,22 +79,11 @@ JUCE_API bool JUCE_CALLTYPE Process::isRunningUnderDebugger() return juce_isRunningUnderDebugger(); } -void Process::raisePrivilege() +static void swapUserAndEffectiveUser() { - // If running suid root, change effective user to root - if (geteuid() != 0 && getuid() == 0) - { - setreuid (geteuid(), getuid()); - setregid (getegid(), getgid()); - } + (void) setreuid (geteuid(), getuid()); + (void) setregid (getegid(), getgid()); } -void Process::lowerPrivilege() -{ - // If runing suid root, change effective user back to real user - if (geteuid() == 0 && getuid() != 0) - { - setreuid (geteuid(), getuid()); - setregid (getegid(), getgid()); - } -} +JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { if (geteuid() != 0 && getuid() == 0) swapUserAndEffectiveUser(); } +JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() { if (geteuid() == 0 && getuid() != 0) swapUserAndEffectiveUser(); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm index 2fbb603..0a00e92 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -32,18 +35,20 @@ bool File::copyInternal (const File& dest) const { JUCE_AUTORELEASEPOOL - NSFileManager* fm = [NSFileManager defaultManager]; + { + NSFileManager* fm = [NSFileManager defaultManager]; - return [fm fileExistsAtPath: juceStringToNS (fullPath)] -#if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - && [fm copyItemAtPath: juceStringToNS (fullPath) - toPath: juceStringToNS (dest.getFullPathName()) - error: nil]; -#else - && [fm copyPath: juceStringToNS (fullPath) - toPath: juceStringToNS (dest.getFullPathName()) - handler: nil]; -#endif + return [fm fileExistsAtPath: juceStringToNS (fullPath)] + #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + && [fm copyItemAtPath: juceStringToNS (fullPath) + toPath: juceStringToNS (dest.getFullPathName()) + error: nil]; + #else + && [fm copyPath: juceStringToNS (fullPath) + toPath: juceStringToNS (dest.getFullPathName()) + handler: nil]; + #endif + } } void File::findFileSystemRoots (Array& destArray) @@ -73,33 +78,35 @@ namespace FileHelpers static bool isHiddenFile (const String& path) { - #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 + #if defined (MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 JUCE_AUTORELEASEPOOL - NSNumber* hidden = nil; - NSError* err = nil; + { + NSNumber* hidden = nil; + NSError* err = nil; - return [[NSURL fileURLWithPath: juceStringToNS (path)] - getResourceValue: &hidden forKey: NSURLIsHiddenKey error: &err] - && [hidden boolValue]; - #elif JUCE_IOS + return [[NSURL fileURLWithPath: juceStringToNS (path)] + getResourceValue: &hidden forKey: NSURLIsHiddenKey error: &err] + && [hidden boolValue]; + } + #elif JUCE_IOS return File (path).getFileName().startsWithChar ('.'); - #else + #else FSRef ref; LSItemInfoRecord info; - return FSPathMakeRefWithOptions ((const UInt8*) path.toUTF8().getAddress(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr + return FSPathMakeRefWithOptions ((const UInt8*) path.toRawUTF8(), kFSPathMakeRefDoNotFollowLeafSymlink, &ref, 0) == noErr && LSCopyItemInfoForRef (&ref, kLSRequestBasicFlagsOnly, &info) == noErr && (info.flags & kLSItemInfoIsInvisible) != 0; - #endif + #endif } - #if JUCE_IOS - String getIOSSystemLocation (NSSearchPathDirectory type) + #if JUCE_IOS + static String getIOSSystemLocation (NSSearchPathDirectory type) { return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) objectAtIndex: 0]); } - #endif + #endif static bool launchExecutable (const String& pathAndArguments) { @@ -125,14 +132,14 @@ namespace FileHelpers bool File::isOnCDRomDrive() const { - const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", 0 }; + static const char* const cdTypes[] = { "cd9660", "cdfs", "cddafs", "udf", nullptr }; return FileHelpers::isFileOnDriveType (*this, cdTypes); } bool File::isOnHardDisk() const { - const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", 0 }; + static const char* const nonHDTypes[] = { "nfs", "smbfs", "ramfs", nullptr }; return ! (isOnCDRomDrive() || FileHelpers::isFileOnDriveType (*this, nonHDTypes)); } @@ -143,17 +150,19 @@ bool File::isOnRemovableDrive() const return false; // xxx is this possible? #else JUCE_AUTORELEASEPOOL - BOOL removable = false; + { + BOOL removable = false; - [[NSWorkspace sharedWorkspace] - getFileSystemInfoForPath: juceStringToNS (getFullPathName()) - isRemovable: &removable - isWritable: nil - isUnmountable: nil - description: nil - type: nil]; + [[NSWorkspace sharedWorkspace] + getFileSystemInfoForPath: juceStringToNS (getFullPathName()) + isRemovable: &removable + isWritable: nil + isUnmountable: nil + description: nil + type: nil]; - return removable; + return removable; + } #endif } @@ -163,126 +172,128 @@ bool File::isHidden() const } //============================================================================== -const char* juce_Argv0 = nullptr; // referenced from juce_Application.cpp +const char* const* juce_argv = nullptr; +int juce_argc = 0; File File::getSpecialLocation (const SpecialLocationType type) { JUCE_AUTORELEASEPOOL - String resultPath; - - switch (type) { - case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; + String resultPath; - #if JUCE_IOS - case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break; - case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break; - - case tempDirectory: + switch (type) { - File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory)); - tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); - tmp.createDirectory(); - return tmp.getFullPathName(); - } - - #else - case userDocumentsDirectory: resultPath = "~/Documents"; break; - case userDesktopDirectory: resultPath = "~/Desktop"; break; - - case tempDirectory: - { - File tmp ("~/Library/Caches/" + juce_getExecutableFile().getFileNameWithoutExtension()); - tmp.createDirectory(); - return tmp.getFullPathName(); - } - #endif - case userMusicDirectory: resultPath = "~/Music"; break; - case userMoviesDirectory: resultPath = "~/Movies"; break; - case userApplicationDataDirectory: resultPath = "~/Library"; break; - case commonApplicationDataDirectory: resultPath = "/Library"; break; - case globalApplicationsDirectory: resultPath = "/Applications"; break; - - case invokedExecutableFile: - if (juce_Argv0 != 0) - return File (CharPointer_UTF8 (juce_Argv0)); - // deliberate fall-through... - - case currentExecutableFile: - return juce_getExecutableFile(); - - case currentApplicationFile: - { - const File exe (juce_getExecutableFile()); - const File parent (exe.getParentDirectory()); + case userHomeDirectory: resultPath = nsStringToJuce (NSHomeDirectory()); break; #if JUCE_IOS - return parent; + case userDocumentsDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDocumentDirectory); break; + case userDesktopDirectory: resultPath = FileHelpers::getIOSSystemLocation (NSDesktopDirectory); break; + + case tempDirectory: + { + File tmp (FileHelpers::getIOSSystemLocation (NSCachesDirectory)); + tmp = tmp.getChildFile (juce_getExecutableFile().getFileNameWithoutExtension()); + tmp.createDirectory(); + return tmp.getFullPathName(); + } + #else - return parent.getFullPathName().endsWithIgnoreCase ("Contents/MacOS") - ? parent.getParentDirectory().getParentDirectory() - : exe; + case userDocumentsDirectory: resultPath = "~/Documents"; break; + case userDesktopDirectory: resultPath = "~/Desktop"; break; + + case tempDirectory: + { + File tmp ("~/Library/Caches/" + juce_getExecutableFile().getFileNameWithoutExtension()); + tmp.createDirectory(); + return File (tmp.getFullPathName()); + } #endif + case userMusicDirectory: resultPath = "~/Music"; break; + case userMoviesDirectory: resultPath = "~/Movies"; break; + case userPicturesDirectory: resultPath = "~/Pictures"; break; + case userApplicationDataDirectory: resultPath = "~/Library"; break; + case commonApplicationDataDirectory: resultPath = "/Library"; break; + case commonDocumentsDirectory: resultPath = "/Users/Shared"; break; + case globalApplicationsDirectory: resultPath = "/Applications"; break; + + case invokedExecutableFile: + if (juce_argv != nullptr && juce_argc > 0) + return File (CharPointer_UTF8 (juce_argv[0])); + // deliberate fall-through... + + case currentExecutableFile: + return juce_getExecutableFile(); + + case currentApplicationFile: + { + const File exe (juce_getExecutableFile()); + const File parent (exe.getParentDirectory()); + + #if JUCE_IOS + return parent; + #else + return parent.getFullPathName().endsWithIgnoreCase ("Contents/MacOS") + ? parent.getParentDirectory().getParentDirectory() + : exe; + #endif + } + + case hostApplicationPath: + { + unsigned int size = 8192; + HeapBlock buffer; + buffer.calloc (size + 8); + + _NSGetExecutablePath (buffer.getData(), &size); + return File (String::fromUTF8 (buffer, (int) size)); + } + + default: + jassertfalse; // unknown type? + break; } - case hostApplicationPath: - { - unsigned int size = 8192; - HeapBlock buffer; - buffer.calloc (size + 8); - - _NSGetExecutablePath (buffer.getData(), &size); - return String::fromUTF8 (buffer, size); - } - - default: - jassertfalse; // unknown type? - break; + if (resultPath.isNotEmpty()) + return File (resultPath.convertToPrecomposedUnicode()); } - if (resultPath.isNotEmpty()) - return File (resultPath.convertToPrecomposedUnicode()); - - return File::nonexistent; + return File(); } //============================================================================== String File::getVersion() const { JUCE_AUTORELEASEPOOL - String result; - - NSBundle* bundle = [NSBundle bundleWithPath: juceStringToNS (getFullPathName())]; - - if (bundle != nil) { - NSDictionary* info = [bundle infoDictionary]; - - if (info != nil) - { - NSString* name = [info valueForKey: nsStringLiteral ("CFBundleShortVersionString")]; - - if (name != nil) - result = nsStringToJuce (name); - } + if (NSBundle* bundle = [NSBundle bundleWithPath: juceStringToNS (getFullPathName())]) + if (NSDictionary* info = [bundle infoDictionary]) + if (NSString* name = [info valueForKey: nsStringLiteral ("CFBundleShortVersionString")]) + return nsStringToJuce (name); } - return result; + return String(); } //============================================================================== +static NSString* getFileLink (const String& path) +{ + #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + return [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (path) error: nil]; + #else + // (the cast here avoids a deprecation warning) + return [((id) [NSFileManager defaultManager]) pathContentOfSymbolicLinkAtPath: juceStringToNS (path)]; + #endif +} + +bool File::isLink() const +{ + return getFileLink (fullPath) != nil; +} + File File::getLinkedTarget() const { - #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_5) - NSString* dest = [[NSFileManager defaultManager] destinationOfSymbolicLinkAtPath: juceStringToNS (getFullPathName()) error: nil]; - - #else - // (the cast here avoids a deprecation warning) - NSString* dest = [((id) [NSFileManager defaultManager]) pathContentOfSymbolicLinkAtPath: juceStringToNS (getFullPathName())]; - #endif - - if (dest != nil) - return File (nsStringToJuce (dest)); + if (NSString* dest = getFileLink (fullPath)) + return getSiblingFile (nsStringToJuce (dest)); return *this; } @@ -293,20 +304,21 @@ bool File::moveToTrash() const if (! exists()) return true; - #if JUCE_IOS - return deleteFile(); //xxx is there a trashcan on the iPhone? - #else + #if JUCE_IOS + return deleteFile(); //xxx is there a trashcan on the iOS? + #else JUCE_AUTORELEASEPOOL + { + NSString* p = juceStringToNS (getFullPathName()); - NSString* p = juceStringToNS (getFullPathName()); - - return [[NSWorkspace sharedWorkspace] - performFileOperation: NSWorkspaceRecycleOperation - source: [p stringByDeletingLastPathComponent] - destination: nsEmptyString() - files: [NSArray arrayWithObject: [p lastPathComponent]] - tag: nil ]; - #endif + return [[NSWorkspace sharedWorkspace] + performFileOperation: NSWorkspaceRecycleOperation + source: [p stringByDeletingLastPathComponent] + destination: nsEmptyString() + files: [NSArray arrayWithObject: [p lastPathComponent]] + tag: nil ]; + } + #endif } //============================================================================== @@ -319,8 +331,9 @@ public: enumerator (nil) { JUCE_AUTORELEASEPOOL - - enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory.getFullPathName())] retain]; + { + enumerator = [[[NSFileManager defaultManager] enumeratorAtPath: juceStringToNS (directory.getFullPathName())] retain]; + } } ~Pimpl() @@ -333,30 +346,32 @@ public: Time* const modTime, Time* const creationTime, bool* const isReadOnly) { JUCE_AUTORELEASEPOOL - const char* wildcardUTF8 = nullptr; - - for (;;) { - NSString* file; - if (enumerator == nil || (file = [enumerator nextObject]) == nil) - return false; + const char* wildcardUTF8 = nullptr; - [enumerator skipDescendents]; - filenameFound = nsStringToJuce (file); + for (;;) + { + NSString* file; + if (enumerator == nil || (file = [enumerator nextObject]) == nil) + return false; - if (wildcardUTF8 == nullptr) - wildcardUTF8 = wildCard.toUTF8(); + [enumerator skipDescendents]; + filenameFound = nsStringToJuce (file).convertToPrecomposedUnicode(); - if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0) - continue; + if (wildcardUTF8 == nullptr) + wildcardUTF8 = wildCard.toUTF8(); - const String path (parentDir + filenameFound); - updateStatInfoForFile (path, isDir, fileSize, modTime, creationTime, isReadOnly); + if (fnmatch (wildcardUTF8, filenameFound.toUTF8(), FNM_CASEFOLD) != 0) + continue; - if (isHidden != nullptr) - *isHidden = FileHelpers::isHiddenFile (path); + const String fullPath (parentDir + filenameFound); + updateStatInfoForFile (fullPath, isDir, fileSize, modTime, creationTime, isReadOnly); - return true; + if (isHidden != nullptr) + *isHidden = FileHelpers::isHiddenFile (fullPath); + + return true; + } } } @@ -364,11 +379,11 @@ private: String parentDir, wildCard; NSDirectoryEnumerator* enumerator; - JUCE_DECLARE_NON_COPYABLE (Pimpl); + JUCE_DECLARE_NON_COPYABLE (Pimpl) }; -DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) - : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildCard)) +DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildcard) + : pimpl (new DirectoryIterator::NativeIterator::Pimpl (directory, wildcard)) { } @@ -385,69 +400,75 @@ bool DirectoryIterator::NativeIterator::next (String& filenameFound, //============================================================================== -bool Process::openDocument (const String& fileName, const String& parameters) +bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { - #if JUCE_IOS - return [[UIApplication sharedApplication] openURL: [NSURL URLWithString: juceStringToNS (fileName)]]; - #else JUCE_AUTORELEASEPOOL - - if (parameters.isEmpty()) { - return [[NSWorkspace sharedWorkspace] openFile: juceStringToNS (fileName)] - || [[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString: juceStringToNS (fileName)]]; + NSURL* filenameAsURL = [NSURL URLWithString: juceStringToNS (fileName)]; + + #if JUCE_IOS + (void) parameters; + return [[UIApplication sharedApplication] openURL: filenameAsURL]; + #else + NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; + + if (parameters.isEmpty()) + return [workspace openFile: juceStringToNS (fileName)] + || [workspace openURL: filenameAsURL]; + + const File file (fileName); + + if (file.isBundle()) + { + StringArray params; + params.addTokens (parameters, true); + + NSMutableArray* paramArray = [[[NSMutableArray alloc] init] autorelease]; + for (int i = 0; i < params.size(); ++i) + [paramArray addObject: juceStringToNS (params[i])]; + + NSMutableDictionary* dict = [[[NSMutableDictionary alloc] init] autorelease]; + [dict setObject: paramArray + forKey: nsStringLiteral ("NSWorkspaceLaunchConfigurationArguments")]; + + return [workspace launchApplicationAtURL: filenameAsURL + options: NSWorkspaceLaunchDefault | NSWorkspaceLaunchNewInstance + configuration: dict + error: nil]; + } + + if (file.exists()) + return FileHelpers::launchExecutable ("\"" + fileName + "\" " + parameters); + + return false; + #endif } - - bool ok = false; - const File file (fileName); - - if (file.isBundle()) - { - NSMutableArray* urls = [NSMutableArray array]; - - StringArray docs; - docs.addTokens (parameters, true); - for (int i = 0; i < docs.size(); ++i) - [urls addObject: juceStringToNS (docs[i])]; - - ok = [[NSWorkspace sharedWorkspace] openURLs: urls - withAppBundleIdentifier: [[NSBundle bundleWithPath: juceStringToNS (fileName)] bundleIdentifier] - options: 0 - additionalEventParamDescriptor: nil - launchIdentifiers: nil]; - } - else if (file.exists()) - { - ok = FileHelpers::launchExecutable ("\"" + fileName + "\" " + parameters); - } - - return ok; - #endif } void File::revealToUser() const { - #if ! JUCE_IOS + #if ! JUCE_IOS if (exists()) [[NSWorkspace sharedWorkspace] selectFile: juceStringToNS (getFullPathName()) inFileViewerRootedAtPath: nsEmptyString()]; else if (getParentDirectory().exists()) getParentDirectory().revealToUser(); - #endif + #endif } //============================================================================== OSType File::getMacOSType() const { JUCE_AUTORELEASEPOOL + { + #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + NSDictionary* fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath: juceStringToNS (getFullPathName()) error: nil]; + #else + // (the cast here avoids a deprecation warning) + NSDictionary* fileDict = [((id) [NSFileManager defaultManager]) fileAttributesAtPath: juceStringToNS (getFullPathName()) traverseLink: NO]; + #endif - #if JUCE_IOS || (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_ALLOWED >= MAC_OS_X_VERSION_10_5) - NSDictionary* fileDict = [[NSFileManager defaultManager] attributesOfItemAtPath: juceStringToNS (getFullPathName()) error: nil]; - #else - // (the cast here avoids a deprecation warning) - NSDictionary* fileDict = [((id) [NSFileManager defaultManager]) fileAttributesAtPath: juceStringToNS (getFullPathName()) traverseLink: NO]; - #endif - - return [fileDict fileHFSTypeCode]; + return [fileDict fileHFSTypeCode]; + } } bool File::isBundle() const @@ -456,7 +477,9 @@ bool File::isBundle() const return false; // xxx can't find a sensible way to do this without trying to open the bundle.. #else JUCE_AUTORELEASEPOOL - return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (getFullPathName())]; + { + return [[NSWorkspace sharedWorkspace] isFilePackageAtPath: juceStringToNS (getFullPathName())]; + } #endif } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm index fb8a1f0..130759e 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Network.mm @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -36,12 +39,17 @@ void MACAddress::findAllAddresses (Array& result) { const sockaddr_dl* const sadd = (const sockaddr_dl*) cursor->ifa_addr; - #ifndef IFT_ETHER - #define IFT_ETHER 6 - #endif + #ifndef IFT_ETHER + enum { IFT_ETHER = 6 }; + #endif if (sadd->sdl_type == IFT_ETHER) - result.addIfNotAlreadyThere (MACAddress (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen)); + { + MACAddress ma (MACAddress (((const uint8*) sadd->sdl_data) + sadd->sdl_nlen)); + + if (! ma.isNull()) + result.addIfNotAlreadyThere (ma); + } } } @@ -50,328 +58,313 @@ void MACAddress::findAllAddresses (Array& result) } //============================================================================== -bool Process::openEmailWithAttachments (const String& targetEmailAddress, - const String& emailSubject, - const String& bodyText, - const StringArray& filesToAttach) +bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) { #if JUCE_IOS + (void) targetEmailAddress; + (void) emailSubject; + (void) bodyText; + (void) filesToAttach; + //xxx probably need to use MFMailComposeViewController jassertfalse; return false; #else JUCE_AUTORELEASEPOOL - - String script; - script << "tell application \"Mail\"\r\n" - "set newMessage to make new outgoing message with properties {subject:\"" - << emailSubject.replace ("\"", "\\\"") - << "\", content:\"" - << bodyText.replace ("\"", "\\\"") - << "\" & return & return}\r\n" - "tell newMessage\r\n" - "set visible to true\r\n" - "set sender to \"sdfsdfsdfewf\"\r\n" - "make new to recipient at end of to recipients with properties {address:\"" - << targetEmailAddress - << "\"}\r\n"; - - for (int i = 0; i < filesToAttach.size(); ++i) { - script << "tell content\r\n" - "make new attachment with properties {file name:\"" - << filesToAttach[i].replace ("\"", "\\\"") - << "\"} at after the last paragraph\r\n" + String script; + script << "tell application \"Mail\"\r\n" + "set newMessage to make new outgoing message with properties {subject:\"" + << emailSubject.replace ("\"", "\\\"") + << "\", content:\"" + << bodyText.replace ("\"", "\\\"") + << "\" & return & return}\r\n" + "tell newMessage\r\n" + "set visible to true\r\n" + "set sender to \"sdfsdfsdfewf\"\r\n" + "make new to recipient at end of to recipients with properties {address:\"" + << targetEmailAddress + << "\"}\r\n"; + + for (int i = 0; i < filesToAttach.size(); ++i) + { + script << "tell content\r\n" + "make new attachment with properties {file name:\"" + << filesToAttach[i].replace ("\"", "\\\"") + << "\"} at after the last paragraph\r\n" + "end tell\r\n"; + } + + script << "end tell\r\n" "end tell\r\n"; + + NSAppleScript* s = [[NSAppleScript alloc] initWithSource: juceStringToNS (script)]; + NSDictionary* error = nil; + const bool ok = [s executeAndReturnError: &error] != nil; + [s release]; + + return ok; } - - script << "end tell\r\n" - "end tell\r\n"; - - NSAppleScript* s = [[NSAppleScript alloc] initWithSource: juceStringToNS (script)]; - NSDictionary* error = nil; - const bool ok = [s executeAndReturnError: &error] != nil; - [s release]; - - return ok; #endif } //============================================================================== -} // (juce namespace) - -using namespace juce; - -//============================================================================== -#define JuceURLConnection MakeObjCClassName(JuceURLConnection) - -@interface JuceURLConnection : NSObject -{ -@public - NSURLRequest* request; - NSURLConnection* connection; - NSMutableData* data; - Thread* runLoopThread; - bool initialised, hasFailed, hasFinished; - int position; - int64 contentLength; - NSDictionary* headers; - NSLock* dataLock; -} - -- (JuceURLConnection*) initWithRequest: (NSURLRequest*) req withCallback: (URL::OpenStreamProgressCallback*) callback withContext: (void*) context; -- (void) dealloc; -- (void) connection: (NSURLConnection*) connection didReceiveResponse: (NSURLResponse*) response; -- (void) connection: (NSURLConnection*) connection didFailWithError: (NSError*) error; -- (void) connection: (NSURLConnection*) connection didReceiveData: (NSData*) data; -- (void) connectionDidFinishLoading: (NSURLConnection*) connection; - -- (BOOL) isOpen; -- (int) read: (char*) dest numBytes: (int) num; -- (int) readPosition; -- (void) stop; -- (void) createConnection; - -@end - -class JuceURLConnectionMessageThread : public Thread +class URLConnectionState : public Thread { public: - JuceURLConnectionMessageThread (JuceURLConnection* owner_) + URLConnectionState (NSURLRequest* req) : Thread ("http connection"), - owner (owner_) + contentLength (-1), + delegate (nil), + request ([req retain]), + connection (nil), + data ([[NSMutableData data] retain]), + headers (nil), + statusCode (0), + initialised (false), + hasFailed (false), + hasFinished (false) { + static DelegateClass cls; + delegate = [cls.createInstance() init]; + DelegateClass::setState (delegate, this); } - ~JuceURLConnectionMessageThread() + ~URLConnectionState() { - stopThread (10000); + stop(); + [connection release]; + [data release]; + [request release]; + [headers release]; + [delegate release]; } - void run() + bool start (URL::OpenStreamProgressCallback* callback, void* context) { - [owner createConnection]; + startThread(); - while (! threadShouldExit()) + while (isThreadRunning() && ! initialised) { - JUCE_AUTORELEASEPOOL - [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; - } - } - -private: - JuceURLConnection* owner; -}; - - -@implementation JuceURLConnection - -- (JuceURLConnection*) initWithRequest: (NSURLRequest*) req - withCallback: (URL::OpenStreamProgressCallback*) callback - withContext: (void*) context; -{ - [super init]; - request = req; - [request retain]; - data = [[NSMutableData data] retain]; - dataLock = [[NSLock alloc] init]; - connection = nil; - initialised = false; - hasFailed = false; - hasFinished = false; - contentLength = -1; - headers = nil; - - runLoopThread = new JuceURLConnectionMessageThread (self); - runLoopThread->startThread(); - - while (runLoopThread->isThreadRunning() && ! initialised) - { - if (callback != nullptr) - callback (context, -1, (int) [[request HTTPBody] length]); - - Thread::sleep (1); - } - - return self; -} - -- (void) dealloc -{ - [self stop]; - - deleteAndZero (runLoopThread); - [connection release]; - [data release]; - [dataLock release]; - [request release]; - [headers release]; - [super dealloc]; -} - -- (void) createConnection -{ - NSUInteger oldRetainCount = [self retainCount]; - connection = [[NSURLConnection alloc] initWithRequest: request - delegate: self]; - - if (oldRetainCount == [self retainCount]) - [self retain]; // newer SDK should already retain this, but there were problems in older versions.. - - if (connection == nil) - runLoopThread->signalThreadShouldExit(); -} - -- (void) connection: (NSURLConnection*) conn didReceiveResponse: (NSURLResponse*) response -{ - (void) conn; - [dataLock lock]; - [data setLength: 0]; - [dataLock unlock]; - initialised = true; - contentLength = [response expectedContentLength]; - - [headers release]; - headers = nil; - - if ([response isKindOfClass: [NSHTTPURLResponse class]]) - headers = [[((NSHTTPURLResponse*) response) allHeaderFields] retain]; -} - -- (void) connection: (NSURLConnection*) conn didFailWithError: (NSError*) error -{ - (void) conn; - DBG (nsStringToJuce ([error description])); - hasFailed = true; - initialised = true; - - if (runLoopThread != nullptr) - runLoopThread->signalThreadShouldExit(); -} - -- (void) connection: (NSURLConnection*) conn didReceiveData: (NSData*) newData -{ - (void) conn; - [dataLock lock]; - [data appendData: newData]; - [dataLock unlock]; - initialised = true; -} - -- (void) connectionDidFinishLoading: (NSURLConnection*) conn -{ - (void) conn; - hasFinished = true; - initialised = true; - - if (runLoopThread != nullptr) - runLoopThread->signalThreadShouldExit(); -} - -- (BOOL) isOpen -{ - return connection != nil && ! hasFailed; -} - -- (int) readPosition -{ - return position; -} - -- (int) read: (char*) dest numBytes: (int) numNeeded -{ - int numDone = 0; - - while (numNeeded > 0) - { - int available = jmin (numNeeded, (int) [data length]); - - if (available > 0) - { - [dataLock lock]; - [data getBytes: dest length: available]; - [data replaceBytesInRange: NSMakeRange (0, available) withBytes: nil length: 0]; - [dataLock unlock]; - - numDone += available; - numNeeded -= available; - dest += available; - } - else - { - if (hasFailed || hasFinished) - break; + if (callback != nullptr) + callback (context, -1, (int) [[request HTTPBody] length]); Thread::sleep (1); } + + return connection != nil && ! hasFailed; } - position += numDone; - return numDone; -} + void stop() + { + [connection cancel]; + stopThread (10000); + } -- (void) stop -{ - [connection cancel]; + int read (char* dest, int numBytes) + { + int numDone = 0; - if (runLoopThread != nullptr) - runLoopThread->stopThread (10000); -} + while (numBytes > 0) + { + const int available = jmin (numBytes, (int) [data length]); -@end + if (available > 0) + { + const ScopedLock sl (dataLock); + [data getBytes: dest length: (NSUInteger) available]; + [data replaceBytesInRange: NSMakeRange (0, (NSUInteger) available) withBytes: nil length: 0]; + + numDone += available; + numBytes -= available; + dest += available; + } + else + { + if (hasFailed || hasFinished) + break; + + Thread::sleep (1); + } + } + + return numDone; + } + + void didReceiveResponse (NSURLResponse* response) + { + { + const ScopedLock sl (dataLock); + [data setLength: 0]; + } + + initialised = true; + contentLength = [response expectedContentLength]; + + [headers release]; + headers = nil; + + if ([response isKindOfClass: [NSHTTPURLResponse class]]) + { + NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response; + headers = [[httpResponse allHeaderFields] retain]; + statusCode = (int) [httpResponse statusCode]; + } + } + + void didFailWithError (NSError* error) + { + DBG (nsStringToJuce ([error description])); (void) error; + hasFailed = true; + initialised = true; + signalThreadShouldExit(); + } + + void didReceiveData (NSData* newData) + { + const ScopedLock sl (dataLock); + [data appendData: newData]; + initialised = true; + } + + void didSendBodyData (NSInteger /*totalBytesWritten*/, NSInteger /*totalBytesExpected*/) + { + } + + void finishedLoading() + { + hasFinished = true; + initialised = true; + signalThreadShouldExit(); + } + + void run() override + { + connection = [[NSURLConnection alloc] initWithRequest: request + delegate: delegate]; + while (! threadShouldExit()) + { + JUCE_AUTORELEASEPOOL + { + [[NSRunLoop currentRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; + } + } + } + + int64 contentLength; + CriticalSection dataLock; + NSObject* delegate; + NSURLRequest* request; + NSURLConnection* connection; + NSMutableData* data; + NSDictionary* headers; + int statusCode; + bool initialised, hasFailed, hasFinished; + +private: + //============================================================================== + struct DelegateClass : public ObjCClass + { + DelegateClass() : ObjCClass ("JUCEAppDelegate_") + { + addIvar ("state"); + + addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); + addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); + addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); + addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:), + connectionDidSendBodyData, "v@:@iii"); + addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); + addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@"); + + registerClass(); + } + + static void setState (id self, URLConnectionState* state) { object_setInstanceVariable (self, "state", state); } + static URLConnectionState* getState (id self) { return getIvar (self, "state"); } + + private: + static void didReceiveResponse (id self, SEL, NSURLConnection*, NSURLResponse* response) + { + getState (self)->didReceiveResponse (response); + } + + static void didFailWithError (id self, SEL, NSURLConnection*, NSError* error) + { + getState (self)->didFailWithError (error); + } + + static void didReceiveData (id self, SEL, NSURLConnection*, NSData* newData) + { + getState (self)->didReceiveData (newData); + } + + static NSURLRequest* willSendRequest (id, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse*) + { + return request; + } + + static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) + { + getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); + } + + static void connectionDidFinishLoading (id self, SEL, NSURLConnection*) + { + getState (self)->finishedLoading(); + } + }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (URLConnectionState) +}; -namespace juce -{ //============================================================================== class WebInputStream : public InputStream { public: - //============================================================================== WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) - : connection (nil), - address (address_), headers (headers_), postData (postData_), position (0), + : statusCode (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { JUCE_AUTORELEASEPOOL - connection = createConnection (progressCallback, progressCallbackContext); - - if (responseHeaders != nullptr && connection != nil && connection->headers != nil) { - NSEnumerator* enumerator = [connection->headers keyEnumerator]; - NSString* key; + createConnection (progressCallback, progressCallbackContext); - while ((key = [enumerator nextObject]) != nil) - responseHeaders->set (nsStringToJuce (key), - nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) + { + statusCode = connection->statusCode; + + NSEnumerator* enumerator = [connection->headers keyEnumerator]; + + while (NSString* key = [enumerator nextObject]) + responseHeaders->set (nsStringToJuce (key), + nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); + } } } - ~WebInputStream() - { - close(); - } - //============================================================================== - bool isError() const { return connection == nil; } - int64 getTotalLength() { return connection == nil ? -1 : connection->contentLength; } - bool isExhausted() { return finished; } - int64 getPosition() { return position; } + bool isError() const { return connection == nullptr; } + int64 getTotalLength() override { return connection == nullptr ? -1 : connection->contentLength; } + bool isExhausted() override { return finished; } + int64 getPosition() override { return position; } - int read (void* buffer, int bytesToRead) + int read (void* buffer, int bytesToRead) override { jassert (buffer != nullptr && bytesToRead >= 0); if (finished || isError()) - { return 0; - } - else + + JUCE_AUTORELEASEPOOL { - JUCE_AUTORELEASEPOOL - const int bytesRead = [connection read: static_cast (buffer) numBytes: bytesToRead]; + const int bytesRead = connection->read (static_cast (buffer), bytesToRead); position += bytesRead; if (bytesRead == 0) @@ -381,7 +374,7 @@ public: } } - bool setPosition (int64 wantedPos) + bool setPosition (int64 wantedPos) override { if (wantedPos != position) { @@ -389,9 +382,9 @@ public: if (wantedPos < position) { - close(); + connection = nullptr; position = 0; - connection = createConnection (0, 0); + createConnection (0, 0); } skipNextBytes (wantedPos - position); @@ -400,9 +393,10 @@ public: return true; } - //============================================================================== + int statusCode; + private: - JuceURLConnection* connection; + ScopedPointer connection; String address, headers; MemoryBlock postData; int64 position; @@ -410,64 +404,42 @@ private: const bool isPost; const int timeOutMs; - void close() + void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { - [connection stop]; - [connection release]; - connection = nil; - } + jassert (connection == nullptr); - JuceURLConnection* createConnection (URL::OpenStreamProgressCallback* progressCallback, - void* progressCallbackContext) - { NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: juceStringToNS (address)] cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval: timeOutMs <= 0 ? 60.0 : (timeOutMs / 1000.0)]; - if (req == nil) - return nil; - - [req setHTTPMethod: nsStringLiteral (isPost ? "POST" : "GET")]; - //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; - - StringArray headerLines; - headerLines.addLines (headers); - headerLines.removeEmptyStrings (true); - - for (int i = 0; i < headerLines.size(); ++i) + if (req != nil) { - const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim()); - const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim()); + [req setHTTPMethod: nsStringLiteral (isPost ? "POST" : "GET")]; + //[req setCachePolicy: NSURLRequestReloadIgnoringLocalAndRemoteCacheData]; - if (key.isNotEmpty() && value.isNotEmpty()) - [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; + StringArray headerLines; + headerLines.addLines (headers); + headerLines.removeEmptyStrings (true); + + for (int i = 0; i < headerLines.size(); ++i) + { + const String key (headerLines[i].upToFirstOccurrenceOf (":", false, false).trim()); + const String value (headerLines[i].fromFirstOccurrenceOf (":", false, false).trim()); + + if (key.isNotEmpty() && value.isNotEmpty()) + [req addValue: juceStringToNS (value) forHTTPHeaderField: juceStringToNS (key)]; + } + + if (isPost && postData.getSize() > 0) + [req setHTTPBody: [NSData dataWithBytes: postData.getData() + length: postData.getSize()]]; + + connection = new URLConnectionState (req); + + if (! connection->start (progressCallback, progressCallbackContext)) + connection = nullptr; } - - if (isPost && postData.getSize() > 0) - [req setHTTPBody: [NSData dataWithBytes: postData.getData() - length: postData.getSize()]]; - - JuceURLConnection* const s = [[JuceURLConnection alloc] initWithRequest: req - withCallback: progressCallback - withContext: progressCallbackContext]; - - if ([s isOpen]) - return s; - - [s release]; - return nil; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; - -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->isError() ? nullptr : wi.release(); -} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCSuffix.h b/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCSuffix.h deleted file mode 100644 index 835825e..0000000 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_ObjCSuffix.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#ifndef __JUCE_MAC_OBJCSUFFIX_JUCEHEADER__ -#define __JUCE_MAC_OBJCSUFFIX_JUCEHEADER__ - -/** 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 - -#ifndef DOXYGEN - #define appendMacro1(a, b, c, d, e) a ## _ ## b ## _ ## c ## _ ## d ## _ ## e - #define appendMacro2(a, b, c, d, e) appendMacro1(a, b, c, d, e) - #define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER, JUCE_ObjCExtraSuffix) -#endif - -#endif // __JUCE_MAC_OBJCSUFFIX_JUCEHEADER__ diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm index bd02506..862eb53 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -26,10 +29,10 @@ String String::fromCFString (CFStringRef cfString) { if (cfString == 0) - return String::empty; + return String(); CFRange range = { 0, CFStringGetLength (cfString) }; - HeapBlock u (range.length + 1); + HeapBlock u ((size_t) range.length + 1); CFStringGetCharacters (cfString, range, u); u[range.length] = 0; @@ -39,14 +42,16 @@ String String::fromCFString (CFStringRef cfString) CFStringRef String::toCFString() const { CharPointer_UTF16 utf16 (toUTF16()); - return CFStringCreateWithCharacters (kCFAllocatorDefault, (const UniChar*) utf16.getAddress(), utf16.length()); + return CFStringCreateWithCharacters (kCFAllocatorDefault, (const UniChar*) utf16.getAddress(), (CFIndex) utf16.length()); } String String::convertToPrecomposedUnicode() const { #if JUCE_IOS JUCE_AUTORELEASEPOOL - return nsStringToJuce ([juceStringToNS (*this) precomposedStringWithCanonicalMapping]); + { + return nsStringToJuce ([juceStringToNS (*this) precomposedStringWithCanonicalMapping]); + } #else UnicodeMapping map; @@ -65,7 +70,7 @@ String String::convertToPrecomposedUnicode() const if (CreateUnicodeToTextInfo (&map, &conversionInfo) == noErr) { - const int bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (getCharPointer()); + const size_t bytesNeeded = CharPointer_UTF16::getBytesRequiredFor (getCharPointer()); HeapBlock tempOut; tempOut.calloc (bytesNeeded + 4); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm index d0f4143..2d49ae6 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -36,13 +39,17 @@ ScopedAutoReleasePool::~ScopedAutoReleasePool() //============================================================================== void Logger::outputDebugString (const String& text) { - std::cerr << text << std::endl; + // Would prefer to use std::cerr here, but avoiding it for + // the moment, due to clang JIT linkage problems. + fputs (text.toRawUTF8(), stderr); + fputs ("\n", stderr); + fflush (stderr); } //============================================================================== namespace SystemStatsHelpers { - #if JUCE_INTEL + #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM static void doCPUID (uint32& a, uint32& b, uint32& c, uint32& d, uint32 type) { uint32 la = a, lb = b, lc = c, ld = d; @@ -62,21 +69,17 @@ namespace SystemStatsHelpers } //============================================================================== -SystemStats::CPUFlags::CPUFlags() +void CPUInformation::initialise() noexcept { - #if JUCE_INTEL - uint32 familyModel = 0, extFeatures = 0, features = 0, dummy = 0; - SystemStatsHelpers::doCPUID (familyModel, extFeatures, dummy, features, 1); + #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM + uint32 a = 0, b = 0, d = 0, c = 0; + SystemStatsHelpers::doCPUID (a, b, c, d, 1); - hasMMX = (features & (1 << 23)) != 0; - hasSSE = (features & (1 << 25)) != 0; - hasSSE2 = (features & (1 << 26)) != 0; - has3DNow = (extFeatures & (1 << 31)) != 0; - #else - hasMMX = false; - hasSSE = false; - hasSSE2 = false; - has3DNow = false; + hasMMX = (d & (1u << 23)) != 0; + hasSSE = (d & (1u << 25)) != 0; + hasSSE2 = (d & (1u << 26)) != 0; + has3DNow = (b & (1u << 31)) != 0; + hasSSE3 = (c & (1u << 0)) != 0; #endif #if JUCE_IOS || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) @@ -102,9 +105,33 @@ static RLimitInitialiser rLimitInitialiser; #endif //============================================================================== +#if ! JUCE_IOS +static String getOSXVersion() +{ + JUCE_AUTORELEASEPOOL + { + NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile: + nsStringLiteral ("/System/Library/CoreServices/SystemVersion.plist")]; + + return nsStringToJuce ([dict objectForKey: nsStringLiteral ("ProductVersion")]); + } +} +#endif + SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() { - return MacOSX; + #if JUCE_IOS + return iOS; + #else + StringArray parts; + parts.addTokens (getOSXVersion(), ".", StringRef()); + + jassert (parts[0].getIntValue() == 10); + const int major = parts[1].getIntValue(); + jassert (major > 2); + + return (OperatingSystemType) (major + MacOSX_10_4 - 4); + #endif } String SystemStats::getOperatingSystemName() @@ -112,26 +139,18 @@ String SystemStats::getOperatingSystemName() #if JUCE_IOS return "iOS " + nsStringToJuce ([[UIDevice currentDevice] systemVersion]); #else - SInt32 major, minor; - Gestalt (gestaltSystemVersionMajor, &major); - Gestalt (gestaltSystemVersionMinor, &minor); - - String s ("Mac OSX "); - s << (int) major << '.' << (int) minor; - return s; + return "Mac OSX " + getOSXVersion(); #endif } -#if ! JUCE_IOS -int SystemStats::getOSXMinorVersionNumber() +String SystemStats::getDeviceDescription() { - SInt32 versionMinor = 0; - OSErr err = Gestalt (gestaltSystemVersionMinor, &versionMinor); - (void) err; - jassert (err == noErr); - return (int) versionMinor; + #if JUCE_IOS + return nsStringToJuce ([[UIDevice currentDevice] model]); + #else + return String(); + #endif } -#endif bool SystemStats::isOperatingSystem64Bit() { @@ -140,7 +159,7 @@ bool SystemStats::isOperatingSystem64Bit() #elif JUCE_64BIT return true; #else - return getOSXMinorVersionNumber() >= 6; + return getOperatingSystemType() >= MacOSX_10_6; #endif } @@ -155,7 +174,7 @@ int SystemStats::getMemorySizeInMegabytes() String SystemStats::getCpuVendor() { - #if JUCE_INTEL + #if JUCE_INTEL && ! JUCE_NO_INLINE_ASM uint32 dummy = 0; uint32 vendor[4] = { 0 }; @@ -163,7 +182,7 @@ String SystemStats::getCpuVendor() return String (reinterpret_cast (vendor), 12); #else - return String::empty; + return String(); #endif } @@ -199,39 +218,64 @@ String SystemStats::getComputerName() if (gethostname (name, sizeof (name) - 1) == 0) return String (name).upToLastOccurrenceOf (".local", false, true); - return String::empty; + return String(); +} + +static String getLocaleValue (CFStringRef key) +{ + CFLocaleRef cfLocale = CFLocaleCopyCurrent(); + const String result (String::fromCFString ((CFStringRef) CFLocaleGetValue (cfLocale, key))); + CFRelease (cfLocale); + return result; +} + +String SystemStats::getUserLanguage() { return getLocaleValue (kCFLocaleLanguageCode); } +String SystemStats::getUserRegion() { return getLocaleValue (kCFLocaleCountryCode); } + +String SystemStats::getDisplayLanguage() +{ + CFArrayRef cfPrefLangs = CFLocaleCopyPreferredLanguages(); + const String result (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (cfPrefLangs, 0))); + CFRelease (cfPrefLangs); + return result; } //============================================================================== -class HiResCounterHandler +/* NB: these are kept outside the HiResCounterInfo struct and initialised to 1 to avoid + division-by-zero errors if some other static constructor calls us before this file's + static constructors have had a chance to fill them in correctly.. +*/ +static uint64 hiResCounterNumerator = 0, hiResCounterDenominator = 1; + +class HiResCounterInfo { public: - HiResCounterHandler() + HiResCounterInfo() { mach_timebase_info_data_t timebase; (void) mach_timebase_info (&timebase); if (timebase.numer % 1000000 == 0) { - numerator = timebase.numer / 1000000; - denominator = timebase.denom; + hiResCounterNumerator = timebase.numer / 1000000; + hiResCounterDenominator = timebase.denom; } else { - numerator = timebase.numer; - denominator = timebase.denom * (int64) 1000000; + hiResCounterNumerator = timebase.numer; + hiResCounterDenominator = timebase.denom * (uint64) 1000000; } - highResTimerFrequency = (timebase.denom * (int64) 1000000000) / timebase.numer; - highResTimerToMillisecRatio = numerator / (double) denominator; + highResTimerFrequency = (timebase.denom * (uint64) 1000000000) / timebase.numer; + highResTimerToMillisecRatio = hiResCounterNumerator / (double) hiResCounterDenominator; } - inline uint32 millisecondsSinceStartup() const noexcept + uint32 millisecondsSinceStartup() const noexcept { - return (uint32) ((mach_absolute_time() * numerator) / denominator); + return (uint32) ((mach_absolute_time() * hiResCounterNumerator) / hiResCounterDenominator); } - inline double getMillisecondCounterHiRes() const noexcept + double getMillisecondCounterHiRes() const noexcept { return mach_absolute_time() * highResTimerToMillisecRatio; } @@ -239,15 +283,14 @@ public: int64 highResTimerFrequency; private: - int64 numerator, denominator; double highResTimerToMillisecRatio; }; -static HiResCounterHandler hiResCounterHandler; +static HiResCounterInfo hiResCounterInfo; -uint32 juce_millisecondsSinceStartup() noexcept { return hiResCounterHandler.millisecondsSinceStartup(); } -double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterHandler.getMillisecondCounterHiRes(); } -int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterHandler.highResTimerFrequency; } +uint32 juce_millisecondsSinceStartup() noexcept { return hiResCounterInfo.millisecondsSinceStartup(); } +double Time::getMillisecondCounterHiRes() noexcept { return hiResCounterInfo.getMillisecondCounterHiRes(); } +int64 Time::getHighResolutionTicksPerSecond() noexcept { return hiResCounterInfo.highResTimerFrequency; } int64 Time::getHighResolutionTicks() noexcept { return (int64) mach_absolute_time(); } bool Time::setSystemTimeToThisTime() const diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm index a98e995..004baeb 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Threads.mm @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -28,32 +31,45 @@ live in juce_posix_SharedCode.h! */ +#if JUCE_IOS +bool isIOSAppActive = true; +#endif + //============================================================================== -bool Process::isForegroundProcess() +JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess() { #if JUCE_MAC return [NSApp isActive]; #else - return true; // xxx change this if more than one app is ever possible on the iPhone! + return isIOSAppActive; #endif } -void Process::raisePrivilege() +JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() +{ + #if JUCE_MAC + [NSApp activateIgnoringOtherApps: YES]; + #endif +} + +JUCE_API void JUCE_CALLTYPE Process::hide() +{ + #if JUCE_MAC + [NSApp hide: nil]; + #endif +} + +JUCE_API void JUCE_CALLTYPE Process::raisePrivilege() { jassertfalse; } -void Process::lowerPrivilege() +JUCE_API void JUCE_CALLTYPE Process::lowerPrivilege() { jassertfalse; } -void Process::terminate() -{ - exit (0); -} - -void Process::setPriority (ProcessPriority) +JUCE_API void JUCE_CALLTYPE Process::setPriority (ProcessPriority) { // xxx } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h b/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h index 89daf3c..10878d0 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_osx_ObjCHelpers.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_OSX_OBJCHELPERS_JUCEHEADER__ -#define __JUCE_OSX_OBJCHELPERS_JUCEHEADER__ +#ifndef JUCE_OSX_OBJCHELPERS_H_INCLUDED +#define JUCE_OSX_OBJCHELPERS_H_INCLUDED /* This file contains a few helper functions that are used internally but which @@ -33,26 +36,134 @@ namespace { //============================================================================== - String nsStringToJuce (NSString* s) + static inline String nsStringToJuce (NSString* s) { return CharPointer_UTF8 ([s UTF8String]); } - NSString* juceStringToNS (const String& s) + static inline NSString* juceStringToNS (const String& s) { return [NSString stringWithUTF8String: s.toUTF8()]; } - NSString* nsStringLiteral (const char* const s) noexcept + static inline NSString* nsStringLiteral (const char* const s) noexcept { return [NSString stringWithUTF8String: s]; } - NSString* nsEmptyString() noexcept + static inline NSString* nsEmptyString() noexcept { return [NSString string]; } + + #if JUCE_MAC + template + static NSRect makeNSRect (const RectangleType& r) noexcept + { + return NSMakeRect (static_cast (r.getX()), + static_cast (r.getY()), + static_cast (r.getWidth()), + static_cast (r.getHeight())); + } + #endif } +//============================================================================== +template +struct NSObjectRetainer +{ + inline NSObjectRetainer (ObjectType* o) : object (o) { [object retain]; } + inline ~NSObjectRetainer() { [object release]; } -#endif // __JUCE_OSX_OBJCHELPERS_JUCEHEADER__ + ObjectType* object; +}; + +//============================================================================== +template +struct ObjCClass +{ + ObjCClass (const char* nameRoot) + : cls (objc_allocateClassPair ([SuperclassType class], getRandomisedName (nameRoot).toUTF8(), 0)) + { + } + + ~ObjCClass() + { + objc_disposeClassPair (cls); + } + + void registerClass() + { + objc_registerClassPair (cls); + } + + SuperclassType* createInstance() const + { + return class_createInstance (cls, 0); + } + + template + void addIvar (const char* name) + { + BOOL b = class_addIvar (cls, name, sizeof (Type), (uint8_t) rint (log2 (sizeof (Type))), @encode (Type)); + jassert (b); (void) b; + } + + template + void addMethod (SEL selector, FunctionType callbackFn, const char* signature) + { + BOOL b = class_addMethod (cls, selector, (IMP) callbackFn, signature); + jassert (b); (void) b; + } + + template + void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2) + { + addMethod (selector, callbackFn, (String (sig1) + sig2).toUTF8()); + } + + template + void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3) + { + addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3).toUTF8()); + } + + template + void addMethod (SEL selector, FunctionType callbackFn, const char* sig1, const char* sig2, const char* sig3, const char* sig4) + { + addMethod (selector, callbackFn, (String (sig1) + sig2 + sig3 + sig4).toUTF8()); + } + + void addProtocol (Protocol* protocol) + { + BOOL b = class_addProtocol (cls, protocol); + jassert (b); (void) b; + } + + static id sendSuperclassMessage (id self, SEL selector) + { + objc_super s = { self, [SuperclassType class] }; + return objc_msgSendSuper (&s, selector); + } + + template + static Type getIvar (id self, const char* name) + { + void* v = nullptr; + object_getInstanceVariable (self, name, &v); + return static_cast (v); + } + + Class cls; + +private: + static String getRandomisedName (const char* root) + { + return root + String::toHexString (juce::Random::getSystemRandom().nextInt64()); + } + + JUCE_DECLARE_NON_COPYABLE (ObjCClass) +}; + + +#endif // JUCE_OSX_OBJCHELPERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp b/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp index 603e0dc..d0c0b09 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_posix_NamedPipe.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -31,10 +34,10 @@ public: pipeOutName (pipePath + "_out"), pipeIn (-1), pipeOut (-1), createdPipe (createPipe), - blocked (false), stopReadOperation (false) + stopReadOperation (false) { signal (SIGPIPE, signalHandler); - siginterrupt (SIGPIPE, 1); + juce_siginterrupt (SIGPIPE, 1); } ~Pimpl() @@ -49,71 +52,65 @@ public: } } - int read (char* destBuffer, int maxBytesToRead) + int read (char* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { - int bytesRead = -1; - blocked = true; + const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); if (pipeIn == -1) { - pipeIn = ::open ((createdPipe ? pipeInName - : pipeOutName).toUTF8(), O_RDWR); + pipeIn = openPipe (createdPipe ? pipeInName : pipeOutName, O_RDWR | O_NONBLOCK, timeoutEnd); if (pipeIn == -1) - { - blocked = false; return -1; - } } - bytesRead = 0; + int bytesRead = 0; while (bytesRead < maxBytesToRead) { const int bytesThisTime = maxBytesToRead - bytesRead; - const int numRead = (int) ::read (pipeIn, destBuffer, bytesThisTime); + const int numRead = (int) ::read (pipeIn, destBuffer, (size_t) bytesThisTime); - if (numRead <= 0 || stopReadOperation) + if (numRead <= 0) { - bytesRead = -1; - break; + if (errno != EWOULDBLOCK || stopReadOperation || hasExpired (timeoutEnd)) + return -1; + + const int maxWaitingTime = 30; + waitForInput (pipeIn, timeoutEnd == 0 ? maxWaitingTime + : jmin (maxWaitingTime, + (int) (timeoutEnd - Time::getMillisecondCounter()))); + continue; } bytesRead += numRead; destBuffer += numRead; } - blocked = false; return bytesRead; } int write (const char* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { - int bytesWritten = -1; + const uint32 timeoutEnd = getTimeoutEnd (timeOutMilliseconds); if (pipeOut == -1) { - pipeOut = ::open ((createdPipe ? pipeOutName - : pipeInName).toUTF8(), O_WRONLY); + pipeOut = openPipe (createdPipe ? pipeOutName : pipeInName, O_WRONLY, timeoutEnd); if (pipeOut == -1) return -1; } - bytesWritten = 0; - const uint32 timeOutTime = Time::getMillisecondCounter() + timeOutMilliseconds; + int bytesWritten = 0; - while (bytesWritten < numBytesToWrite - && (timeOutMilliseconds < 0 || Time::getMillisecondCounter() < timeOutTime)) + while (bytesWritten < numBytesToWrite && ! hasExpired (timeoutEnd)) { const int bytesThisTime = numBytesToWrite - bytesWritten; - const int numWritten = (int) ::write (pipeOut, sourceBuffer, bytesThisTime); + const int numWritten = (int) ::write (pipeOut, sourceBuffer, (size_t) bytesThisTime); if (numWritten <= 0) - { - bytesWritten = -1; - break; - } + return -1; bytesWritten += numWritten; sourceBuffer += numWritten; @@ -132,56 +129,77 @@ public: int pipeIn, pipeOut; const bool createdPipe; - bool volatile blocked, stopReadOperation; + bool stopReadOperation; private: static void signalHandler (int) {} - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl); + static uint32 getTimeoutEnd (const int timeOutMilliseconds) + { + return timeOutMilliseconds >= 0 ? Time::getMillisecondCounter() + (uint32) timeOutMilliseconds : 0; + } + + static bool hasExpired (const uint32 timeoutEnd) + { + return timeoutEnd != 0 && Time::getMillisecondCounter() >= timeoutEnd; + } + + int openPipe (const String& name, int flags, const uint32 timeoutEnd) + { + for (;;) + { + const int p = ::open (name.toUTF8(), flags); + + if (p != -1 || hasExpired (timeoutEnd) || stopReadOperation) + return p; + + Thread::sleep (2); + } + } + + static void waitForInput (const int handle, const int timeoutMsecs) noexcept + { + struct timeval timeout; + timeout.tv_sec = timeoutMsecs / 1000; + timeout.tv_usec = (timeoutMsecs % 1000) * 1000; + + fd_set rset; + FD_ZERO (&rset); + FD_SET (handle, &rset); + + select (handle + 1, &rset, nullptr, 0, &timeout); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; -NamedPipe::NamedPipe() +void NamedPipe::close() { -} - -NamedPipe::~NamedPipe() -{ - close(); -} - -void NamedPipe::cancelPendingReads() -{ - while (pimpl != nullptr && pimpl->blocked) + if (pimpl != nullptr) { pimpl->stopReadOperation = true; char buffer[1] = { 0 }; - int bytesWritten = (int) ::write (pimpl->pipeIn, buffer, 1); - (void) bytesWritten; + ssize_t done = ::write (pimpl->pipeIn, buffer, 1); + (void) done; - int timeout = 2000; - while (pimpl->blocked && --timeout >= 0) - Thread::sleep (2); - - pimpl->stopReadOperation = false; + ScopedWriteLock sl (lock); + pimpl = nullptr; } } -void NamedPipe::close() -{ - cancelPendingReads(); - ScopedPointer deleter (pimpl); // (clears the pimpl member variable before deleting it) -} - bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) { - close(); - #if JUCE_IOS pimpl = new Pimpl (File::getSpecialLocation (File::tempDirectory) .getChildFile (File::createLegalFileName (pipeName)).getFullPathName(), createPipe); #else - pimpl = new Pimpl ("/tmp/" + File::createLegalFileName (pipeName), createPipe); + String file (pipeName); + + if (! File::isAbsolutePath (file)) + file = "/tmp/" + File::createLegalFileName (file); + + pimpl = new Pimpl (file, createPipe); #endif if (createPipe && ! pimpl->createFifos()) @@ -193,17 +211,14 @@ bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) return true; } -int NamedPipe::read (void* destBuffer, int maxBytesToRead, int /*timeOutMilliseconds*/) +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) { - return pimpl != nullptr ? pimpl->read (static_cast (destBuffer), maxBytesToRead) : -1; + ScopedReadLock sl (lock); + return pimpl != nullptr ? pimpl->read (static_cast (destBuffer), maxBytesToRead, timeOutMilliseconds) : -1; } int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { + ScopedReadLock sl (lock); return pimpl != nullptr ? pimpl->write (static_cast (sourceBuffer), numBytesToWrite, timeOutMilliseconds) : -1; } - -bool NamedPipe::isOpen() const -{ - return pimpl != nullptr; -} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h index 417fee8..5e0ba45 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h @@ -1,36 +1,31 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -/* - 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() noexcept { pthread_mutexattr_t atts; @@ -39,149 +34,101 @@ CriticalSection::CriticalSection() noexcept #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); #endif - pthread_mutex_init (&internal, &atts); -} - -CriticalSection::~CriticalSection() noexcept -{ - pthread_mutex_destroy (&internal); -} - -void CriticalSection::enter() const noexcept -{ - pthread_mutex_lock (&internal); -} - -bool CriticalSection::tryEnter() const noexcept -{ - return pthread_mutex_trylock (&internal) == 0; -} - -void CriticalSection::exit() const noexcept -{ - pthread_mutex_unlock (&internal); + pthread_mutex_init (&lock, &atts); + pthread_mutexattr_destroy (&atts); } +CriticalSection::~CriticalSection() noexcept { pthread_mutex_destroy (&lock); } +void CriticalSection::enter() const noexcept { pthread_mutex_lock (&lock); } +bool CriticalSection::tryEnter() const noexcept { return pthread_mutex_trylock (&lock) == 0; } +void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&lock); } //============================================================================== -class WaitableEventImpl +WaitableEvent::WaitableEvent (const bool useManualReset) noexcept + : triggered (false), manualReset (useManualReset) { -public: - WaitableEventImpl (const bool manualReset_) - : triggered (false), - manualReset (manualReset_) - { - pthread_cond_init (&condition, 0); + pthread_cond_init (&condition, 0); - pthread_mutexattr_t atts; - pthread_mutexattr_init (&atts); - #if ! JUCE_ANDROID - pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); - #endif - pthread_mutex_init (&mutex, &atts); - } - - ~WaitableEventImpl() - { - pthread_cond_destroy (&condition); - pthread_mutex_destroy (&mutex); - } - - bool wait (const int timeOutMillisecs) noexcept - { - pthread_mutex_lock (&mutex); - - if (! triggered) - { - if (timeOutMillisecs < 0) - { - do - { - pthread_cond_wait (&condition, &mutex); - } - while (! triggered); - } - else - { - struct timeval now; - gettimeofday (&now, 0); - - struct timespec time; - time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000); - time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; - - if (time.tv_nsec >= 1000000000) - { - time.tv_nsec -= 1000000000; - time.tv_sec++; - } - - do - { - if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT) - { - pthread_mutex_unlock (&mutex); - return false; - } - } - while (! triggered); - } - } - - if (! manualReset) - triggered = false; - - pthread_mutex_unlock (&mutex); - return true; - } - - void signal() noexcept - { - pthread_mutex_lock (&mutex); - triggered = true; - pthread_cond_broadcast (&condition); - pthread_mutex_unlock (&mutex); - } - - void reset() noexcept - { - pthread_mutex_lock (&mutex); - triggered = false; - pthread_mutex_unlock (&mutex); - } - -private: - pthread_cond_t condition; - pthread_mutex_t mutex; - bool triggered; - const bool manualReset; - - JUCE_DECLARE_NON_COPYABLE (WaitableEventImpl); -}; - -WaitableEvent::WaitableEvent (const bool manualReset) noexcept - : internal (new WaitableEventImpl (manualReset)) -{ + pthread_mutexattr_t atts; + pthread_mutexattr_init (&atts); + #if ! JUCE_ANDROID + pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); + #endif + pthread_mutex_init (&mutex, &atts); } WaitableEvent::~WaitableEvent() noexcept { - delete static_cast (internal); + pthread_cond_destroy (&condition); + pthread_mutex_destroy (&mutex); } bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept { - return static_cast (internal)->wait (timeOutMillisecs); + pthread_mutex_lock (&mutex); + + if (! triggered) + { + if (timeOutMillisecs < 0) + { + do + { + pthread_cond_wait (&condition, &mutex); + } + while (! triggered); + } + else + { + struct timeval now; + gettimeofday (&now, 0); + + struct timespec time; + time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000); + time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000; + + if (time.tv_nsec >= 1000000000) + { + time.tv_nsec -= 1000000000; + time.tv_sec++; + } + + do + { + if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT) + { + pthread_mutex_unlock (&mutex); + return false; + } + } + while (! triggered); + } + } + + if (! manualReset) + triggered = false; + + pthread_mutex_unlock (&mutex); + return true; } void WaitableEvent::signal() const noexcept { - static_cast (internal)->signal(); + pthread_mutex_lock (&mutex); + + if (! triggered) + { + triggered = true; + pthread_cond_broadcast (&condition); + } + + pthread_mutex_unlock (&mutex); } void WaitableEvent::reset() const noexcept { - static_cast (internal)->reset(); + pthread_mutex_lock (&mutex); + triggered = false; + pthread_mutex_unlock (&mutex); } //============================================================================== @@ -190,9 +137,17 @@ void JUCE_CALLTYPE Thread::sleep (int millisecs) struct timespec time; time.tv_sec = millisecs / 1000; time.tv_nsec = (millisecs % 1000) * 1000000; - nanosleep (&time, 0); + nanosleep (&time, nullptr); } +void JUCE_CALLTYPE Process::terminate() +{ + #if JUCE_ANDROID + _exit (EXIT_FAILURE); + #else + std::_Exit (EXIT_FAILURE); + #endif +} //============================================================================== const juce_wchar File::separator = '/'; @@ -205,7 +160,7 @@ File File::getCurrentWorkingDirectory() char localBuffer [1024]; char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1); - int bufferSize = 4096; + size_t bufferSize = 4096; while (cwd == nullptr && errno == ERANGE) { @@ -222,6 +177,21 @@ bool File::setAsCurrentWorkingDirectory() const return chdir (getFullPathName().toUTF8()) == 0; } +//============================================================================== +// The unix siginterrupt function is deprecated - this does the same job. +int juce_siginterrupt (int sig, int flag) +{ + struct ::sigaction act; + (void) ::sigaction (sig, nullptr, &act); + + if (flag != 0) + act.sa_flags &= ~SA_RESTART; + else + act.sa_flags |= SA_RESTART; + + return ::sigaction (sig, &act, nullptr); +} + //============================================================================== namespace { @@ -281,10 +251,8 @@ namespace return value == -1 ? getResultForErrno() : Result::ok(); } - int getFD (void* handle) noexcept - { - return (int) (pointer_sized_int) handle; - } + int getFD (void* handle) noexcept { return (int) (pointer_sized_int) handle; } + void* fdToVoidPointer (int fd) noexcept { return (void*) (pointer_sized_int) fd; } } bool File::isDirectory() const @@ -312,6 +280,12 @@ int64 File::getSize() const return juce_stat (fullPath, info) ? info.st_size : 0; } +uint64 File::getFileIdentifier() const +{ + juce_statStruct info; + return juce_stat (fullPath, info) ? (uint64) info.st_ino : 0; +} + //============================================================================== bool File::hasWriteAccess() const { @@ -415,23 +389,18 @@ int64 juce_fileSetPosition (void* handle, int64 pos) void FileInputStream::openHandle() { - totalSize = file.getSize(); - const int f = open (file.getFullPathName().toUTF8(), O_RDONLY, 00644); if (f != -1) - fileHandle = (void*) f; + fileHandle = fdToVoidPointer (f); else status = getResultForErrno(); } -void FileInputStream::closeHandle() +FileInputStream::~FileInputStream() { if (fileHandle != 0) - { close (getFD (fileHandle)); - fileHandle = 0; - } } size_t FileInputStream::readInternal (void* const buffer, const size_t numBytes) @@ -465,7 +434,7 @@ void FileOutputStream::openHandle() if (currentPosition >= 0) { - fileHandle = (void*) f; + fileHandle = fdToVoidPointer (f); } else { @@ -483,7 +452,7 @@ void FileOutputStream::openHandle() const int f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644); if (f != -1) - fileHandle = (void*) f; + fileHandle = fdToVoidPointer (f); else status = getResultForErrno(); } @@ -498,7 +467,7 @@ void FileOutputStream::closeHandle() } } -int FileOutputStream::writeInternal (const void* const data, const int numBytes) +ssize_t FileOutputStream::writeInternal (const void* const data, const size_t numBytes) { ssize_t result = 0; @@ -510,14 +479,25 @@ int FileOutputStream::writeInternal (const void* const data, const int numBytes) status = getResultForErrno(); } - return (int) result; + return result; } void FileOutputStream::flushInternal() { if (fileHandle != 0) + { if (fsync (getFD (fileHandle)) == -1) status = getResultForErrno(); + + #if JUCE_ANDROID + // This stuff tells the OS to asynchronously update the metadata + // that the OS has cached aboud the file - this metadata is used + // when the device is acting as a USB drive, and unless it's explicitly + // refreshed, it'll get out of step with the real file. + const LocalRef t (javaString (file.getFullPathName())); + android.activity.callVoidMethod (JuceAppActivity.scanFile, t.get()); + #endif + } } Result FileOutputStream::truncate() @@ -530,28 +510,43 @@ Result FileOutputStream::truncate() } //============================================================================== -MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode) - : address (nullptr), - length (0), - fileHandle (0) +String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue) +{ + if (const char* s = ::getenv (name.toUTF8())) + return String::fromUTF8 (s); + + return defaultValue; +} + +//============================================================================== +void MemoryMappedFile::openInternal (const File& file, AccessMode mode) { jassert (mode == readOnly || mode == readWrite); + if (range.getStart() > 0) + { + const long pageSize = sysconf (_SC_PAGE_SIZE); + range.setStart (range.getStart() - (range.getStart() % pageSize)); + } + fileHandle = open (file.getFullPathName().toUTF8(), mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644); if (fileHandle != -1) { - const int64 fileSize = file.getSize(); - - void* m = mmap (0, (size_t) fileSize, + void* m = mmap (0, (size_t) range.getLength(), mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ, - MAP_SHARED, fileHandle, 0); + MAP_SHARED, fileHandle, + (off_t) range.getStart()); if (m != MAP_FAILED) { address = m; - length = (size_t) fileSize; + madvise (m, (size_t) range.getLength(), MADV_SEQUENTIAL); + } + else + { + range = Range(); } } } @@ -559,17 +554,23 @@ MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMo MemoryMappedFile::~MemoryMappedFile() { if (address != nullptr) - munmap (address, length); + munmap (address, (size_t) range.getLength()); if (fileHandle != 0) close (fileHandle); } //============================================================================== +#if JUCE_PROJUCER_LIVE_BUILD +extern "C" const char* juce_getCurrentExecutablePath(); +#endif + File juce_getExecutableFile(); File juce_getExecutableFile() { - #if JUCE_ANDROID + #if JUCE_PROJUCER_LIVE_BUILD + return File (juce_getCurrentExecutablePath()); + #elif JUCE_ANDROID return File (android.appFile); #else struct DLAddrReader @@ -616,7 +617,8 @@ String File::getVolumeLabel() const char mountPointSpace [MAXPATHLEN]; } attrBuf; - struct attrlist attrList = { 0 }; + struct attrlist attrList; + zerostruct (attrList); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) attrList.bitmapcount = ATTR_BIT_MAP_COUNT; attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME; @@ -637,7 +639,7 @@ String File::getVolumeLabel() const } #endif - return String::empty; + return String(); } int File::getVolumeSerialNumber() const @@ -685,34 +687,55 @@ String juce_getOutputFromCommand (const String& command) //============================================================================== +#if JUCE_IOS class InterProcessLock::Pimpl { public: - Pimpl (const String& name, const int timeOutMillisecs) + Pimpl (const String&, int) + : handle (1), refCount (1) // On iOS just fake success.. + { + } + + int handle, refCount; +}; + +#else + +class InterProcessLock::Pimpl +{ +public: + Pimpl (const String& lockName, const int timeOutMillisecs) : handle (0), refCount (1) { - #if JUCE_IOS - handle = 1; // On iOS we can't run multiple apps, so just assume success. + #if JUCE_MAC + if (! createLockFile (File ("~/Library/Caches/com.juce.locks").getChildFile (lockName), timeOutMillisecs)) + // Fallback if the user's home folder is on a network drive with no ability to lock.. + createLockFile (File ("/tmp/com.juce.locks").getChildFile (lockName), timeOutMillisecs); + #else + File tempFolder ("/var/tmp"); + if (! tempFolder.isDirectory()) + tempFolder = "/tmp"; - // Note that we can't get the normal temp folder here, as it might be different for each app. - #if JUCE_MAC - File tempFolder ("~/Library/Caches/com.juce.locks"); - #else - File tempFolder ("/var/tmp"); + createLockFile (tempFolder.getChildFile (lockName), timeOutMillisecs); + #endif + } - if (! tempFolder.isDirectory()) - tempFolder = "/tmp"; - #endif + ~Pimpl() + { + closeFile(); + } - const File temp (tempFolder.getChildFile (name)); - - temp.create(); - handle = open (temp.getFullPathName().toUTF8(), O_RDWR); + bool createLockFile (const File& file, const int timeOutMillisecs) + { + file.create(); + handle = open (file.getFullPathName().toUTF8(), O_RDWR); if (handle != 0) { - struct flock fl = { 0 }; + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; fl.l_type = F_WRLCK; @@ -723,10 +746,15 @@ public: const int result = fcntl (handle, F_SETLK, &fl); if (result >= 0) - return; + return true; - if (errno != EINTR) + const int error = errno; + + if (error != EINTR) { + if (error == EBADF || error == ENOTSUP) + return false; + if (timeOutMillisecs == 0 || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime)) break; @@ -737,20 +765,16 @@ public: } closeFile(); - #endif - } - - ~Pimpl() - { - closeFile(); + return true; // only false if there's a file system error. Failure to lock still returns true. } void closeFile() { - #if ! JUCE_IOS if (handle != 0) { - struct flock fl = { 0 }; + struct flock fl; + zerostruct (fl); + fl.l_whence = SEEK_SET; fl.l_type = F_UNLCK; @@ -760,14 +784,13 @@ public: close (handle); handle = 0; } - #endif } int handle, refCount; }; +#endif -InterProcessLock::InterProcessLock (const String& name_) - : name (name_) +InterProcessLock::InterProcessLock (const String& nm) : name (nm) { } @@ -812,18 +835,20 @@ extern "C" void* threadEntryProc (void*); extern "C" void* threadEntryProc (void* userData) { JUCE_AUTORELEASEPOOL - - #if JUCE_ANDROID - struct AndroidThreadScope { - AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); } - ~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); } - }; + #if JUCE_ANDROID + struct AndroidThreadScope + { + AndroidThreadScope() { threadLocalJNIEnvHolder.attach(); } + ~AndroidThreadScope() { threadLocalJNIEnvHolder.detach(); } + }; - const AndroidThreadScope androidEnv; - #endif + const AndroidThreadScope androidEnv; + #endif + + juce_threadEntryPoint (userData); + } - juce_threadEntryPoint (userData); return nullptr; } @@ -858,13 +883,19 @@ void Thread::killThread() } } -void Thread::setCurrentThreadName (const String& name) +void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) { #if JUCE_IOS || (JUCE_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) JUCE_AUTORELEASEPOOL - [[NSThread currentThread] setName: juceStringToNS (name)]; + { + [[NSThread currentThread] setName: juceStringToNS (name)]; + } #elif JUCE_LINUX - prctl (PR_SET_NAME, name.toUTF8().getAddress(), 0, 0, 0); + #if (__GLIBC__ * 1000 + __GLIBC_MINOR__) >= 2012 + pthread_setname_np (pthread_self(), name.toRawUTF8()); + #else + prctl (PR_SET_NAME, name.toRawUTF8(), 0, 0, 0); + #endif #endif } @@ -874,7 +905,7 @@ bool Thread::setThreadPriority (void* handle, int priority) int policy; priority = jlimit (0, 10, priority); - if (handle == 0) + if (handle == nullptr) handle = (void*) pthread_self(); if (pthread_getschedparam ((pthread_t) handle, &policy, ¶m) != 0) @@ -889,12 +920,12 @@ bool Thread::setThreadPriority (void* handle, int priority) return pthread_setschedparam ((pthread_t) handle, policy, ¶m) == 0; } -Thread::ThreadID Thread::getCurrentThreadId() +Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId() { return (ThreadID) pthread_self(); } -void Thread::yield() +void JUCE_CALLTYPE Thread::yield() { sched_yield(); } @@ -908,7 +939,7 @@ void Thread::yield() #define SUPPORT_AFFINITIES 1 #endif -void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) +void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) { #if SUPPORT_AFFINITIES cpu_set_t affinity; @@ -941,8 +972,8 @@ void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) bool DynamicLibrary::open (const String& name) { close(); - handle = dlopen (name.toUTF8(), RTLD_LOCAL | RTLD_NOW); - return handle != 0; + handle = dlopen (name.isEmpty() ? nullptr : name.toUTF8().getAddress(), RTLD_LOCAL | RTLD_NOW); + return handle != nullptr; } void DynamicLibrary::close() @@ -965,9 +996,14 @@ void* DynamicLibrary::getFunction (const String& functionName) noexcept class ChildProcess::ActiveProcess { public: - ActiveProcess (const StringArray& arguments) + ActiveProcess (const StringArray& arguments, int streamFlags) : childPID (0), pipeHandle (0), readHandle (0) { + // Looks like you're trying to launch a non-existent exe or a folder (perhaps on OSX + // you're trying to launch the .app folder rather than the actual binary inside it?) + jassert ((! arguments[0].containsChar ('/')) + || File::getCurrentWorkingDirectory().getChildFile (arguments[0]).existsAsFile()); + int pipeHandles[2] = { 0 }; if (pipe (pipeHandles) == 0) @@ -983,12 +1019,23 @@ public: { // we're the child process.. close (pipeHandles[0]); // close the read handle - dup2 (pipeHandles[1], 1); // turns the pipe into stdout + + if ((streamFlags & wantStdOut) != 0) + dup2 (pipeHandles[1], 1); // turns the pipe into stdout + else + close (STDOUT_FILENO); + + if ((streamFlags & wantStdErr) != 0) + dup2 (pipeHandles[1], 2); + else + close (STDERR_FILENO); + close (pipeHandles[1]); Array argv; for (int i = 0; i < arguments.size(); ++i) - argv.add (arguments[i].toUTF8().getAddress()); + if (arguments[i].isNotEmpty()) + argv.add (const_cast (arguments[i].toUTF8().getAddress())); argv.add (nullptr); @@ -1014,55 +1061,74 @@ public: close (pipeHandle); } - bool isRunning() const + bool isRunning() const noexcept { if (childPID != 0) { int childState; const int pid = waitpid (childPID, &childState, WNOHANG); - return pid > 0 && ! (WIFEXITED (childState) || WIFSIGNALED (childState)); + return pid == 0 || ! (WIFEXITED (childState) || WIFSIGNALED (childState)); } return false; } - int read (void* const dest, const int numBytes) + int read (void* const dest, const int numBytes) noexcept { jassert (dest != nullptr); + #ifdef fdopen + #error // the zlib headers define this function as NULL! + #endif + if (readHandle == 0 && childPID != 0) readHandle = fdopen (pipeHandle, "r"); if (readHandle != 0) - return fread (dest, 1, numBytes, readHandle); + return (int) fread (dest, 1, (size_t) numBytes, readHandle); return 0; } - bool killProcess() const + bool killProcess() const noexcept { return ::kill (childPID, SIGKILL) == 0; } + uint32 getExitCode() const noexcept + { + if (childPID != 0) + { + int childState = 0; + const int pid = waitpid (childPID, &childState, WNOHANG); + + if (pid >= 0 && WIFEXITED (childState)) + return WEXITSTATUS (childState); + } + + return 0; + } + int childPID; private: int pipeHandle; FILE* readHandle; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) }; -bool ChildProcess::start (const String& command) +bool ChildProcess::start (const String& command, int streamFlags) { - StringArray tokens; - tokens.addTokens (command, true); - tokens.removeEmptyStrings (true); + return start (StringArray::fromTokens (command, true), streamFlags); +} - if (tokens.size() == 0) +bool ChildProcess::start (const StringArray& args, int streamFlags) +{ + if (args.size() == 0) return false; - activeProcess = new ActiveProcess (tokens); + activeProcess = new ActiveProcess (args, streamFlags); if (activeProcess->childPID == 0) activeProcess = nullptr; @@ -1070,17 +1136,154 @@ bool ChildProcess::start (const String& command) return activeProcess != nullptr; } -bool ChildProcess::isRunning() const +//============================================================================== +struct HighResolutionTimer::Pimpl { - return activeProcess != nullptr && activeProcess->isRunning(); -} + Pimpl (HighResolutionTimer& t) : owner (t), thread (0), shouldStop (false) + { + } -int ChildProcess::readProcessOutput (void* dest, int numBytes) -{ - return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0; -} + ~Pimpl() + { + jassert (thread == 0); + } -bool ChildProcess::kill() -{ - return activeProcess == nullptr || activeProcess->killProcess(); -} + void start (int newPeriod) + { + periodMs = newPeriod; + + if (thread == 0) + { + shouldStop = false; + + if (pthread_create (&thread, nullptr, timerThread, this) == 0) + setThreadToRealtime (thread, (uint64) newPeriod); + else + jassertfalse; + } + } + + void stop() + { + if (thread != 0) + { + shouldStop = true; + + while (thread != 0 && thread != pthread_self()) + Thread::yield(); + } + } + + HighResolutionTimer& owner; + int volatile periodMs; + +private: + pthread_t thread; + bool volatile shouldStop; + + static void* timerThread (void* param) + { + #if ! JUCE_ANDROID + int dummy; + pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, &dummy); + #endif + + reinterpret_cast (param)->timerThread(); + return nullptr; + } + + void timerThread() + { + Clock clock (periodMs); + + while (! shouldStop) + { + clock.wait(); + owner.hiResTimerCallback(); + } + + periodMs = 0; + thread = 0; + } + + struct Clock + { + #if JUCE_MAC || JUCE_IOS + Clock (double millis) noexcept + { + mach_timebase_info_data_t timebase; + (void) mach_timebase_info (&timebase); + delta = (((uint64_t) (millis * 1000000.0)) * timebase.denom) / timebase.numer; + time = mach_absolute_time(); + } + + void wait() noexcept + { + time += delta; + mach_wait_until (time); + } + + uint64_t time, delta; + + #elif JUCE_ANDROID + Clock (double millis) noexcept : delta ((uint64) (millis * 1000000)) + { + } + + void wait() noexcept + { + struct timespec t; + t.tv_sec = (time_t) (delta / 1000000000); + t.tv_nsec = (long) (delta % 1000000000); + nanosleep (&t, nullptr); + } + + uint64 delta; + #else + Clock (double millis) noexcept : delta ((uint64) (millis * 1000000)) + { + struct timespec t; + clock_gettime (CLOCK_MONOTONIC, &t); + time = 1000000000 * (int64) t.tv_sec + t.tv_nsec; + } + + void wait() noexcept + { + time += delta; + + struct timespec t; + t.tv_sec = (time_t) (time / 1000000000); + t.tv_nsec = (long) (time % 1000000000); + + clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &t, nullptr); + } + + uint64 time, delta; + #endif + }; + + static bool setThreadToRealtime (pthread_t thread, uint64 periodMs) + { + #if JUCE_MAC || JUCE_IOS + thread_time_constraint_policy_data_t policy; + policy.period = (uint32_t) (periodMs * 1000000); + policy.computation = 50000; + policy.constraint = policy.period; + policy.preemptible = true; + + return thread_policy_set (pthread_mach_thread_np (thread), + THREAD_TIME_CONSTRAINT_POLICY, + (thread_policy_t) &policy, + THREAD_TIME_CONSTRAINT_POLICY_COUNT) == KERN_SUCCESS; + + #else + (void) periodMs; + struct sched_param param; + param.sched_priority = sched_get_priority_max (SCHED_RR); + return pthread_setschedparam (thread, SCHED_RR, ¶m) == 0; + + #endif + } + + JUCE_DECLARE_NON_COPYABLE (Pimpl) +}; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h b/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h index 8032999..0dc0efc 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_ComSmartPtr.h @@ -1,31 +1,56 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_WIN32_COMSMARTPTR_JUCEHEADER__ -#define __JUCE_WIN32_COMSMARTPTR_JUCEHEADER__ +#ifndef JUCE_WIN32_COMSMARTPTR_H_INCLUDED +#define JUCE_WIN32_COMSMARTPTR_H_INCLUDED +#if ! (defined (_MSC_VER) || defined (__uuidof)) +template struct UUIDGetter { static CLSID get() { jassertfalse; return CLSID(); } }; +#define __uuidof(x) UUIDGetter::get() +#endif + +inline GUID uuidFromString (const char* const s) noexcept +{ + unsigned long p0; + unsigned int p1, p2, p3, p4, p5, p6, p7, p8, p9, p10; + + #ifndef _MSC_VER + sscanf + #else + sscanf_s + #endif + (s, "%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", + &p0, &p1, &p2, &p3, &p4, &p5, &p6, &p7, &p8, &p9, &p10); + + GUID g = { p0, (uint16) p1, (uint16) p2, { (uint8) p3, (uint8) p4, (uint8) p5, (uint8) p6, + (uint8) p7, (uint8) p8, (uint8) p9, (uint8) p10 }}; + return g; +} //============================================================================== /** A simple COM smart pointer. @@ -34,10 +59,10 @@ template class ComSmartPtr { public: - ComSmartPtr() throw() : p (0) {} - ComSmartPtr (ComClass* const p_) : p (p_) { if (p_ != 0) p_->AddRef(); } - ComSmartPtr (const ComSmartPtr& p_) : p (p_.p) { if (p != 0) p ->AddRef(); } - ~ComSmartPtr() { release(); } + ComSmartPtr() throw() : p (0) {} + ComSmartPtr (ComClass* const obj) : p (obj) { if (p) p->AddRef(); } + ComSmartPtr (const ComSmartPtr& other) : p (other.p) { if (p) p->AddRef(); } + ~ComSmartPtr() { release(); } operator ComClass*() const throw() { return p; } ComClass& operator*() const throw() { return *p; } @@ -63,12 +88,9 @@ public: HRESULT CoCreateInstance (REFCLSID classUUID, DWORD dwClsContext = CLSCTX_INPROC_SERVER) { - #if ! JUCE_MINGW - return ::CoCreateInstance (classUUID, 0, dwClsContext, __uuidof (ComClass), (void**) resetAndGetPointerAddress()); - #else - jassertfalse; // need to find a mingw equivalent of __uuidof to make this possible - return E_NOTIMPL; - #endif + HRESULT hr = ::CoCreateInstance (classUUID, 0, dwClsContext, __uuidof (ComClass), (void**) resetAndGetPointerAddress()); + jassert (hr != CO_E_NOTINITIALIZED); // You haven't called CoInitialize for the current thread! + return hr; } template @@ -83,12 +105,7 @@ public: template HRESULT QueryInterface (ComSmartPtr& destObject) const { - #if ! JUCE_MINGW return this->QueryInterface (__uuidof (OtherComClass), destObject); - #else - jassertfalse; // need to find a mingw equivalent of __uuidof to make this possible - return E_NOTIMPL; - #endif } private: @@ -107,16 +124,29 @@ template class ComBaseClassHelperBase : public ComClass { public: - ComBaseClassHelperBase() : refCount (1) {} + ComBaseClassHelperBase (unsigned int initialRefCount) : refCount (initialRefCount) {} virtual ~ComBaseClassHelperBase() {} ULONG __stdcall AddRef() { return ++refCount; } ULONG __stdcall Release() { const ULONG r = --refCount; if (r == 0) delete this; return r; } - void resetReferenceCount() noexcept { refCount = 0; } - protected: ULONG refCount; + + JUCE_COMRESULT QueryInterface (REFIID refId, void** result) + { + if (refId == IID_IUnknown) + return castToType (result); + + *result = 0; + return E_NOINTERFACE; + } + + template + JUCE_COMRESULT castToType (void** result) + { + this->AddRef(); *result = dynamic_cast (this); return S_OK; + } }; /** Handy base class for writing COM objects, providing ref-counting and a basic QueryInterface method. @@ -125,21 +155,16 @@ template class ComBaseClassHelper : public ComBaseClassHelperBase { public: - ComBaseClassHelper() {} + ComBaseClassHelper (unsigned int initialRefCount = 1) : ComBaseClassHelperBase (initialRefCount) {} ~ComBaseClassHelper() {} JUCE_COMRESULT QueryInterface (REFIID refId, void** result) { - #if ! JUCE_MINGW - if (refId == __uuidof (ComClass)) { AddRef(); *result = dynamic_cast (this); return S_OK; } - if (refId == IID_IUnknown) { AddRef(); *result = dynamic_cast (this); return S_OK; } - #else - jassertfalse; // need to find a mingw equivalent of __uuidof to make this possible - #endif + if (refId == __uuidof (ComClass)) + return this->template castToType (result); - *result = 0; - return E_NOINTERFACE; + return ComBaseClassHelperBase ::QueryInterface (refId, result); } }; -#endif // __JUCE_WIN32_COMSMARTPTR_JUCEHEADER__ +#endif // JUCE_WIN32_COMSMARTPTR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp index 2052961..80ad084 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -39,7 +42,7 @@ namespace WindowsFileHelpers { static_jassert (sizeof (ULARGE_INTEGER) == sizeof (FILETIME)); // tell me if this fails! - return (int64) ((reinterpret_cast (ft)->QuadPart - literal64bit (116444736000000000)) / 10000); + return (int64) ((reinterpret_cast (ft)->QuadPart - 116444736000000000LL) / 10000); } FILETIME* timeToFileTime (const int64 time, FILETIME* const ft) noexcept @@ -47,7 +50,7 @@ namespace WindowsFileHelpers if (time <= 0) return nullptr; - reinterpret_cast (ft)->QuadPart = (ULONGLONG) (time * 10000 + literal64bit (116444736000000000)); + reinterpret_cast (ft)->QuadPart = (ULONGLONG) (time * 10000 + 116444736000000000LL); return ft; } @@ -59,7 +62,7 @@ namespace WindowsFileHelpers const size_t numBytes = CharPointer_UTF16::getBytesRequiredFor (path.getCharPointer()) + 4; HeapBlock pathCopy; pathCopy.calloc (numBytes, 1); - path.copyToUTF16 (pathCopy, (int) numBytes); + path.copyToUTF16 (pathCopy, numBytes); if (PathStripToRoot (pathCopy)) path = static_cast (pathCopy); @@ -90,7 +93,7 @@ namespace WindowsFileHelpers if (SHGetSpecialFolderPath (0, path, type, FALSE)) return File (String (path)); - return File::nonexistent; + return File(); } File getModuleFileName (HINSTANCE moduleHandle) @@ -184,7 +187,7 @@ bool File::moveToTrash() const const size_t numBytes = CharPointer_UTF16::getBytesRequiredFor (fullPath.getCharPointer()) + 8; HeapBlock doubleNullTermPath; doubleNullTermPath.calloc (numBytes, 1); - fullPath.copyToUTF16 (doubleNullTermPath, (int) numBytes); + fullPath.copyToUTF16 (doubleNullTermPath, numBytes); SHFILEOPSTRUCT fos = { 0 }; fos.wFunc = FO_DELETE; @@ -222,8 +225,6 @@ int64 juce_fileSetPosition (void* handle, int64 pos) void FileInputStream::openHandle() { - totalSize = file.getSize(); - HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); @@ -233,7 +234,7 @@ void FileInputStream::openHandle() status = WindowsFileHelpers::getResultForLastError(); } -void FileInputStream::closeHandle() +FileInputStream::~FileInputStream() { CloseHandle ((HANDLE) fileHandle); } @@ -280,7 +281,7 @@ void FileOutputStream::closeHandle() CloseHandle ((HANDLE) fileHandle); } -int FileOutputStream::writeInternal (const void* buffer, int numBytes) +ssize_t FileOutputStream::writeInternal (const void* buffer, size_t numBytes) { if (fileHandle != nullptr) { @@ -288,7 +289,7 @@ int FileOutputStream::writeInternal (const void* buffer, int numBytes) if (! WriteFile ((HANDLE) fileHandle, buffer, (DWORD) numBytes, &actualNum, 0)) status = WindowsFileHelpers::getResultForLastError(); - return (int) actualNum; + return (ssize_t) actualNum; } return 0; @@ -312,13 +313,18 @@ Result FileOutputStream::truncate() } //============================================================================== -MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode) - : address (nullptr), - length (0), - fileHandle (nullptr) +void MemoryMappedFile::openInternal (const File& file, AccessMode mode) { jassert (mode == readOnly || mode == readWrite); + if (range.getStart() > 0) + { + SYSTEM_INFO systemInfo; + GetNativeSystemInfo (&systemInfo); + + range.setStart (range.getStart() - (range.getStart() % systemInfo.dwAllocationGranularity)); + } + DWORD accessMode = GENERIC_READ, createType = OPEN_EXISTING; DWORD protect = PAGE_READONLY, access = FILE_MAP_READ; @@ -331,20 +337,21 @@ MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMo } HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode, FILE_SHARE_READ, 0, - createType, FILE_ATTRIBUTE_NORMAL, 0); + createType, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0); if (h != INVALID_HANDLE_VALUE) { fileHandle = (void*) h; - const int64 fileSize = file.getSize(); - HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (fileSize >> 32), (DWORD) fileSize, 0); + HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (range.getEnd() >> 32), (DWORD) range.getEnd(), 0); + if (mappingHandle != 0) { - address = MapViewOfFile (mappingHandle, access, 0, 0, (SIZE_T) fileSize); + address = MapViewOfFile (mappingHandle, access, (DWORD) (range.getStart() >> 32), + (DWORD) range.getStart(), (SIZE_T) range.getLength()); - if (address != nullptr) - length = (size_t) fileSize; + if (address == nullptr) + range = Range(); CloseHandle (mappingHandle); } @@ -467,6 +474,28 @@ int64 File::getVolumeTotalSize() const return WindowsFileHelpers::getDiskSpaceInfo (getFullPathName(), true); } +uint64 File::getFileIdentifier() const +{ + uint64 result = 0; + + HANDLE h = CreateFile (getFullPathName().toWideCharPointer(), + GENERIC_READ, FILE_SHARE_READ, nullptr, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + + if (h != INVALID_HANDLE_VALUE) + { + BY_HANDLE_FILE_INFORMATION info; + zerostruct (info); + + if (GetFileInformationByHandle (h, &info)) + result = (((uint64) info.nFileIndexHigh) << 32) | info.nFileIndexLow; + + CloseHandle (h); + } + + return result; +} + //============================================================================== bool File::isOnCDRomDrive() const { @@ -482,8 +511,8 @@ bool File::isOnHardDisk() const if (fullPath.toLowerCase()[0] <= 'b' && fullPath[1] == ':') return n != DRIVE_REMOVABLE; - else - return n != DRIVE_CDROM && n != DRIVE_REMOTE; + + return n != DRIVE_CDROM && n != DRIVE_REMOTE; } bool File::isOnRemovableDrive() const @@ -511,9 +540,11 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) case userDesktopDirectory: csidlType = CSIDL_DESKTOP; break; case userApplicationDataDirectory: csidlType = CSIDL_APPDATA; break; case commonApplicationDataDirectory: csidlType = CSIDL_COMMON_APPDATA; break; + case commonDocumentsDirectory: csidlType = CSIDL_COMMON_DOCUMENTS; break; case globalApplicationsDirectory: csidlType = CSIDL_PROGRAM_FILES; break; - case userMusicDirectory: csidlType = 0x0d /*CSIDL_MYMUSIC*/; break; - case userMoviesDirectory: csidlType = 0x0e /*CSIDL_MYVIDEO*/; break; + case userMusicDirectory: csidlType = 0x0d; /*CSIDL_MYMUSIC*/ break; + case userMoviesDirectory: csidlType = 0x0e; /*CSIDL_MYVIDEO*/ break; + case userPicturesDirectory: csidlType = 0x27; /*CSIDL_MYPICTURES*/ break; case tempDirectory: { @@ -533,7 +564,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) default: jassertfalse; // unknown type? - return File::nonexistent; + return File(); } return WindowsFileHelpers::getSpecialFolderPath (csidlType); @@ -581,6 +612,11 @@ String File::getVersion() const } //============================================================================== +bool File::isLink() const +{ + return hasFileExtension (".lnk"); +} + File File::getLinkedTarget() const { File result (*this); @@ -588,30 +624,42 @@ File File::getLinkedTarget() const if (! exists()) p += ".lnk"; - else if (getFileExtension() != ".lnk") + else if (! hasFileExtension (".lnk")) return result; - ComSmartPtr shellLink; - if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink))) - { - ComSmartPtr persistFile; - if (SUCCEEDED (shellLink.QueryInterface (persistFile))) - { - if (SUCCEEDED (persistFile->Load (p.toWideCharPointer(), STGM_READ)) - && SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))) - { - WIN32_FIND_DATA winFindData; - WCHAR resolvedPath [MAX_PATH]; + ComSmartPtr shellLink; + ComSmartPtr persistFile; - if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, SLGP_UNCPRIORITY))) - result = File (resolvedPath); - } - } + if (SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) + && SUCCEEDED (shellLink.QueryInterface (persistFile)) + && SUCCEEDED (persistFile->Load (p.toWideCharPointer(), STGM_READ)) + && SUCCEEDED (shellLink->Resolve (0, SLR_ANY_MATCH | SLR_NO_UI))) + { + WIN32_FIND_DATA winFindData; + WCHAR resolvedPath [MAX_PATH]; + + if (SUCCEEDED (shellLink->GetPath (resolvedPath, MAX_PATH, &winFindData, SLGP_UNCPRIORITY))) + result = File (resolvedPath); } return result; } +bool File::createLink (const String& description, const File& linkFileToCreate) const +{ + linkFileToCreate.deleteFile(); + + ComSmartPtr shellLink; + ComSmartPtr persistFile; + + CoInitialize (0); + + return SUCCEEDED (shellLink.CoCreateInstance (CLSID_ShellLink)) + && SUCCEEDED (shellLink->SetPath (getFullPathName().toWideCharPointer())) + && SUCCEEDED (shellLink->SetDescription (description.toWideCharPointer())) + && SUCCEEDED (shellLink.QueryInterface (persistFile)) + && SUCCEEDED (persistFile->Save (linkFileToCreate.getFullPathName().toWideCharPointer(), TRUE)); +} //============================================================================== class DirectoryIterator::NativeIterator::Pimpl @@ -665,7 +713,7 @@ private: const String directoryWithWildCard; HANDLE handle; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) }; DirectoryIterator::NativeIterator::NativeIterator (const File& directory, const String& wildCard) @@ -686,13 +734,14 @@ bool DirectoryIterator::NativeIterator::next (String& filenameFound, //============================================================================== -bool Process::openDocument (const String& fileName, const String& parameters) +bool JUCE_CALLTYPE Process::openDocument (const String& fileName, const String& parameters) { HINSTANCE hInstance = 0; JUCE_TRY { - hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT); + hInstance = ShellExecute (0, 0, fileName.toWideCharPointer(), + parameters.toWideCharPointer(), 0, SW_SHOWDEFAULT); } JUCE_CATCH_ALL @@ -701,45 +750,42 @@ bool Process::openDocument (const String& fileName, const String& parameters) void File::revealToUser() const { - #if JUCE_MINGW - jassertfalse; // not supported in MinGW.. - #else - #pragma warning (push) - #pragma warning (disable: 4090) // (alignment warning) - ITEMIDLIST* const itemIDList = ILCreateFromPath (fullPath.toWideCharPointer()); - #pragma warning (pop) + DynamicLibrary dll ("Shell32.dll"); + JUCE_LOAD_WINAPI_FUNCTION (dll, ILCreateFromPathW, ilCreateFromPathW, ITEMIDLIST*, (LPCWSTR)) + JUCE_LOAD_WINAPI_FUNCTION (dll, ILFree, ilFree, void, (ITEMIDLIST*)) + JUCE_LOAD_WINAPI_FUNCTION (dll, SHOpenFolderAndSelectItems, shOpenFolderAndSelectItems, HRESULT, (ITEMIDLIST*, UINT, void*, DWORD)) - if (itemIDList != nullptr) + if (ilCreateFromPathW != nullptr && shOpenFolderAndSelectItems != nullptr && ilFree != nullptr) { - SHOpenFolderAndSelectItems (itemIDList, 0, nullptr, 0); - ILFree (itemIDList); + if (ITEMIDLIST* const itemIDList = ilCreateFromPathW (fullPath.toWideCharPointer())) + { + shOpenFolderAndSelectItems (itemIDList, 0, nullptr, 0); + ilFree (itemIDList); + } } - #endif } //============================================================================== class NamedPipe::Pimpl { public: - Pimpl (const String& file, const bool isPipe_) - : pipeH (0), + Pimpl (const String& pipeName, const bool createPipe) + : filename ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName)), + pipeH (INVALID_HANDLE_VALUE), cancelEvent (CreateEvent (0, FALSE, FALSE, 0)), - connected (false), - isPipe (isPipe_) + connected (false), ownsPipe (createPipe), shouldStop (false) { - pipeH = isPipe ? CreateNamedPipe (file.toWideCharPointer(), - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, - PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0) - : CreateFile (file.toWideCharPointer(), - GENERIC_READ | GENERIC_WRITE, 0, 0, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + if (createPipe) + pipeH = CreateNamedPipe (filename.toWideCharPointer(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, + PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, 0); } ~Pimpl() { disconnectPipe(); - if (pipeH != 0) + if (pipeH != INVALID_HANDLE_VALUE) CloseHandle (pipeH); CloseHandle (cancelEvent); @@ -747,37 +793,48 @@ public: bool connect (const int timeOutMs) { - if (! isPipe) - return true; + if (! ownsPipe) + { + if (pipeH != INVALID_HANDLE_VALUE) + return true; + + const Time timeOutEnd (Time::getCurrentTime() + RelativeTime::milliseconds (timeOutMs)); + + for (;;) + { + { + const ScopedLock sl (createFileLock); + + if (pipeH == INVALID_HANDLE_VALUE) + pipeH = CreateFile (filename.toWideCharPointer(), + GENERIC_READ | GENERIC_WRITE, 0, 0, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); + } + + if (pipeH != INVALID_HANDLE_VALUE) + return true; + + if (shouldStop || (timeOutMs >= 0 && Time::getCurrentTime() > timeOutEnd)) + return false; + + Thread::sleep (1); + } + } if (! connected) { - OVERLAPPED over = { 0 }; - over.hEvent = CreateEvent (0, TRUE, FALSE, 0); + OverlappedEvent over; - if (ConnectNamedPipe (pipeH, &over)) + if (ConnectNamedPipe (pipeH, &over.over) == 0) { - connected = false; // yes, you read that right. In overlapped mode it should always return 0. - } - else - { - const DWORD err = GetLastError(); - - if (err == ERROR_IO_PENDING || err == ERROR_PIPE_LISTENING) + switch (GetLastError()) { - HANDLE handles[] = { over.hEvent, cancelEvent }; - - if (WaitForMultipleObjects (2, handles, FALSE, - timeOutMs >= 0 ? timeOutMs : INFINITE) == WAIT_OBJECT_0) - connected = true; - } - else if (err == ERROR_PIPE_CONNECTED) - { - connected = true; + case ERROR_PIPE_CONNECTED: connected = true; break; + case ERROR_IO_PENDING: + case ERROR_PIPE_LISTENING: connected = waitForIO (over, timeOutMs); break; + default: break; } } - - CloseHandle (over.hEvent); } return connected; @@ -785,174 +842,150 @@ public: void disconnectPipe() { - if (connected) + if (ownsPipe && connected) { DisconnectNamedPipe (pipeH); connected = false; } } - bool isConnected() const noexcept { return connected; } - - HANDLE pipeH, cancelEvent; - bool connected, isPipe; -}; - -NamedPipe::NamedPipe() -{ -} - -NamedPipe::~NamedPipe() -{ - close(); -} - -bool NamedPipe::isOpen() const -{ - return pimpl != nullptr && pimpl->connected; -} - -void NamedPipe::cancelPendingReads() -{ - if (pimpl != nullptr) - SetEvent (pimpl->cancelEvent); -} - -void NamedPipe::close() -{ - cancelPendingReads(); - - const ScopedLock sl (lock); - ScopedPointer deleter (pimpl); // (clears the pimpl member variable before deleting it) -} - -bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) -{ - close(); - - pimpl = new Pimpl ("\\\\.\\pipe\\" + File::createLegalFileName (pipeName), createPipe); - - if (pimpl->pipeH != INVALID_HANDLE_VALUE) - return true; - - pimpl = nullptr; - return false; -} - -int NamedPipe::read (void* destBuffer, const int maxBytesToRead, const int timeOutMilliseconds) -{ - const ScopedLock sl (lock); - int bytesRead = -1; - bool waitAgain = true; - - while (waitAgain && pimpl != nullptr) + int read (void* destBuffer, const int maxBytesToRead, const int timeOutMilliseconds) { - waitAgain = false; - - if (! pimpl->connect (timeOutMilliseconds)) - break; - - if (maxBytesToRead <= 0) - return 0; - - OVERLAPPED over = { 0 }; - over.hEvent = CreateEvent (0, TRUE, FALSE, 0); - - unsigned long numRead; - - if (ReadFile (pimpl->pipeH, destBuffer, (DWORD) maxBytesToRead, &numRead, &over)) - { - bytesRead = (int) numRead; - } - else + while (connect (timeOutMilliseconds)) { + if (maxBytesToRead <= 0) + return 0; + + OverlappedEvent over; + unsigned long numRead; + + if (ReadFile (pipeH, destBuffer, (DWORD) maxBytesToRead, &numRead, &over.over)) + return (int) numRead; + const DWORD lastError = GetLastError(); if (lastError == ERROR_IO_PENDING) { - HANDLE handles[] = { over.hEvent, pimpl->cancelEvent }; - DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE, - timeOutMilliseconds >= 0 ? timeOutMilliseconds - : INFINITE); - if (waitResult != WAIT_OBJECT_0) - { - // if the operation timed out, let's cancel it... - CancelIo (pimpl->pipeH); - WaitForSingleObject (over.hEvent, INFINITE); // makes sure cancel is complete - } + if (! waitForIO (over, timeOutMilliseconds)) + return -1; - if (GetOverlappedResult (pimpl->pipeH, &over, &numRead, FALSE)) - { - bytesRead = (int) numRead; - } - else if ((GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_PIPE_NOT_CONNECTED) && pimpl->isPipe) - { - pimpl->disconnectPipe(); - waitAgain = true; - } + if (GetOverlappedResult (pipeH, &over.over, &numRead, FALSE)) + return (int) numRead; } - else if (pimpl->isPipe) - { - waitAgain = true; - if (lastError == ERROR_BROKEN_PIPE || lastError == ERROR_PIPE_NOT_CONNECTED) - pimpl->disconnectPipe(); - else - Sleep (5); + if (ownsPipe && (GetLastError() == ERROR_BROKEN_PIPE || GetLastError() == ERROR_PIPE_NOT_CONNECTED)) + disconnectPipe(); + else + break; + } + + return -1; + } + + int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) + { + if (connect (timeOutMilliseconds)) + { + if (numBytesToWrite <= 0) + return 0; + + OverlappedEvent over; + unsigned long numWritten; + + if (WriteFile (pipeH, sourceBuffer, (DWORD) numBytesToWrite, &numWritten, &over.over)) + return (int) numWritten; + + if (GetLastError() == ERROR_IO_PENDING) + { + if (! waitForIO (over, timeOutMilliseconds)) + return -1; + + if (GetOverlappedResult (pipeH, &over.over, &numWritten, FALSE)) + return (int) numWritten; + + if (GetLastError() == ERROR_BROKEN_PIPE && ownsPipe) + disconnectPipe(); } } - CloseHandle (over.hEvent); + return -1; } - return bytesRead; + const String filename; + HANDLE pipeH, cancelEvent; + bool connected, ownsPipe, shouldStop; + CriticalSection createFileLock; + +private: + struct OverlappedEvent + { + OverlappedEvent() + { + zerostruct (over); + over.hEvent = CreateEvent (0, TRUE, FALSE, 0); + } + + ~OverlappedEvent() + { + CloseHandle (over.hEvent); + } + + OVERLAPPED over; + }; + + bool waitForIO (OverlappedEvent& over, int timeOutMilliseconds) + { + if (shouldStop) + return false; + + HANDLE handles[] = { over.over.hEvent, cancelEvent }; + DWORD waitResult = WaitForMultipleObjects (2, handles, FALSE, + timeOutMilliseconds >= 0 ? timeOutMilliseconds + : INFINITE); + + if (waitResult == WAIT_OBJECT_0) + return true; + + CancelIo (pipeH); + return false; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) +}; + +void NamedPipe::close() +{ + if (pimpl != nullptr) + { + pimpl->shouldStop = true; + SetEvent (pimpl->cancelEvent); + + ScopedWriteLock sl (lock); + pimpl = nullptr; + } +} + +bool NamedPipe::openInternal (const String& pipeName, const bool createPipe) +{ + pimpl = new Pimpl (pipeName, createPipe); + + if (createPipe && pimpl->pipeH == INVALID_HANDLE_VALUE) + { + pimpl = nullptr; + return false; + } + + return true; +} + +int NamedPipe::read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds) +{ + ScopedReadLock sl (lock); + return pimpl != nullptr ? pimpl->read (destBuffer, maxBytesToRead, timeOutMilliseconds) : -1; } int NamedPipe::write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds) { - int bytesWritten = -1; - - if (pimpl != nullptr && pimpl->connect (timeOutMilliseconds)) - { - if (numBytesToWrite <= 0) - return 0; - - OVERLAPPED over = { 0 }; - over.hEvent = CreateEvent (0, TRUE, FALSE, 0); - - unsigned long numWritten; - - if (WriteFile (pimpl->pipeH, sourceBuffer, (DWORD) numBytesToWrite, &numWritten, &over)) - { - bytesWritten = (int) numWritten; - } - else if (GetLastError() == ERROR_IO_PENDING) - { - HANDLE handles[] = { over.hEvent, pimpl->cancelEvent }; - DWORD waitResult; - - waitResult = WaitForMultipleObjects (2, handles, FALSE, - timeOutMilliseconds >= 0 ? timeOutMilliseconds - : INFINITE); - - if (waitResult != WAIT_OBJECT_0) - { - CancelIo (pimpl->pipeH); - WaitForSingleObject (over.hEvent, INFINITE); - } - - if (GetOverlappedResult (pimpl->pipeH, &over, &numWritten, FALSE)) - { - bytesWritten = (int) numWritten; - } - else if (GetLastError() == ERROR_BROKEN_PIPE && pimpl->isPipe) - { - pimpl->disconnectPipe(); - } - } - - CloseHandle (over.hEvent); - } - - return bytesWritten; + ScopedReadLock sl (lock); + return pimpl != nullptr ? pimpl->write (sourceBuffer, numBytesToWrite, timeOutMilliseconds) : -1; } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp index 8b3e4ad..e7af2c1 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Network.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -31,48 +34,6 @@ #define INTERNET_OPTION_DISABLE_AUTODIAL 70 #endif -//============================================================================== -#ifndef WORKAROUND_TIMEOUT_BUG - //#define WORKAROUND_TIMEOUT_BUG 1 -#endif - -#if WORKAROUND_TIMEOUT_BUG -// Required because of a Microsoft bug in setting a timeout -class InternetConnectThread : public Thread -{ -public: - InternetConnectThread (URL_COMPONENTS& uc_, HINTERNET sessionHandle_, HINTERNET& connection_, const bool isFtp_) - : Thread ("Internet"), uc (uc_), sessionHandle (sessionHandle_), connection (connection_), isFtp (isFtp_) - { - startThread(); - } - - ~InternetConnectThread() - { - stopThread (60000); - } - - void run() - { - connection = InternetConnect (sessionHandle, uc.lpszHostName, - uc.nPort, _T(""), _T(""), - isFtp ? INTERNET_SERVICE_FTP - : INTERNET_SERVICE_HTTP, - 0, 0); - notify(); - } - -private: - URL_COMPONENTS& uc; - HINTERNET sessionHandle; - HINTERNET& connection; - const bool isFtp; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InternetConnectThread); -}; -#endif - - //============================================================================== class WebInputStream : public InputStream { @@ -80,42 +41,50 @@ public: WebInputStream (const String& address_, bool isPost_, const MemoryBlock& postData_, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers_, int timeOutMs_, StringPairArray* responseHeaders) - : connection (0), request (0), + : statusCode (0), connection (0), request (0), address (address_), headers (headers_), postData (postData_), position (0), finished (false), isPost (isPost_), timeOutMs (timeOutMs_) { createConnection (progressCallback, progressCallbackContext); - if (responseHeaders != nullptr && ! isError()) + if (! isError()) { - DWORD bufferSizeBytes = 4096; - - for (;;) + if (responseHeaders != nullptr) { - HeapBlock buffer ((size_t) bufferSizeBytes); + DWORD bufferSizeBytes = 4096; - if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) + for (;;) { - StringArray headersArray; - headersArray.addLines (reinterpret_cast (buffer.getData())); + HeapBlock buffer ((size_t) bufferSizeBytes); - for (int i = 0; i < headersArray.size(); ++i) + if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) { - const String& header = headersArray[i]; - const String key (header.upToFirstOccurrenceOf (": ", false, false)); - const String value (header.fromFirstOccurrenceOf (": ", false, false)); - const String previousValue ((*responseHeaders) [key]); + StringArray headersArray; + headersArray.addLines (String (reinterpret_cast (buffer.getData()))); - responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + for (int i = 0; i < headersArray.size(); ++i) + { + const String& header = headersArray[i]; + const String key (header.upToFirstOccurrenceOf (": ", false, false)); + const String value (header.fromFirstOccurrenceOf (": ", false, false)); + const String previousValue ((*responseHeaders) [key]); + + responseHeaders->set (key, previousValue.isEmpty() ? value : (previousValue + "," + value)); + } + + break; } - break; + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + break; } - - if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) - break; } + DWORD status = 0; + DWORD statusSize = sizeof (status); + + if (HttpQueryInfo (request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &statusSize, 0)) + statusCode = (int) status; } } @@ -185,6 +154,8 @@ public: return true; } + int statusCode; + private: //============================================================================== HINTERNET connection, request; @@ -255,31 +226,19 @@ private: else if (timeOutMs < 0) timeOutMs = -1; - InternetSetOption (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT, &timeOutMs, sizeof (timeOutMs)); + applyTimeout (sessionHandle, INTERNET_OPTION_CONNECT_TIMEOUT); + applyTimeout (sessionHandle, INTERNET_OPTION_RECEIVE_TIMEOUT); + applyTimeout (sessionHandle, INTERNET_OPTION_SEND_TIMEOUT); + applyTimeout (sessionHandle, INTERNET_OPTION_DATA_RECEIVE_TIMEOUT); + applyTimeout (sessionHandle, INTERNET_OPTION_DATA_SEND_TIMEOUT); const bool isFtp = address.startsWithIgnoreCase ("ftp:"); - #if WORKAROUND_TIMEOUT_BUG - connection = 0; - - { - InternetConnectThread connectThread (uc, sessionHandle, connection, isFtp); - connectThread.wait (timeOutMs); - - if (connection == 0) - { - InternetCloseHandle (sessionHandle); - sessionHandle = 0; - } - } - #else connection = InternetConnect (sessionHandle, uc.lpszHostName, uc.nPort, uc.lpszUserName, uc.lpszPassword, isFtp ? (DWORD) INTERNET_SERVICE_FTP : (DWORD) INTERNET_SERVICE_HTTP, 0, 0); - #endif - if (connection != 0) { if (isFtp) @@ -290,10 +249,15 @@ private: } } + void applyTimeout (HINTERNET sessionHandle, const DWORD option) + { + InternetSetOption (sessionHandle, option, &timeOutMs, sizeof (timeOutMs)); + } + void openHTTPConnection (URL_COMPONENTS& uc, URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { - const TCHAR* mimeTypes[] = { _T("*/*"), 0 }; + const TCHAR* mimeTypes[] = { _T("*/*"), nullptr }; DWORD flags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_COOKIES; @@ -323,7 +287,7 @@ private: if (bytesToDo > 0 && ! InternetWriteFile (request, - static_cast (postData.getData()) + bytesSent, + static_cast (postData.getData()) + bytesSent, (DWORD) bytesToDo, &bytesDone)) { break; @@ -349,50 +313,57 @@ private: close(); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) }; -InputStream* URL::createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, - const String& headers, const int timeOutMs, StringPairArray* responseHeaders) -{ - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); - - return wi->isError() ? nullptr : wi.release(); -} - //============================================================================== -namespace MACAddressHelpers +struct GetAdaptersInfoHelper { - void getViaGetAdaptersInfo (Array& result) + bool callGetAdaptersInfo() { DynamicLibrary dll ("iphlpapi.dll"); - JUCE_DLL_FUNCTION (GetAdaptersInfo, getAdaptersInfo, DWORD, dll, (PIP_ADAPTER_INFO, PULONG)) + JUCE_LOAD_WINAPI_FUNCTION (dll, GetAdaptersInfo, getAdaptersInfo, DWORD, (PIP_ADAPTER_INFO, PULONG)) - if (getAdaptersInfo != nullptr) + if (getAdaptersInfo == nullptr) + return false; + + adapterInfo.malloc (1); + ULONG len = sizeof (IP_ADAPTER_INFO); + + if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW) + adapterInfo.malloc (len, 1); + + return getAdaptersInfo (adapterInfo, &len) == NO_ERROR; + } + + HeapBlock adapterInfo; +}; + +namespace MACAddressHelpers +{ + static void addAddress (Array& result, const MACAddress& ma) + { + if (! ma.isNull()) + result.addIfNotAlreadyThere (ma); + } + + static void getViaGetAdaptersInfo (Array& result) + { + GetAdaptersInfoHelper gah; + + if (gah.callGetAdaptersInfo()) { - ULONG len = sizeof (IP_ADAPTER_INFO); - HeapBlock adapterInfo (1); - - if (getAdaptersInfo (adapterInfo, &len) == ERROR_BUFFER_OVERFLOW) - adapterInfo.malloc (len, 1); - - if (getAdaptersInfo (adapterInfo, &len) == NO_ERROR) - { - for (PIP_ADAPTER_INFO adapter = adapterInfo; adapter != nullptr; adapter = adapter->Next) - if (adapter->AddressLength >= 6) - result.addIfNotAlreadyThere (MACAddress (adapter->Address)); - } + for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) + if (adapter->AddressLength >= 6) + addAddress (result, MACAddress (adapter->Address)); } } - void getViaNetBios (Array& result) + static void getViaNetBios (Array& result) { DynamicLibrary dll ("netapi32.dll"); - JUCE_DLL_FUNCTION (Netbios, NetbiosCall, UCHAR, dll, (PNCB)) + JUCE_LOAD_WINAPI_FUNCTION (dll, Netbios, NetbiosCall, UCHAR, (PNCB)) if (NetbiosCall != 0) { @@ -425,12 +396,13 @@ namespace MACAddressHelpers NAME_BUFFER NameBuff [30]; }; - ASTAT astat = { 0 }; + ASTAT astat; + zerostruct (astat); ncb.ncb_buffer = (unsigned char*) &astat; ncb.ncb_length = sizeof (ASTAT); if (NetbiosCall (&ncb) == 0 && astat.adapt.adapter_type == 0xfe) - result.addIfNotAlreadyThere (MACAddress (astat.adapt.adapter_address)); + addAddress (result, MACAddress (astat.adapt.adapter_address)); } } } @@ -443,30 +415,47 @@ void MACAddress::findAllAddresses (Array& result) MACAddressHelpers::getViaNetBios (result); } -//============================================================================== -bool Process::openEmailWithAttachments (const String& targetEmailAddress, - const String& emailSubject, - const String& bodyText, - const StringArray& filesToAttach) +void IPAddress::findAllAddresses (Array& result) { - typedef ULONG (WINAPI *MAPISendMailType) (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG); + result.addIfNotAlreadyThere (IPAddress::local()); - DynamicLibrary mapiLib ("MAPI32.dll"); - MAPISendMailType mapiSendMail = (MAPISendMailType) mapiLib.getFunction ("MAPISendMail"); + GetAdaptersInfoHelper gah; + + if (gah.callGetAdaptersInfo()) + { + for (PIP_ADAPTER_INFO adapter = gah.adapterInfo; adapter != nullptr; adapter = adapter->Next) + { + IPAddress ip (adapter->IpAddressList.IpAddress.String); + + if (ip != IPAddress::any()) + result.addIfNotAlreadyThere (ip); + } + } +} + +//============================================================================== +bool JUCE_CALLTYPE Process::openEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach) +{ + DynamicLibrary dll ("MAPI32.dll"); + JUCE_LOAD_WINAPI_FUNCTION (dll, MAPISendMail, mapiSendMail, + ULONG, (LHANDLE, ULONG, lpMapiMessage, ::FLAGS, ULONG)) if (mapiSendMail == nullptr) return false; MapiMessage message = { 0 }; - message.lpszSubject = (LPSTR) emailSubject.toUTF8().getAddress(); - message.lpszNoteText = (LPSTR) bodyText.toUTF8().getAddress(); + message.lpszSubject = (LPSTR) emailSubject.toRawUTF8(); + message.lpszNoteText = (LPSTR) bodyText.toRawUTF8(); MapiRecipDesc recip = { 0 }; recip.ulRecipClass = MAPI_TO; String targetEmailAddress_ (targetEmailAddress); if (targetEmailAddress_.isEmpty()) targetEmailAddress_ = " "; // (Windows Mail can't deal with a blank address) - recip.lpszName = (LPSTR) targetEmailAddress_.toUTF8().getAddress(); + recip.lpszName = (LPSTR) targetEmailAddress_.toRawUTF8(); message.nRecipCount = 1; message.lpRecips = &recip; @@ -479,7 +468,7 @@ bool Process::openEmailWithAttachments (const String& targetEmailAddress, for (int i = 0; i < filesToAttach.size(); ++i) { files[i].nPosition = (ULONG) -1; - files[i].lpszPathName = (LPSTR) filesToAttach[i].toUTF8().getAddress(); + files[i].lpszPathName = (LPSTR) filesToAttach[i].toRawUTF8(); } return mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp index d71f1c7..8119474 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Registry.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -61,9 +64,9 @@ struct RegistryKeyWrapper } static bool setValue (const String& regValuePath, const DWORD type, - const void* data, size_t dataSize) + const void* data, size_t dataSize, const DWORD wow64Flags) { - const RegistryKeyWrapper key (regValuePath, true, 0); + const RegistryKeyWrapper key (regValuePath, true, wow64Flags); return key.key != 0 && RegSetValueEx (key.key, key.wideCharValueName, 0, type, @@ -112,97 +115,113 @@ struct RegistryKeyWrapper return defaultValue; } + static bool keyExists (const String& regValuePath, const DWORD wow64Flags) + { + return RegistryKeyWrapper (regValuePath, false, wow64Flags).key != 0; + } + + static bool valueExists (const String& regValuePath, const DWORD wow64Flags) + { + const RegistryKeyWrapper key (regValuePath, false, wow64Flags); + + if (key.key == 0) + return false; + + unsigned char buffer [512]; + unsigned long bufferSize = sizeof (buffer); + DWORD type = 0; + + const LONG result = RegQueryValueEx (key.key, key.wideCharValueName, + 0, &type, buffer, &bufferSize); + + return result == ERROR_SUCCESS || result == ERROR_MORE_DATA; + } + HKEY key; const wchar_t* wideCharValueName; String valueName; - JUCE_DECLARE_NON_COPYABLE (RegistryKeyWrapper); + JUCE_DECLARE_NON_COPYABLE (RegistryKeyWrapper) }; -uint32 WindowsRegistry::getBinaryValue (const String& regValuePath, MemoryBlock& result) +uint32 JUCE_CALLTYPE WindowsRegistry::getBinaryValue (const String& regValuePath, MemoryBlock& result, WoW64Mode mode) { - return RegistryKeyWrapper::getBinaryValue (regValuePath, result, 0); + return RegistryKeyWrapper::getBinaryValue (regValuePath, result, (DWORD) mode); } -String WindowsRegistry::getValue (const String& regValuePath, const String& defaultValue) +String JUCE_CALLTYPE WindowsRegistry::getValue (const String& regValuePath, const String& defaultValue, WoW64Mode mode) { - return RegistryKeyWrapper::getValue (regValuePath, defaultValue, 0); + return RegistryKeyWrapper::getValue (regValuePath, defaultValue, (DWORD) mode); } -String WindowsRegistry::getValueWow64 (const String& regValuePath, const String& defaultValue) -{ - return RegistryKeyWrapper::getValue (regValuePath, defaultValue, KEY_WOW64_64KEY); -} - -bool WindowsRegistry::setValue (const String& regValuePath, const String& value) +bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const String& value, WoW64Mode mode) { return RegistryKeyWrapper::setValue (regValuePath, REG_SZ, value.toWideCharPointer(), - CharPointer_UTF16::getBytesRequiredFor (value.getCharPointer())); + CharPointer_UTF16::getBytesRequiredFor (value.getCharPointer()), mode); } -bool WindowsRegistry::setValue (const String& regValuePath, const uint32 value) +bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint32 value, WoW64Mode mode) { - return RegistryKeyWrapper::setValue (regValuePath, REG_DWORD, &value, sizeof (value)); + return RegistryKeyWrapper::setValue (regValuePath, REG_DWORD, &value, sizeof (value), (DWORD) mode); } -bool WindowsRegistry::setValue (const String& regValuePath, const uint64 value) +bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const uint64 value, WoW64Mode mode) { - return RegistryKeyWrapper::setValue (regValuePath, REG_QWORD, &value, sizeof (value)); + return RegistryKeyWrapper::setValue (regValuePath, REG_QWORD, &value, sizeof (value), (DWORD) mode); } -bool WindowsRegistry::setValue (const String& regValuePath, const MemoryBlock& value) +bool JUCE_CALLTYPE WindowsRegistry::setValue (const String& regValuePath, const MemoryBlock& value, WoW64Mode mode) { - return RegistryKeyWrapper::setValue (regValuePath, REG_BINARY, value.getData(), value.getSize()); + return RegistryKeyWrapper::setValue (regValuePath, REG_BINARY, value.getData(), value.getSize(), (DWORD) mode); } -bool WindowsRegistry::valueExists (const String& regValuePath) +bool JUCE_CALLTYPE WindowsRegistry::valueExists (const String& regValuePath, WoW64Mode mode) { - const RegistryKeyWrapper key (regValuePath, false, 0); - - if (key.key == 0) - return false; - - unsigned char buffer [512]; - unsigned long bufferSize = sizeof (buffer); - DWORD type = 0; - - const LONG result = RegQueryValueEx (key.key, key.wideCharValueName, - 0, &type, buffer, &bufferSize); - - return result == ERROR_SUCCESS || result == ERROR_MORE_DATA; + return RegistryKeyWrapper::valueExists (regValuePath, (DWORD) mode); } -void WindowsRegistry::deleteValue (const String& regValuePath) +bool JUCE_CALLTYPE WindowsRegistry::keyExists (const String& regValuePath, WoW64Mode mode) { - const RegistryKeyWrapper key (regValuePath, true, 0); + return RegistryKeyWrapper::keyExists (regValuePath, (DWORD) mode); +} + +void JUCE_CALLTYPE WindowsRegistry::deleteValue (const String& regValuePath, WoW64Mode mode) +{ + const RegistryKeyWrapper key (regValuePath, true, (DWORD) mode); if (key.key != 0) RegDeleteValue (key.key, key.wideCharValueName); } -void WindowsRegistry::deleteKey (const String& regKeyPath) +void JUCE_CALLTYPE WindowsRegistry::deleteKey (const String& regKeyPath, WoW64Mode mode) { - const RegistryKeyWrapper key (regKeyPath, true, 0); + const RegistryKeyWrapper key (regKeyPath, true, (DWORD) mode); if (key.key != 0) RegDeleteKey (key.key, key.wideCharValueName); } -bool WindowsRegistry::registerFileAssociation (const String& fileExtension, - const String& symbolicDescription, - const String& fullDescription, - const File& targetExecutable, - const int iconResourceNumber, - const bool registerForCurrentUserOnly) +bool JUCE_CALLTYPE WindowsRegistry::registerFileAssociation (const String& fileExtension, + const String& symbolicDescription, + const String& fullDescription, + const File& targetExecutable, + const int iconResourceNumber, + const bool registerForCurrentUserOnly, + WoW64Mode mode) { const char* const root = registerForCurrentUserOnly ? "HKEY_CURRENT_USER\\Software\\Classes\\" : "HKEY_CLASSES_ROOT\\"; const String key (root + symbolicDescription); - return setValue (root + fileExtension + "\\", symbolicDescription) - && setValue (key + "\\", fullDescription) - && setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"") + return setValue (root + fileExtension + "\\", symbolicDescription, mode) + && setValue (key + "\\", fullDescription, mode) + && setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"", mode) && (iconResourceNumber == 0 || setValue (key + "\\DefaultIcon\\", - targetExecutable.getFullPathName() + "," + String (-iconResourceNumber))); + targetExecutable.getFullPathName() + "," + String (iconResourceNumber))); } + +// These methods are deprecated: +String WindowsRegistry::getValueWow64 (const String& p, const String& defVal) { return getValue (p, defVal, WoW64_64bit); } +bool WindowsRegistry::valueExistsWow64 (const String& p) { return valueExists (p, WoW64_64bit); } +bool WindowsRegistry::keyExistsWow64 (const String& p) { return keyExists (p, WoW64_64bit); } diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp index 5b21f63..86f1051 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_SystemStats.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -29,7 +32,7 @@ void Logger::outputDebugString (const String& text) } //============================================================================== -#ifdef JUCE_DLL +#ifdef JUCE_DLL_BUILD JUCE_API void* juceDLL_malloc (size_t sz) { return std::malloc (sz); } JUCE_API void juceDLL_free (void* block) { std::free (block); } #endif @@ -42,10 +45,46 @@ void Logger::outputDebugString (const String& text) #pragma intrinsic (__cpuid) #pragma intrinsic (__rdtsc) +static void callCPUID (int result[4], int infoType) +{ + __cpuid (result, infoType); +} + +#else + +static void callCPUID (int result[4], int infoType) +{ + #if ! JUCE_MINGW + __try + #endif + { + #if JUCE_GCC + __asm__ __volatile__ ("cpuid" : "=a" (result[0]), "=b" (result[1]), "=c" (result[2]),"=d" (result[3]) : "a" (infoType)); + #else + __asm + { + mov esi, result + mov eax, infoType + xor ecx, ecx + cpuid + mov dword ptr [esi + 0], eax + mov dword ptr [esi + 4], ebx + mov dword ptr [esi + 8], ecx + mov dword ptr [esi + 12], edx + } + #endif + } + #if ! JUCE_MINGW + __except (EXCEPTION_EXECUTE_HANDLER) {} + #endif +} + +#endif + String SystemStats::getCpuVendor() { - int info [4]; - __cpuid (info, 0); + int info[4] = { 0 }; + callCPUID (info, 0); char v [12]; memcpy (v, info + 1, 4); @@ -55,63 +94,18 @@ String SystemStats::getCpuVendor() return String (v, 12); } -#else - //============================================================================== -// CPU info functions using old fashioned inline asm... - -static void juce_getCpuVendor (char* const v) +void CPUInformation::initialise() noexcept { - int vendor[4] = { 0 }; + int info[4] = { 0 }; + callCPUID (info, 1); - #if ! JUCE_MINGW - __try - #endif - { - #if JUCE_GCC - unsigned int dummy = 0; - __asm__ ("cpuid" : "=a" (dummy), "=b" (vendor[0]), "=c" (vendor[2]),"=d" (vendor[1]) : "a" (0)); - #else - __asm - { - mov eax, 0 - cpuid - mov [vendor], ebx - mov [vendor + 4], edx - mov [vendor + 8], ecx - } - #endif - } - #if ! JUCE_MINGW - __except (EXCEPTION_EXECUTE_HANDLER) - { - *v = 0; - } - #endif - - memcpy (v, vendor, 16); -} - -String SystemStats::getCpuVendor() -{ - char v [16]; - juce_getCpuVendor (v); - return String (v, 16); -} -#endif - - -//============================================================================== -SystemStats::CPUFlags::CPUFlags() -{ - hasMMX = IsProcessorFeaturePresent (PF_MMX_INSTRUCTIONS_AVAILABLE) != 0; - hasSSE = IsProcessorFeaturePresent (PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0; - hasSSE2 = IsProcessorFeaturePresent (PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0; - #ifdef PF_AMD3D_INSTRUCTIONS_AVAILABLE - has3DNow = IsProcessorFeaturePresent (PF_AMD3D_INSTRUCTIONS_AVAILABLE) != 0; - #else - has3DNow = IsProcessorFeaturePresent (PF_3DNOW_INSTRUCTIONS_AVAILABLE) != 0; - #endif + // NB: IsProcessorFeaturePresent doesn't work on XP + hasMMX = (info[3] & (1 << 23)) != 0; + hasSSE = (info[3] & (1 << 25)) != 0; + hasSSE2 = (info[3] & (1 << 26)) != 0; + hasSSE3 = (info[2] & (1 << 0)) != 0; + has3DNow = (info[1] & (1 << 31)) != 0; SYSTEM_INFO systemInfo; GetNativeSystemInfo (&systemInfo); @@ -131,27 +125,53 @@ static DebugFlagsInitialiser debugFlagsInitialiser; #endif //============================================================================== -SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() +static bool isWindowsVersionOrLater (SystemStats::OperatingSystemType target) { - OSVERSIONINFO info; - info.dwOSVersionInfoSize = sizeof (info); - GetVersionEx (&info); + OSVERSIONINFOEX info; + zerostruct (info); + info.dwOSVersionInfoSize = sizeof (OSVERSIONINFOEX); - if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) + if (target >= SystemStats::WinVista) { - switch (info.dwMajorVersion) + info.dwMajorVersion = 6; + + switch (target) { - case 5: return (info.dwMinorVersion == 0) ? Win2000 : WinXP; - case 6: return (info.dwMinorVersion == 0) ? WinVista : Windows7; - default: jassertfalse; break; // !! not a supported OS! + case SystemStats::WinVista: info.dwMinorVersion = 0; break; + case SystemStats::Windows7: info.dwMinorVersion = 1; break; + case SystemStats::Windows8: info.dwMinorVersion = 2; break; + default: jassertfalse; break; } } - else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + else { - jassert (info.dwMinorVersion != 0); // !! still running on Windows 95?? - return Win98; + info.dwMajorVersion = 5; + info.dwMinorVersion = target >= SystemStats::WinXP ? 1 : 0; } + DWORDLONG mask = 0; + + VER_SET_CONDITION (mask, VER_MAJORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION (mask, VER_MINORVERSION, VER_GREATER_EQUAL); + VER_SET_CONDITION (mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + VER_SET_CONDITION (mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); + + return VerifyVersionInfo (&info, + VER_MAJORVERSION | VER_MINORVERSION + | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, + mask) != FALSE; +} + +SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() +{ + const SystemStats::OperatingSystemType types[] + = { Windows8, Windows7, WinVista, WinXP, Win2000 }; + + for (int i = 0; i < numElementsInArray (types); ++i) + if (isWindowsVersionOrLater (types[i])) + return types[i]; + + jassertfalse; // need to support whatever new version is running! return UnknownOS; } @@ -162,30 +182,36 @@ String SystemStats::getOperatingSystemName() switch (getOperatingSystemType()) { case Windows7: name = "Windows 7"; break; + case Windows8: name = "Windows 8"; break; case WinVista: name = "Windows Vista"; break; case WinXP: name = "Windows XP"; break; case Win2000: name = "Windows 2000"; break; - case Win98: name = "Windows 98"; break; default: jassertfalse; break; // !! new type of OS? } return name; } +String SystemStats::getDeviceDescription() +{ + return String::empty; +} + bool SystemStats::isOperatingSystem64Bit() { - #ifdef _WIN64 + #if JUCE_64BIT return true; #else typedef BOOL (WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); - LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (_T("kernel32")), "IsWow64Process"); + LPFN_ISWOW64PROCESS fnIsWow64Process + = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandleA ("kernel32"), "IsWow64Process"); BOOL isWow64 = FALSE; - return fnIsWow64Process != 0 + return fnIsWow64Process != nullptr && fnIsWow64Process (GetCurrentProcess(), &isWow64) - && (isWow64 != FALSE); + && isWow64 != FALSE; #endif } @@ -198,6 +224,20 @@ int SystemStats::getMemorySizeInMegabytes() return (int) (mem.ullTotalPhys / (1024 * 1024)) + 1; } +//============================================================================== +String SystemStats::getEnvironmentVariable (const String& name, const String& defaultValue) +{ + DWORD len = GetEnvironmentVariableW (name.toWideCharPointer(), nullptr, 0); + if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) + return String (defaultValue); + + HeapBlock buffer (len); + len = GetEnvironmentVariableW (name.toWideCharPointer(), buffer, len); + + return String (CharPointer_wchar_t (buffer), + CharPointer_wchar_t (buffer + len)); +} + //============================================================================== uint32 juce_millisecondsSinceStartup() noexcept { @@ -370,3 +410,34 @@ String SystemStats::getComputerName() GetComputerName (text, &len); return String (text, len); } + +static String getLocaleValue (LCID locale, LCTYPE key, const char* defaultValue) +{ + TCHAR buffer [256] = { 0 }; + if (GetLocaleInfo (locale, key, buffer, 255) > 0) + return buffer; + + return defaultValue; +} + +String SystemStats::getUserLanguage() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, "en"); } +String SystemStats::getUserRegion() { return getLocaleValue (LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, "US"); } + +String SystemStats::getDisplayLanguage() +{ + DynamicLibrary dll ("kernel32.dll"); + JUCE_LOAD_WINAPI_FUNCTION (dll, GetUserDefaultUILanguage, getUserDefaultUILanguage, LANGID, (void)) + + if (getUserDefaultUILanguage == nullptr) + return "en"; + + const DWORD langID = MAKELCID (getUserDefaultUILanguage(), SORT_DEFAULT); + + String mainLang (getLocaleValue (langID, LOCALE_SISO639LANGNAME, "en")); + String region (getLocaleValue (langID, LOCALE_SISO3166CTRYNAME, nullptr)); + + if (region.isNotEmpty()) + mainLang << '-' << region; + + return mainLang; +} diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp index 9f2b242..4eb24e2 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Threads.cpp @@ -1,30 +1,40 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ HWND juce_messageWindowHandle = 0; // (this is used by other parts of the codebase) +void* getUser32Function (const char* functionName) +{ + HMODULE module = GetModuleHandleA ("user32.dll"); + jassert (module != 0); + return (void*) GetProcAddress (module, functionName); +} + //============================================================================== #if ! JUCE_USE_INTRINSICS // In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in @@ -52,59 +62,33 @@ __int64 juce_InterlockedCompareExchange64 (volatile __int64* value, __int64 newV CriticalSection::CriticalSection() noexcept { // (just to check the MS haven't changed this structure and broken things...) - #if JUCE_VC7_OR_EARLIER + #if JUCE_VC7_OR_EARLIER static_jassert (sizeof (CRITICAL_SECTION) <= 24); - #else - static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (internal)); - #endif + #else + static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (lock)); + #endif - InitializeCriticalSection ((CRITICAL_SECTION*) internal); + InitializeCriticalSection ((CRITICAL_SECTION*) lock); } -CriticalSection::~CriticalSection() noexcept -{ - DeleteCriticalSection ((CRITICAL_SECTION*) internal); -} +CriticalSection::~CriticalSection() noexcept { DeleteCriticalSection ((CRITICAL_SECTION*) lock); } +void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_SECTION*) lock); } +bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) lock) != FALSE; } +void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) lock); } -void CriticalSection::enter() const noexcept -{ - EnterCriticalSection ((CRITICAL_SECTION*) internal); -} - -bool CriticalSection::tryEnter() const noexcept -{ - return TryEnterCriticalSection ((CRITICAL_SECTION*) internal) != FALSE; -} - -void CriticalSection::exit() const noexcept -{ - LeaveCriticalSection ((CRITICAL_SECTION*) internal); -} //============================================================================== WaitableEvent::WaitableEvent (const bool manualReset) noexcept - : internal (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) -{ -} + : handle (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) {} -WaitableEvent::~WaitableEvent() noexcept -{ - CloseHandle (internal); -} +WaitableEvent::~WaitableEvent() noexcept { CloseHandle (handle); } -bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept -{ - return WaitForSingleObject (internal, (DWORD) timeOutMillisecs) == WAIT_OBJECT_0; -} +void WaitableEvent::signal() const noexcept { SetEvent (handle); } +void WaitableEvent::reset() const noexcept { ResetEvent (handle); } -void WaitableEvent::signal() const noexcept +bool WaitableEvent::wait (const int timeOutMs) const noexcept { - SetEvent (internal); -} - -void WaitableEvent::reset() const noexcept -{ - ResetEvent (internal); + return WaitForSingleObject (handle, (DWORD) timeOutMs) == WAIT_OBJECT_0; } //============================================================================== @@ -147,7 +131,7 @@ void Thread::killThread() } } -void Thread::setCurrentThreadName (const String& name) +void JUCE_CALLTYPE Thread::setCurrentThreadName (const String& name) { #if JUCE_DEBUG && JUCE_MSVC struct @@ -174,7 +158,7 @@ void Thread::setCurrentThreadName (const String& name) #endif } -Thread::ThreadID Thread::getCurrentThreadId() +Thread::ThreadID JUCE_CALLTYPE Thread::getCurrentThreadId() { return (ThreadID) (pointer_sized_int) GetCurrentThreadId(); } @@ -196,7 +180,7 @@ bool Thread::setThreadPriority (void* handle, int priority) return SetThreadPriority (handle, pri) != FALSE; } -void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) +void JUCE_CALLTYPE Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) { SetThreadAffinityMask (GetCurrentThread(), affinityMask); } @@ -204,14 +188,19 @@ void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask) //============================================================================== struct SleepEvent { - SleepEvent() - : handle (CreateEvent (0, 0, 0, - #if JUCE_DEBUG - _T("Juce Sleep Event"))) - #else - 0)) - #endif + SleepEvent() noexcept + : handle (CreateEvent (nullptr, FALSE, FALSE, + #if JUCE_DEBUG + _T("JUCE Sleep Event"))) + #else + nullptr)) + #endif + {} + + ~SleepEvent() noexcept { + CloseHandle (handle); + handle = 0; } HANDLE handle; @@ -221,17 +210,15 @@ static SleepEvent sleepEvent; void JUCE_CALLTYPE Thread::sleep (const int millisecs) { - if (millisecs >= 10) - { + jassert (millisecs >= 0); + + if (millisecs >= 10 || sleepEvent.handle == 0) Sleep ((DWORD) millisecs); - } else - { // unlike Sleep() this is guaranteed to return to the current thread after // the time expires, so we'll use this for short waits, which are more likely // to need to be accurate WaitForSingleObject (sleepEvent.handle, (DWORD) millisecs); - } } void Thread::yield() @@ -242,7 +229,7 @@ void Thread::yield() //============================================================================== static int lastProcessPriority = -1; -// called by WindowDriver because Windows does wierd things to process priority +// called when the app gains focus because Windows does weird things to process priority // when you swap apps, and this forces an update when the app is brought to the front. void juce_repeatLastProcessPriority() { @@ -263,7 +250,7 @@ void juce_repeatLastProcessPriority() } } -void Process::setPriority (ProcessPriority prior) +void JUCE_CALLTYPE Process::setPriority (ProcessPriority prior) { if (lastProcessPriority != (int) prior) { @@ -282,52 +269,45 @@ bool JUCE_CALLTYPE Process::isRunningUnderDebugger() return juce_isRunningUnderDebugger(); } -String JUCE_CALLTYPE Process::getCurrentCommandLineParams() -{ - return CharacterFunctions::findEndOfToken (CharPointer_UTF16 (GetCommandLineW()), - CharPointer_UTF16 (L" "), - CharPointer_UTF16 (L"\"")).findEndOfWhitespace(); -} - static void* currentModuleHandle = nullptr; -void* Process::getCurrentModuleInstanceHandle() noexcept +void* JUCE_CALLTYPE Process::getCurrentModuleInstanceHandle() noexcept { if (currentModuleHandle == nullptr) - currentModuleHandle = GetModuleHandle (0); + currentModuleHandle = GetModuleHandleA (nullptr); return currentModuleHandle; } -void Process::setCurrentModuleInstanceHandle (void* const newHandle) noexcept +void JUCE_CALLTYPE Process::setCurrentModuleInstanceHandle (void* const newHandle) noexcept { currentModuleHandle = newHandle; } -void Process::raisePrivilege() +void JUCE_CALLTYPE Process::raisePrivilege() { jassertfalse; // xxx not implemented } -void Process::lowerPrivilege() +void JUCE_CALLTYPE Process::lowerPrivilege() { jassertfalse; // xxx not implemented } -void Process::terminate() +void JUCE_CALLTYPE Process::terminate() { #if JUCE_MSVC && JUCE_CHECK_MEMORY_LEAKS _CrtDumpMemoryLeaks(); #endif // bullet in the head in case there's a problem shutting down.. - ExitProcess (0); + ExitProcess (1); } -bool juce_IsRunningInWine() +bool juce_isRunningInWine() { - HMODULE ntdll = GetModuleHandle (_T("ntdll.dll")); - return ntdll != 0 && GetProcAddress (ntdll, "wine_get_version") != 0; + HMODULE ntdll = GetModuleHandleA ("ntdll"); + return ntdll != 0 && GetProcAddress (ntdll, "wine_get_version") != nullptr; } //============================================================================== @@ -464,7 +444,7 @@ void InterProcessLock::exit() class ChildProcess::ActiveProcess { public: - ActiveProcess (const String& command) + ActiveProcess (const String& command, int streamFlags) : ok (false), readPipe (0), writePipe (0) { SECURITY_ATTRIBUTES securityAtts = { 0 }; @@ -476,8 +456,9 @@ public: { STARTUPINFOW startupInfo = { 0 }; startupInfo.cb = sizeof (startupInfo); - startupInfo.hStdError = writePipe; - startupInfo.hStdOutput = writePipe; + + startupInfo.hStdOutput = (streamFlags | wantStdOut) != 0 ? writePipe : 0; + startupInfo.hStdError = (streamFlags | wantStdErr) != 0 ? writePipe : 0; startupInfo.dwFlags = STARTF_USESTDHANDLES; ok = CreateProcess (nullptr, const_cast (command.toWideCharPointer()), @@ -501,12 +482,12 @@ public: CloseHandle (writePipe); } - bool isRunning() const + bool isRunning() const noexcept { return WaitForSingleObject (processInfo.hProcess, 0) != WAIT_OBJECT_0; } - int read (void* dest, int numNeeded) const + int read (void* dest, int numNeeded) const noexcept { int total = 0; @@ -541,23 +522,30 @@ public: return total; } - bool killProcess() const + bool killProcess() const noexcept { return TerminateProcess (processInfo.hProcess, 0) != FALSE; } + uint32 getExitCode() const noexcept + { + DWORD exitCode = 0; + GetExitCodeProcess (processInfo.hProcess, &exitCode); + return (uint32) exitCode; + } + bool ok; private: HANDLE readPipe, writePipe; PROCESS_INFORMATION processInfo; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ActiveProcess) }; -bool ChildProcess::start (const String& command) +bool ChildProcess::start (const String& command, int streamFlags) { - activeProcess = new ActiveProcess (command); + activeProcess = new ActiveProcess (command, streamFlags); if (! activeProcess->ok) activeProcess = nullptr; @@ -565,17 +553,73 @@ bool ChildProcess::start (const String& command) return activeProcess != nullptr; } -bool ChildProcess::isRunning() const +bool ChildProcess::start (const StringArray& args, int streamFlags) { - return activeProcess != nullptr && activeProcess->isRunning(); + String escaped; + + for (int i = 0; i < args.size(); ++i) + { + String arg (args[i]); + + // If there are spaces, surround it with quotes. If there are quotes, + // replace them with \" so that CommandLineToArgv will correctly parse them. + if (arg.containsAnyOf ("\" ")) + arg = arg.replace ("\"", "\\\"").quoted(); + + escaped << arg << ' '; + } + + return start (escaped.trim(), streamFlags); } -int ChildProcess::readProcessOutput (void* dest, int numBytes) +//============================================================================== +struct HighResolutionTimer::Pimpl { - return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0; -} + Pimpl (HighResolutionTimer& t) noexcept : owner (t), periodMs (0) + { + } -bool ChildProcess::kill() -{ - return activeProcess == nullptr || activeProcess->killProcess(); -} + ~Pimpl() + { + jassert (periodMs == 0); + } + + void start (int newPeriod) + { + if (newPeriod != periodMs) + { + stop(); + periodMs = newPeriod; + + TIMECAPS tc; + if (timeGetDevCaps (&tc, sizeof (tc)) == TIMERR_NOERROR) + { + const int actualPeriod = jlimit ((int) tc.wPeriodMin, (int) tc.wPeriodMax, newPeriod); + + timerID = timeSetEvent (actualPeriod, tc.wPeriodMin, callbackFunction, (DWORD_PTR) this, + TIME_PERIODIC | TIME_CALLBACK_FUNCTION | 0x100 /*TIME_KILL_SYNCHRONOUS*/); + } + } + } + + void stop() + { + periodMs = 0; + timeKillEvent (timerID); + } + + HighResolutionTimer& owner; + int periodMs; + +private: + unsigned int timerID; + + static void __stdcall callbackFunction (UINT, UINT, DWORD_PTR userInfo, DWORD_PTR, DWORD_PTR) + { + if (Pimpl* const timer = reinterpret_cast (userInfo)) + if (timer->periodMs != 0) + timer->owner.hiResTimerCallback(); + } + + JUCE_DECLARE_NON_COPYABLE (Pimpl) +}; diff --git a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp new file mode 100644 index 0000000..6c4d087 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp @@ -0,0 +1,149 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +IPAddress::IPAddress() noexcept +{ + address[0] = 0; address[1] = 0; + address[2] = 0; address[3] = 0; +} + +IPAddress::IPAddress (const uint8 bytes[4]) noexcept +{ + address[0] = bytes[0]; address[1] = bytes[1]; + address[2] = bytes[2]; address[3] = bytes[3]; +} + +IPAddress::IPAddress (uint8 a0, uint8 a1, uint8 a2, uint8 a3) noexcept +{ + address[0] = a0; address[1] = a1; + address[2] = a2; address[3] = a3; +} + +IPAddress::IPAddress (uint32 n) noexcept +{ + address[0] = (n >> 24); + address[1] = (n >> 16) & 255; + address[2] = (n >> 8) & 255; + address[3] = (n & 255); +} + +IPAddress::IPAddress (const String& adr) +{ + StringArray tokens; + tokens.addTokens (adr, ".", String()); + + for (int i = 0; i < 4; ++i) + address[i] = (uint8) tokens[i].getIntValue(); +} + +String IPAddress::toString() const +{ + String s ((int) address[0]); + + for (int i = 1; i < 4; ++i) + s << '.' << (int) address[i]; + + return s; +} + +IPAddress IPAddress::any() noexcept { return IPAddress(); } +IPAddress IPAddress::broadcast() noexcept { return IPAddress (255, 255, 255, 255); } +IPAddress IPAddress::local() noexcept { return IPAddress (127, 0, 0, 1); } + +bool IPAddress::operator== (const IPAddress& other) const noexcept +{ + return address[0] == other.address[0] + && address[1] == other.address[1] + && address[2] == other.address[2] + && address[3] == other.address[3]; +} + +bool IPAddress::operator!= (const IPAddress& other) const noexcept +{ + return ! operator== (other); +} + +#if ! JUCE_WINDOWS +static void addAddress (const sockaddr_in* addr_in, Array& result) +{ + in_addr_t addr = addr_in->sin_addr.s_addr; + + if (addr != INADDR_NONE) + result.addIfNotAlreadyThere (IPAddress (ntohl (addr))); +} + +static void findIPAddresses (int sock, Array& result) +{ + ifconf cfg; + HeapBlock buffer; + int bufferSize = 1024; + + do + { + bufferSize *= 2; + buffer.calloc ((size_t) bufferSize); + + cfg.ifc_len = bufferSize; + cfg.ifc_buf = buffer; + + if (ioctl (sock, SIOCGIFCONF, &cfg) < 0 && errno != EINVAL) + return; + + } while (bufferSize < cfg.ifc_len + 2 * (int) (IFNAMSIZ + sizeof (struct sockaddr_in6))); + + #if JUCE_MAC || JUCE_IOS + while (cfg.ifc_len >= (int) (IFNAMSIZ + sizeof (struct sockaddr_in))) + { + if (cfg.ifc_req->ifr_addr.sa_family == AF_INET) // Skip non-internet addresses + addAddress ((const sockaddr_in*) &cfg.ifc_req->ifr_addr, result); + + cfg.ifc_len -= IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; + cfg.ifc_buf += IFNAMSIZ + cfg.ifc_req->ifr_addr.sa_len; + } + #else + for (size_t i = 0; i < cfg.ifc_len / sizeof (struct ifreq); ++i) + { + const ifreq& item = cfg.ifc_req[i]; + + if (item.ifr_addr.sa_family == AF_INET) + addAddress ((const sockaddr_in*) &item.ifr_addr, result); + } + #endif +} + +void IPAddress::findAllAddresses (Array& result) +{ + const int sock = socket (AF_INET, SOCK_DGRAM, 0); // a dummy socket to execute the IO control + + if (sock >= 0) + { + findIPAddresses (sock, result); + ::close (sock); + } +} +#endif diff --git a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h new file mode 100644 index 0000000..1f2f0e8 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.h @@ -0,0 +1,82 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_IPADDRESS_H_INCLUDED +#define JUCE_IPADDRESS_H_INCLUDED + + +//============================================================================== +/** + An IPV4 address. +*/ +class JUCE_API IPAddress +{ +public: + //============================================================================== + /** Populates a list of all the IP addresses that this machine is using. */ + static void findAllAddresses (Array& results); + + //============================================================================== + /** Creates a null address (0.0.0.0). */ + IPAddress() noexcept; + + /** Creates an address from 4 bytes. */ + explicit IPAddress (const uint8 bytes[4]) noexcept; + + /** Creates an address from 4 bytes. */ + IPAddress (uint8 address1, uint8 address2, uint8 address3, uint8 address4) noexcept; + + /** Creates an address from a packed 32-bit integer, where the MSB is + the first number in the address, and the LSB is the last. + */ + explicit IPAddress (uint32 asNativeEndian32Bit) noexcept; + + /** Parses a string IP address of the form "a.b.c.d". */ + explicit IPAddress (const String& address); + + /** Returns a dot-separated string in the form "1.2.3.4" */ + String toString() const; + + /** Returns an address meaning "any" (0.0.0.0) */ + static IPAddress any() noexcept; + + /** Returns an address meaning "broadcast" (255.255.255.255) */ + static IPAddress broadcast() noexcept; + + /** Returns an address meaning "localhost" (127.0.0.1) */ + static IPAddress local() noexcept; + + bool operator== (const IPAddress& other) const noexcept; + bool operator!= (const IPAddress& other) const noexcept; + + /** The elements of the IP address. */ + uint8 address[4]; +}; + + +#endif // JUCE_IPADDRESS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp index 03eb734..b7cb3c1 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h index 126785e..67e119e 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_MACAddress.h @@ -1,42 +1,38 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_MACADDRESS_JUCEHEADER__ -#define __JUCE_MACADDRESS_JUCEHEADER__ - -#include "../containers/juce_Array.h" +#ifndef JUCE_MACADDRESS_H_INCLUDED +#define JUCE_MACADDRESS_H_INCLUDED //============================================================================== /** - 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 + Represents a MAC network card adapter address ID. */ class JUCE_API MACAddress { @@ -50,10 +46,10 @@ public: MACAddress(); /** Creates a copy of another address. */ - MACAddress (const MACAddress& other); + MACAddress (const MACAddress&); /** Creates a copy of another address. */ - MACAddress& operator= (const MACAddress& other); + MACAddress& operator= (const MACAddress&); /** Creates an address from 6 bytes. */ explicit MACAddress (const uint8 bytes[6]); @@ -74,8 +70,8 @@ public: /** Returns true if this address is null (00-00-00-00-00-00). */ bool isNull() const noexcept; - bool operator== (const MACAddress& other) const noexcept; - bool operator!= (const MACAddress& other) const noexcept; + bool operator== (const MACAddress&) const noexcept; + bool operator!= (const MACAddress&) const noexcept; //============================================================================== private: @@ -83,4 +79,4 @@ private: }; -#endif // __JUCE_MACADDRESS_JUCEHEADER__ +#endif // JUCE_MACADDRESS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp index da13493..488e63f 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.cpp @@ -1,36 +1,59 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ +NamedPipe::NamedPipe() +{ +} + +NamedPipe::~NamedPipe() +{ + close(); +} + bool NamedPipe::openExisting (const String& pipeName) { + close(); + + ScopedWriteLock sl (lock); currentPipeName = pipeName; return openInternal (pipeName, false); } +bool NamedPipe::isOpen() const +{ + return pimpl != nullptr; +} + bool NamedPipe::createNewPipe (const String& pipeName) { + close(); + + ScopedWriteLock sl (lock); currentPipeName = pipeName; return openInternal (pipeName, true); } diff --git a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h index 0864e9d..d2029bd 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_NamedPipe.h @@ -1,37 +1,40 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_NAMEDPIPE_JUCEHEADER__ -#define __JUCE_NAMEDPIPE_JUCEHEADER__ +#ifndef JUCE_NAMEDPIPE_H_INCLUDED +#define JUCE_NAMEDPIPE_H_INCLUDED //============================================================================== /** 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. + Two processes can use NamedPipe objects to exchange blocks of data. @see InterprocessConnection */ @@ -45,16 +48,13 @@ public: /** 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); @@ -81,31 +81,24 @@ public: 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); + int read (void* destBuffer, int maxBytesToRead, int timeOutMilliseconds); /** Writes some data to the pipe. - - If the operation fails, it returns -1, otherwise, it will return the number of - bytes written. + @returns the number of bytes written, or -1 on failure. */ - 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(); + int write (const void* sourceBuffer, int numBytesToWrite, int timeOutMilliseconds); private: //============================================================================== - class Pimpl; + JUCE_PUBLIC_IN_DLL_BUILD (class Pimpl) ScopedPointer pimpl; String currentPipeName; - CriticalSection lock; + ReadWriteLock lock; bool openInternal (const String& pipeName, const bool createPipe); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NamedPipe); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NamedPipe) }; -#endif // __JUCE_NAMEDPIPE_JUCEHEADER__ +#endif // JUCE_NAMEDPIPE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp index b6fea1f..2d5d1fe 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -34,8 +37,10 @@ #if JUCE_WINDOWS typedef int juce_socklen_t; + typedef SOCKET SocketHandle; #else typedef socklen_t juce_socklen_t; + typedef int SocketHandle; #endif //============================================================================== @@ -57,7 +62,7 @@ namespace SocketHelpers #endif } - static bool resetSocketOptions (const int handle, const bool isDatagram, const bool allowBroadcast) noexcept + static bool resetSocketOptions (const SocketHandle handle, const bool isDatagram, const bool allowBroadcast) noexcept { const int sndBufSize = 65536; const int rcvBufSize = 65536; @@ -70,12 +75,13 @@ namespace SocketHelpers : (setsockopt (handle, IPPROTO_TCP, TCP_NODELAY, (const char*) &one, sizeof (one)) == 0)); } - static bool bindSocketToPort (const int handle, const int port) noexcept + static bool bindSocketToPort (const SocketHandle handle, const int port) noexcept { if (handle <= 0 || port <= 0) return false; - struct sockaddr_in servTmpAddr = { 0 }; + struct sockaddr_in servTmpAddr; + zerostruct (servTmpAddr); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) servTmpAddr.sin_family = PF_INET; servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); servTmpAddr.sin_port = htons ((uint16) port); @@ -83,7 +89,7 @@ namespace SocketHelpers return bind (handle, (struct sockaddr*) &servTmpAddr, sizeof (struct sockaddr_in)) >= 0; } - static int readSocket (const int handle, + static int readSocket (const SocketHandle handle, void* const destBuffer, const int maxBytesToRead, bool volatile& connected, const bool blockUntilSpecifiedAmountHasArrived) noexcept @@ -97,7 +103,8 @@ namespace SocketHelpers #if JUCE_WINDOWS bytesThisTime = recv (handle, static_cast (destBuffer) + bytesRead, maxBytesToRead - bytesRead, 0); #else - while ((bytesThisTime = (int) ::read (handle, addBytesToPointer (destBuffer, bytesRead), maxBytesToRead - bytesRead)) < 0 + while ((bytesThisTime = (int) ::read (handle, addBytesToPointer (destBuffer, bytesRead), + (size_t) (maxBytesToRead - bytesRead))) < 0 && errno == EINTR && connected) { @@ -121,7 +128,7 @@ namespace SocketHelpers return bytesRead; } - static int waitForReadiness (const int handle, const bool forReading, const int timeoutMsecs) noexcept + static int waitForReadiness (const SocketHandle handle, const bool forReading, const int timeoutMsecs) noexcept { struct timeval timeout; struct timeval* timeoutp; @@ -147,7 +154,7 @@ namespace SocketHelpers fd_set* const pwset = forReading ? nullptr : &wset; #if JUCE_WINDOWS - if (select (handle + 1, prset, pwset, 0, timeoutp) < 0) + if (select ((int) handle + 1, prset, pwset, 0, timeoutp) < 0) return -1; #else { @@ -174,7 +181,7 @@ namespace SocketHelpers return FD_ISSET (handle, forReading ? &rset : &wset) ? 1 : 0; } - static bool setSocketBlockingState (const int handle, const bool shouldBlock) noexcept + static bool setSocketBlockingState (const SocketHandle handle, const bool shouldBlock) noexcept { #if JUCE_WINDOWS u_long nonBlocking = shouldBlock ? 0 : (u_long) 1; @@ -201,7 +208,9 @@ namespace SocketHelpers const int portNumber, const int timeOutMillisecs) noexcept { - struct addrinfo hints = { 0 }; + struct addrinfo hints; + zerostruct (hints); + hints.ai_family = AF_UNSPEC; hints.ai_socktype = isDatagram ? SOCK_DGRAM : SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; @@ -230,7 +239,7 @@ namespace SocketHelpers } setSocketBlockingState (handle, false); - const int result = ::connect (handle, info->ai_addr, (int) info->ai_addrlen); + const int result = ::connect (handle, info->ai_addr, (socklen_t) info->ai_addrlen); freeaddrinfo (info); if (result < 0) @@ -266,17 +275,15 @@ StreamingSocket::StreamingSocket() SocketHelpers::initSockets(); } -StreamingSocket::StreamingSocket (const String& hostName_, - const int portNumber_, - const int handle_) - : hostName (hostName_), - portNumber (portNumber_), - handle (handle_), +StreamingSocket::StreamingSocket (const String& host, int portNum, int h) + : hostName (host), + portNumber (portNum), + handle (h), connected (true), isListener (false) { SocketHelpers::initSockets(); - SocketHelpers::resetSocketOptions (handle_, false, false); + SocketHelpers::resetSocketOptions (h, false, false); } StreamingSocket::~StreamingSocket() @@ -285,9 +292,11 @@ StreamingSocket::~StreamingSocket() } //============================================================================== -int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, const bool blockUntilSpecifiedAmountHasArrived) +int StreamingSocket::read (void* destBuffer, const int maxBytesToRead, + const bool blockUntilSpecifiedAmountHasArrived) { - return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, blockUntilSpecifiedAmountHasArrived) + return (connected && ! isListener) ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, + connected, blockUntilSpecifiedAmountHasArrived) : -1; } @@ -301,7 +310,7 @@ int StreamingSocket::write (const void* sourceBuffer, const int numBytesToWrite) #else int result; - while ((result = (int) ::write (handle, sourceBuffer, numBytesToWrite)) < 0 + while ((result = (int) ::write (handle, sourceBuffer, (size_t) numBytesToWrite)) < 0 && errno == EINTR) { } @@ -377,7 +386,7 @@ void StreamingSocket::close() ::close (handle); #endif - hostName = String::empty; + hostName.clear(); portNumber = 0; handle = -1; isListener = false; @@ -393,7 +402,9 @@ bool StreamingSocket::createListener (const int newPortNumber, const String& loc portNumber = newPortNumber; isListener = true; - struct sockaddr_in servTmpAddr = { 0 }; + struct sockaddr_in servTmpAddr; + zerostruct (servTmpAddr); + servTmpAddr.sin_family = PF_INET; servTmpAddr.sin_addr.s_addr = htonl (INADDR_ANY); @@ -423,8 +434,9 @@ bool StreamingSocket::createListener (const int newPortNumber, const String& loc StreamingSocket* StreamingSocket::waitForNextConnection() const { - jassert (isListener || ! connected); // to call this method, you first have to use createListener() to - // prepare this socket as a listener. + // To call this method, you first have to use createListener() to + // prepare this socket as a listener. + jassert (isListener || ! connected); if (connected && isListener) { @@ -448,11 +460,11 @@ bool StreamingSocket::isLocal() const noexcept //============================================================================== //============================================================================== -DatagramSocket::DatagramSocket (const int localPortNumber, const bool allowBroadcast_) +DatagramSocket::DatagramSocket (const int localPortNumber, const bool canBroadcast) : portNumber (0), handle (-1), connected (true), - allowBroadcast (allowBroadcast_), + allowBroadcast (canBroadcast), serverAddress (nullptr) { SocketHelpers::initSockets(); @@ -461,18 +473,18 @@ DatagramSocket::DatagramSocket (const int localPortNumber, const bool allowBroad bindToPort (localPortNumber); } -DatagramSocket::DatagramSocket (const String& hostName_, const int portNumber_, - const int handle_, const int localPortNumber) - : hostName (hostName_), - portNumber (portNumber_), - handle (handle_), +DatagramSocket::DatagramSocket (const String& host, const int portNum, + const int h, const int localPortNumber) + : hostName (host), + portNumber (portNum), + handle (h), connected (true), allowBroadcast (false), serverAddress (nullptr) { SocketHelpers::initSockets(); - SocketHelpers::resetSocketOptions (handle_, true, allowBroadcast); + SocketHelpers::resetSocketOptions (h, true, allowBroadcast); bindToPort (localPortNumber); } @@ -494,7 +506,7 @@ void DatagramSocket::close() ::close (handle); #endif - hostName = String::empty; + hostName.clear(); portNumber = 0; handle = -1; } @@ -554,7 +566,8 @@ int DatagramSocket::waitUntilReady (const bool readyForReading, int DatagramSocket::read (void* destBuffer, const int maxBytesToRead, const bool blockUntilSpecifiedAmountHasArrived) { - return connected ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, blockUntilSpecifiedAmountHasArrived) + return connected ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, + connected, blockUntilSpecifiedAmountHasArrived) : -1; } @@ -564,9 +577,9 @@ int DatagramSocket::write (const void* sourceBuffer, const int numBytesToWrite) jassert (serverAddress != nullptr && connected); return connected ? (int) sendto (handle, (const char*) sourceBuffer, - numBytesToWrite, 0, + (size_t) numBytesToWrite, 0, static_cast (serverAddress)->ai_addr, - static_cast (serverAddress)->ai_addrlen) + (juce_socklen_t) static_cast (serverAddress)->ai_addrlen) : -1; } diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h index 57cce14..73795c9 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SOCKET_JUCEHEADER__ -#define __JUCE_SOCKET_JUCEHEADER__ - -#include "../text/juce_String.h" +#ifndef JUCE_SOCKET_H_INCLUDED +#define JUCE_SOCKET_H_INCLUDED //============================================================================== @@ -91,6 +92,9 @@ public: /** True if the socket is connected to this machine rather than over the network. */ bool isLocal() const noexcept; + /** Returns the OS's socket handle that's currently open. */ + int getRawSocketHandle() const noexcept { return handle; } + //============================================================================== /** Waits until the socket is ready for reading or writing. @@ -142,7 +146,7 @@ public: @see waitForNextConnection */ - bool createListener (int portNumber, const String& localHostName = String::empty); + bool createListener (int portNumber, const String& localHostName = String()); /** When in "listener" mode, this waits for a connection and spawns it as a new socket. @@ -155,7 +159,6 @@ public: */ StreamingSocket* waitForNextConnection() const; - private: //============================================================================== String hostName; @@ -164,7 +167,7 @@ private: StreamingSocket (const String& hostname, int portNumber, int handle); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StreamingSocket); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StreamingSocket) }; @@ -237,6 +240,9 @@ public: /** True if the socket is connected to this machine rather than over the network. */ bool isLocal() const noexcept; + /** Returns the OS's socket handle that's currently open. */ + int getRawSocketHandle() const noexcept { return handle; } + //============================================================================== /** Waits until the socket is ready for reading or writing. @@ -283,7 +289,6 @@ public: */ DatagramSocket* waitForNextConnection() const; - private: //============================================================================== String hostName; @@ -293,8 +298,8 @@ private: DatagramSocket (const String& hostname, int portNumber, int handle, int localPortNumber); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DatagramSocket); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DatagramSocket) }; -#endif // __JUCE_SOCKET_JUCEHEADER__ +#endif // JUCE_SOCKET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp index 2460626..49db1f9 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -27,8 +30,7 @@ URL::URL() { } -URL::URL (const String& url_) - : url (url_) +URL::URL (const String& u) : url (u) { int i = url.indexOfChar ('?'); @@ -61,13 +63,19 @@ URL::URL (const String& url_) } } +URL::URL (const String& u, int) : url (u) {} + +URL URL::createWithoutParsing (const String& u) +{ + return URL (u, 0); +} + URL::URL (const URL& other) : url (other.url), postData (other.postData), parameterNames (other.parameterNames), parameterValues (other.parameterValues), - filesToUpload (other.filesToUpload), - mimeTypes (other.mimeTypes) + filesToUpload (other.filesToUpload) { } @@ -78,7 +86,6 @@ URL& URL::operator= (const URL& other) parameterNames = other.parameterNames; parameterValues = other.parameterValues; filesToUpload = other.filesToUpload; - mimeTypes = other.mimeTypes; return *this; } @@ -89,8 +96,7 @@ bool URL::operator== (const URL& other) const && postData == other.postData && parameterNames == other.parameterNames && parameterValues == other.parameterValues - && filesToUpload == other.filesToUpload - && mimeTypes == other.mimeTypes; + && filesToUpload == other.filesToUpload; } bool URL::operator!= (const URL& other) const @@ -147,61 +153,6 @@ namespace URLHelpers return url.indexOfChar (findStartOfNetLocation (url), '/') + 1; } - static void createHeadersAndPostData (const URL& url, String& headers, MemoryBlock& postData) - { - MemoryOutputStream data (postData, false); - - if (url.getFilesToUpload().size() > 0) - { - // need to upload some files, so do it as multi-part... - const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); - - headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; - - data << "--" << boundary; - - int i; - for (i = 0; i < url.getParameterNames().size(); ++i) - { - data << "\r\nContent-Disposition: form-data; name=\"" - << url.getParameterNames() [i] - << "\"\r\n\r\n" - << url.getParameterValues() [i] - << "\r\n--" - << boundary; - } - - for (i = 0; i < url.getFilesToUpload().size(); ++i) - { - const File file (url.getFilesToUpload().getAllValues() [i]); - const String paramName (url.getFilesToUpload().getAllKeys() [i]); - - data << "\r\nContent-Disposition: form-data; name=\"" << paramName - << "\"; filename=\"" << file.getFileName() << "\"\r\n"; - - const String mimeType (url.getMimeTypesOfUploadFiles() - .getValue (paramName, String::empty)); - - if (mimeType.isNotEmpty()) - data << "Content-Type: " << mimeType << "\r\n"; - - data << "Content-Transfer-Encoding: binary\r\n\r\n" - << file << "\r\n--" << boundary; - } - - data << "--\r\n"; - } - else - { - data << getMangledParameters (url) - << url.getPostData(); - - // just a short text attachment, so use simple url encoding.. - headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " - << (int) data.getDataSize() << "\r\n"; - } - } - static void concatenatePaths (String& path, const String& suffix) { if (! path.endsWithChar ('/')) @@ -224,8 +175,8 @@ String URL::toString (const bool includeGetParameters) const { if (includeGetParameters && parameterNames.size() > 0) return url + "?" + URLHelpers::getMangledParameters (*this); - else - return url; + + return url; } bool URL::isWellFormed() const @@ -250,7 +201,7 @@ String URL::getSubPath() const { const int startOfPath = URLHelpers::findStartOfPath (url); - return startOfPath <= 0 ? String::empty + return startOfPath <= 0 ? String() : url.substring (startOfPath); } @@ -286,10 +237,67 @@ URL URL::getChildURL (const String& subPath) const return u; } +void URL::createHeadersAndPostData (String& headers, MemoryBlock& headersAndPostData) const +{ + MemoryOutputStream data (headersAndPostData, false); + + if (filesToUpload.size() > 0) + { + // (this doesn't currently support mixing custom post-data with uploads..) + jassert (postData.isEmpty()); + + const String boundary (String::toHexString (Random::getSystemRandom().nextInt64())); + + headers << "Content-Type: multipart/form-data; boundary=" << boundary << "\r\n"; + + data << "--" << boundary; + + for (int i = 0; i < parameterNames.size(); ++i) + { + data << "\r\nContent-Disposition: form-data; name=\"" << parameterNames[i] + << "\"\r\n\r\n" << parameterValues[i] + << "\r\n--" << boundary; + } + + for (int i = 0; i < filesToUpload.size(); ++i) + { + const Upload& f = *filesToUpload.getObjectPointerUnchecked(i); + + data << "\r\nContent-Disposition: form-data; name=\"" << f.parameterName + << "\"; filename=\"" << f.filename << "\"\r\n"; + + if (f.mimeType.isNotEmpty()) + data << "Content-Type: " << f.mimeType << "\r\n"; + + data << "Content-Transfer-Encoding: binary\r\n\r\n"; + + if (f.data != nullptr) + data << *f.data; + else + data << f.file; + + data << "\r\n--" << boundary; + } + + data << "--\r\n"; + } + else + { + data << URLHelpers::getMangledParameters (*this) + << postData; + + // if the user-supplied headers didn't contain a content-type, add one now.. + if (! headers.containsIgnoreCase ("Content-Type")) + headers << "Content-Type: application/x-www-form-urlencoded\r\n"; + + headers << "Content-length: " << (int) data.getDataSize() << "\r\n"; + } +} + //============================================================================== bool URL::isProbablyAWebsiteURL (const String& possibleURL) { - const char* validProtocols[] = { "http:", "ftp:", "https:" }; + static const char* validProtocols[] = { "http:", "ftp:", "https:" }; for (int i = 0; i < numElementsInArray (validProtocols); ++i) if (possibleURL.startsWithIgnoreCase (validProtocols[i])) @@ -311,38 +319,45 @@ bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) return atSign > 0 && possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1) - && (! possibleEmailAddress.endsWithChar ('.')); + && ! possibleEmailAddress.endsWithChar ('.'); } //============================================================================== InputStream* URL::createInputStream (const bool usePostCommand, OpenStreamProgressCallback* const progressCallback, void* const progressCallbackContext, - const String& extraHeaders, + String headers, const int timeOutMs, - StringPairArray* const responseHeaders) const + StringPairArray* const responseHeaders, + int* statusCode) const { - String headers; MemoryBlock headersAndPostData; - if (usePostCommand) - URLHelpers::createHeadersAndPostData (*this, headers, headersAndPostData); - - headers += extraHeaders; - if (! headers.endsWithChar ('\n')) headers << "\r\n"; - return createNativeStream (toString (! usePostCommand), usePostCommand, headersAndPostData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders); + if (usePostCommand) + createHeadersAndPostData (headers, headersAndPostData); + + if (! headers.endsWithChar ('\n')) + headers << "\r\n"; + + ScopedPointer wi (new WebInputStream (toString (! usePostCommand), + usePostCommand, headersAndPostData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); + + if (statusCode != nullptr) + *statusCode = wi->statusCode; + + return wi->isError() ? nullptr : wi.release(); } //============================================================================== bool URL::readEntireBinaryStream (MemoryBlock& destData, const bool usePostCommand) const { - const ScopedPointer in (createInputStream (usePostCommand)); + const ScopedPointer in (createInputStream (usePostCommand)); if (in != nullptr) { @@ -355,12 +370,12 @@ bool URL::readEntireBinaryStream (MemoryBlock& destData, String URL::readEntireTextStream (const bool usePostCommand) const { - const ScopedPointer in (createInputStream (usePostCommand)); + const ScopedPointer in (createInputStream (usePostCommand)); if (in != nullptr) return in->readEntireStreamAsString(); - return String::empty; + return String(); } XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const @@ -377,33 +392,44 @@ URL URL::withParameter (const String& parameterName, return u; } -URL URL::withFileToUpload (const String& parameterName, - const File& fileToUpload, - const String& mimeType) const +URL URL::withPOSTData (const String& newPostData) const +{ + URL u (*this); + u.postData = newPostData; + return u; +} + +URL::Upload::Upload (const String& param, const String& name, + const String& mime, const File& f, MemoryBlock* mb) + : parameterName (param), filename (name), mimeType (mime), file (f), data (mb) { jassert (mimeType.isNotEmpty()); // You need to supply a mime type! +} +URL URL::withUpload (Upload* const f) const +{ URL u (*this); - u.filesToUpload.set (parameterName, fileToUpload.getFullPathName()); - u.mimeTypes.set (parameterName, mimeType); + + for (int i = u.filesToUpload.size(); --i >= 0;) + if (u.filesToUpload.getObjectPointerUnchecked(i)->parameterName == f->parameterName) + u.filesToUpload.remove (i); + + u.filesToUpload.add (f); return u; } -URL URL::withPOSTData (const String& postData_) const +URL URL::withFileToUpload (const String& parameterName, const File& fileToUpload, + const String& mimeType) const { - URL u (*this); - u.postData = postData_; - return u; + return withUpload (new Upload (parameterName, fileToUpload.getFileName(), + mimeType, fileToUpload, nullptr)); } -const StringPairArray& URL::getFilesToUpload() const +URL URL::withDataToUpload (const String& parameterName, const String& filename, + const MemoryBlock& fileContentToUpload, const String& mimeType) const { - return filesToUpload; -} - -const StringPairArray& URL::getMimeTypesOfUploadFiles() const -{ - return mimeTypes; + return withUpload (new Upload (parameterName, filename, mimeType, File(), + new MemoryBlock (fileContentToUpload))); } //============================================================================== @@ -416,7 +442,7 @@ String URL::removeEscapeChars (const String& s) // We need to operate on the string as raw UTF8 chars, and then recombine them into unicode // after all the replacements have been made, so that multi-byte chars are handled. - Array utf8 (result.toUTF8().getAddress(), result.getNumBytesAsUTF8()); + Array utf8 (result.toRawUTF8(), (int) result.getNumBytesAsUTF8()); for (int i = 0; i < utf8.size(); ++i) { @@ -441,7 +467,7 @@ String URL::addEscapeChars (const String& s, const bool isParameter) const CharPointer_UTF8 legalChars (isParameter ? "_-.*!'()" : ",$_-.*!'()"); - Array utf8 (s.toUTF8().getAddress(), s.getNumBytesAsUTF8()); + Array utf8 (s.toRawUTF8(), (int) s.getNumBytesAsUTF8()); for (int i = 0; i < utf8.size(); ++i) { @@ -467,5 +493,5 @@ bool URL::launchInDefaultBrowser() const if (u.containsChar ('@') && ! u.containsChar (':')) u = "mailto:" + u; - return Process::openDocument (u, String::empty); + return Process::openDocument (u, String()); } diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.h b/JuceLibraryCode/modules/juce_core/network/juce_URL.h index d416302..4f511d8 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.h @@ -1,35 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_URL_JUCEHEADER__ -#define __JUCE_URL_JUCEHEADER__ - -#include "../text/juce_StringPairArray.h" -#include "../files/juce_File.h" -class InputStream; -class XmlElement; +#ifndef JUCE_URL_H_INCLUDED +#define JUCE_URL_H_INCLUDED //============================================================================== @@ -46,7 +44,11 @@ public: /** Creates an empty URL. */ URL(); - /** Creates a URL from a string. */ + /** Creates a URL from a string. + This will parse any embedded parameters after a '?' character and store them + in the list (see getParameterNames etc). If you don't want this to happen, you + can use createWithoutParsing(). + */ URL (const String& url); /** Creates a copy of another URL. */ @@ -135,22 +137,39 @@ public: URL withParameter (const String& parameterName, const String& parameterValue) const; - /** Returns a copy of this URl, with a file-upload type parameter added to it. + /** 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. + until this URL is later used to create a network input stream. If you want to + upload data from memory, use withDataToUpload(). + + @see withDataToUpload */ URL withFileToUpload (const String& parameterName, const File& fileToUpload, const String& mimeType) 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 content. + Note that the filename parameter should not be a full path, it's just the + last part of the filename. + + @see withFileToUpload + */ + URL withDataToUpload (const String& parameterName, + const String& filename, + const MemoryBlock& fileContentToUpload, + const String& mimeType) const; + /** Returns an array of the names of all the URL's parameters. E.g. for the url "www.fish.com?type=haddock&amount=some+fish", this array would - contain two items: "type" and "haddock". + contain two items: "type" and "amount". You can call getParameterValues() to get the corresponding value of each parameter. Note that the list can contain multiple parameters with the same name. @@ -173,17 +192,6 @@ public: */ const StringArray& getParameterValues() const noexcept { return parameterValues; } - /** 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 @@ -232,7 +240,7 @@ public: /** 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 + the parameters, 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 @@ -245,17 +253,20 @@ public: @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. - @param responseHeaders if this is non-zero, all the (key, value) pairs received as headers + @param responseHeaders if this is non-null, all the (key, value) pairs received as headers in the response will be stored in this array + @param statusCode if this is non-null, it will get set to the http status code, if one + is known, or 0 if a code isn't available @returns an input stream that the caller must delete, or a null pointer if there was an error trying to open it. */ InputStream* createInputStream (bool usePostCommand, OpenStreamProgressCallback* progressCallback = nullptr, void* progressCallbackContext = nullptr, - const String& extraHeaders = String::empty, + String extraHeaders = String(), int connectionTimeOutMs = 0, - StringPairArray* responseHeaders = nullptr) const; + StringPairArray* responseHeaders = nullptr, + int* statusCode = nullptr) const; //============================================================================== @@ -328,20 +339,37 @@ public: */ static String removeEscapeChars (const String& stringToRemoveEscapeCharsFrom); + /** Returns a URL without attempting to remove any embedded parameters from the string. + This may be necessary if you need to create a request that involves both POST + parameters and parameters which are embedded in the URL address itself. + */ + static URL createWithoutParsing (const String& url); + private: //============================================================================== String url, postData; StringArray parameterNames, parameterValues; - StringPairArray filesToUpload, mimeTypes; + struct Upload : public ReferenceCountedObject + { + Upload (const String&, const String&, const String&, const File&, MemoryBlock*); + String parameterName, filename, mimeType; + File file; + ScopedPointer data; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Upload) + }; + + friend struct ContainerDeletePolicy; + ReferenceCountedArray filesToUpload; + + URL (const String&, int); void addParameter (const String&, const String&); + void createHeadersAndPostData (String&, MemoryBlock&) const; + URL withUpload (Upload*) const; - static InputStream* createNativeStream (const String& address, bool isPost, const MemoryBlock& postData, - OpenStreamProgressCallback* progressCallback, - void* progressCallbackContext, const String& headers, - const int timeOutMs, StringPairArray* responseHeaders); - JUCE_LEAK_DETECTOR (URL); + JUCE_LEAK_DETECTOR (URL) }; -#endif // __JUCE_URL_JUCEHEADER__ +#endif // JUCE_URL_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp index 05d64b5..ec01f8e 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -87,8 +90,7 @@ bool BufferedInputStream::setPosition (int64 newPosition) bool BufferedInputStream::isExhausted() { - return (position >= lastReadPos) - && source->isExhausted(); + return position >= lastReadPos && source->isExhausted(); } void BufferedInputStream::ensureBuffered() diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h index 8e958ad..8f1e633 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_BufferedInputStream.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ -#define __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ - -#include "juce_InputStream.h" -#include "../memory/juce_OptionalScopedPointer.h" -#include "../memory/juce_HeapBlock.h" +#ifndef JUCE_BUFFEREDINPUTSTREAM_H_INCLUDED +#define JUCE_BUFFEREDINPUTSTREAM_H_INCLUDED //============================================================================== @@ -71,12 +70,12 @@ public: //============================================================================== - int64 getTotalLength(); - int64 getPosition(); - bool setPosition (int64 newPosition); - int read (void* destBuffer, int maxBytesToRead); - String readString(); - bool isExhausted(); + int64 getTotalLength() override; + int64 getPosition() override; + bool setPosition (int64 newPosition) override; + int read (void* destBuffer, int maxBytesToRead) override; + String readString() override; + bool isExhausted() override; private: @@ -87,7 +86,7 @@ private: HeapBlock buffer; void ensureBuffered(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferedInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BufferedInputStream) }; -#endif // __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ +#endif // JUCE_BUFFEREDINPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp index e803a3f..51b9db9 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.cpp @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -FileInputSource::FileInputSource (const File& file_, bool useFileTimeInHashGeneration_) - : file (file_), useFileTimeInHashGeneration (useFileTimeInHashGeneration_) +FileInputSource::FileInputSource (const File& f, bool useFileTimeInHash) + : file (f), useFileTimeInHashGeneration (useFileTimeInHash) { } diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h index 35b1984..d2e6d86 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ -#define __JUCE_FILEINPUTSOURCE_JUCEHEADER__ - -#include "juce_InputSource.h" -#include "../files/juce_File.h" +#ifndef JUCE_FILEINPUTSOURCE_H_INCLUDED +#define JUCE_FILEINPUTSOURCE_H_INCLUDED //============================================================================== @@ -59,8 +59,8 @@ private: const File file; bool useFileTimeInHashGeneration; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileInputSource) }; -#endif // __JUCE_FILEINPUTSOURCE_JUCEHEADER__ +#endif // JUCE_FILEINPUTSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h b/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h index 456949f..0e13ac5 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputSource.h @@ -1,32 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ -#define __JUCE_INPUTSOURCE_JUCEHEADER__ +#ifndef JUCE_INPUTSOURCE_H_INCLUDED +#define JUCE_INPUTSOURCE_H_INCLUDED -#include "juce_InputStream.h" //============================================================================== /** @@ -49,7 +51,7 @@ public: //============================================================================== /** Returns a new InputStream to read this item. - @returns an inputstream that the caller will delete, or 0 if + @returns an inputstream that the caller will delete, or nullptr if the filename isn't found. */ virtual InputStream* createInputStream() = 0; @@ -57,7 +59,7 @@ public: /** 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 + @returns an inputstream that the caller will delete, or nullptr if the item isn't found. */ virtual InputStream* createInputStreamFor (const String& relatedItemPath) = 0; @@ -69,8 +71,8 @@ public: private: //============================================================================== - JUCE_LEAK_DETECTOR (InputSource); + JUCE_LEAK_DETECTOR (InputSource) }; -#endif // __JUCE_INPUTSOURCE_JUCEHEADER__ +#endif // JUCE_INPUTSOURCE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp index f5afaf3..08ff61c 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h index 187f219..0c7e2dc 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_InputStream.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_INPUTSTREAM_JUCEHEADER__ -#define __JUCE_INPUTSTREAM_JUCEHEADER__ - -#include "../text/juce_String.h" -class MemoryBlock; +#ifndef JUCE_INPUTSTREAM_H_INCLUDED +#define JUCE_INPUTSTREAM_H_INCLUDED //============================================================================== @@ -83,41 +83,28 @@ public: 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. - + The bool is encoded as a single byte - non-zero 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 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 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(); @@ -167,57 +154,42 @@ public: 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. + /** Reads a UTF-8 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. @@ -227,10 +199,10 @@ public: */ virtual String readNextLine(); - /** Reads a zero-terminated UTF8 string from the stream. + /** Reads a zero-terminated UTF-8 string from the stream. - This will read characters from the stream until it hits a zero character or - end-of-stream. + This will read characters from the stream until it hits a null character + or end-of-stream. @see OutputStream::writeString, readEntireStreamAsString */ @@ -238,8 +210,8 @@ public: /** 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. + This will read from the stream's current position until the end-of-stream. + It can read from either UTF-16 or UTF-8 formats. */ virtual String readEntireStreamAsString(); @@ -256,7 +228,6 @@ public: //============================================================================== /** Returns the offset of the next byte that will be read from the stream. - @see setPosition */ virtual int64 getPosition() = 0; @@ -289,7 +260,7 @@ protected: InputStream() noexcept {} private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InputStream) }; -#endif // __JUCE_INPUTSTREAM_JUCEHEADER__ +#endif // JUCE_INPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp index 6ca7436..19b1052 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -26,7 +29,7 @@ MemoryInputStream::MemoryInputStream (const void* const sourceData, const size_t sourceDataSize, const bool keepInternalCopy) - : data (static_cast (sourceData)), + : data (sourceData), dataSize (sourceDataSize), position (0) { @@ -36,7 +39,7 @@ MemoryInputStream::MemoryInputStream (const void* const sourceData, MemoryInputStream::MemoryInputStream (const MemoryBlock& sourceData, const bool keepInternalCopy) - : data (static_cast (sourceData.getData())), + : data (sourceData.getData()), dataSize (sourceData.getSize()), position (0) { @@ -68,9 +71,9 @@ int MemoryInputStream::read (void* const buffer, const int howMany) if (num <= 0) return 0; - memcpy (buffer, data + position, (size_t) num); - position += num; - return (int) num; + memcpy (buffer, addBytesToPointer (data, position), (size_t) num); + position += (unsigned int) num; + return num; } bool MemoryInputStream::isExhausted() @@ -101,12 +104,12 @@ public: void runTest() { beginTest ("Basics"); - Random r; + Random r = getRandom(); int randomInt = r.nextInt(); int64 randomInt64 = r.nextInt64(); double randomDouble = r.nextDouble(); - String randomString (createRandomWideCharString()); + String randomString (createRandomWideCharString (r)); MemoryOutputStream mo; mo.writeInt (randomInt); @@ -129,10 +132,9 @@ public: expect (mi.readDoubleBigEndian() == randomDouble); } - static String createRandomWideCharString() + static String createRandomWideCharString (Random& r) { juce_wchar buffer [50] = { 0 }; - Random r; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h index 74b9e14..013bc39 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryInputStream.h @@ -1,38 +1,38 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ -#define __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ - -#include "juce_InputStream.h" -#include "../memory/juce_HeapBlock.h" +#ifndef JUCE_MEMORYINPUTSTREAM_H_INCLUDED +#define JUCE_MEMORYINPUTSTREAM_H_INCLUDED //============================================================================== /** - Allows a block of data and to be accessed as a stream. + Allows a block of data 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. @@ -70,22 +70,28 @@ public: /** Destructor. */ ~MemoryInputStream(); + /** Returns a pointer to the source data block from which this stream is reading. */ + const void* getData() const noexcept { return data; } + + /** Returns the number of bytes of source data in the block from which this stream is reading. */ + size_t getDataSize() const noexcept { return dataSize; } + //============================================================================== - int64 getPosition(); - bool setPosition (int64 pos); - int64 getTotalLength(); - bool isExhausted(); - int read (void* destBuffer, int maxBytesToRead); + int64 getPosition() override; + bool setPosition (int64 pos) override; + int64 getTotalLength() override; + bool isExhausted() override; + int read (void* destBuffer, int maxBytesToRead) override; private: //============================================================================== - const char* data; + const void* data; size_t dataSize, position; HeapBlock internalCopy; void createInternalCopy(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryInputStream) }; -#endif // __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ +#endif // JUCE_MEMORYINPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp index 4049263..7d07ce6 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.cpp @@ -1,46 +1,54 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ MemoryOutputStream::MemoryOutputStream (const size_t initialSize) - : data (internalBlock), - position (0), - size (0) + : blockToUse (&internalBlock), externalData (nullptr), + position (0), size (0), availableSize (0) { internalBlock.setSize (initialSize, false); } MemoryOutputStream::MemoryOutputStream (MemoryBlock& memoryBlockToWriteTo, const bool appendToExistingBlockContent) - : data (memoryBlockToWriteTo), - position (0), - size (0) + : blockToUse (&memoryBlockToWriteTo), externalData (nullptr), + position (0), size (0), availableSize (0) { if (appendToExistingBlockContent) position = size = memoryBlockToWriteTo.getSize(); } +MemoryOutputStream::MemoryOutputStream (void* destBuffer, size_t destBufferSize) + : blockToUse (nullptr), externalData (destBuffer), + position (0), size (0), availableSize (destBufferSize) +{ + jassert (externalData != nullptr); // This must be a valid pointer. +} + MemoryOutputStream::~MemoryOutputStream() { trimExternalBlockSize(); @@ -53,13 +61,14 @@ void MemoryOutputStream::flush() void MemoryOutputStream::trimExternalBlockSize() { - if (&data != &internalBlock) - data.setSize (size, false); + if (blockToUse != &internalBlock && blockToUse != nullptr) + blockToUse->setSize (size, false); } void MemoryOutputStream::preallocate (const size_t bytesToPreallocate) { - data.ensureSize (bytesToPreallocate + 1); + if (blockToUse != nullptr) + blockToUse->ensureSize (bytesToPreallocate + 1); } void MemoryOutputStream::reset() noexcept @@ -68,38 +77,73 @@ void MemoryOutputStream::reset() noexcept size = 0; } -void MemoryOutputStream::prepareToWrite (int numBytes) +char* MemoryOutputStream::prepareToWrite (size_t numBytes) { - const size_t storageNeeded = position + numBytes; + jassert ((ssize_t) numBytes >= 0); + size_t storageNeeded = position + numBytes; - if (storageNeeded >= data.getSize()) - data.ensureSize ((storageNeeded + jmin ((int) (storageNeeded / 2), 1024 * 1024) + 32) & ~31); -} + char* data; -bool MemoryOutputStream::write (const void* const buffer, int howMany) -{ - jassert (buffer != nullptr && howMany >= 0); - - if (howMany > 0) + if (blockToUse != nullptr) { - prepareToWrite (howMany); - memcpy (static_cast (data.getData()) + position, buffer, (size_t) howMany); - position += howMany; - size = jmax (size, position); + if (storageNeeded >= blockToUse->getSize()) + blockToUse->ensureSize ((storageNeeded + jmin (storageNeeded / 2, (size_t) (1024 * 1024)) + 32) & ~31u); + + data = static_cast (blockToUse->getData()); + } + else + { + if (storageNeeded > availableSize) + return nullptr; + + data = static_cast (externalData); } - return true; + char* const writePointer = data + position; + position += numBytes; + size = jmax (size, position); + return writePointer; } -void MemoryOutputStream::writeRepeatedByte (uint8 byte, int howMany) +bool MemoryOutputStream::write (const void* const buffer, size_t howMany) { - if (howMany > 0) + jassert (buffer != nullptr); + + if (howMany == 0) + return true; + + if (char* dest = prepareToWrite (howMany)) { - prepareToWrite (howMany); - memset (static_cast (data.getData()) + position, byte, (size_t) howMany); - position += howMany; - size = jmax (size, position); + memcpy (dest, buffer, howMany); + return true; } + + return false; +} + +bool MemoryOutputStream::writeRepeatedByte (uint8 byte, size_t howMany) +{ + if (howMany == 0) + return true; + + if (char* dest = prepareToWrite (howMany)) + { + memset (dest, byte, howMany); + return true; + } + + return false; +} + +bool MemoryOutputStream::appendUTF8Char (juce_wchar c) +{ + if (char* dest = prepareToWrite (CharPointer_UTF8::getBytesRequiredFor (c))) + { + CharPointer_UTF8 (dest).write (c); + return true; + } + + return false; } MemoryBlock MemoryOutputStream::getMemoryBlock() const @@ -109,10 +153,13 @@ MemoryBlock MemoryOutputStream::getMemoryBlock() const const void* MemoryOutputStream::getData() const noexcept { - if (data.getSize() > size) - static_cast (data.getData()) [size] = 0; + if (blockToUse == nullptr) + return externalData; - return data.getData(); + if (blockToUse->getSize() > size) + static_cast (blockToUse->getData()) [size] = 0; + + return blockToUse->getData(); } bool MemoryOutputStream::setPosition (int64 newPosition) @@ -123,11 +170,9 @@ bool MemoryOutputStream::setPosition (int64 newPosition) position = jlimit ((size_t) 0, size, (size_t) newPosition); return true; } - else - { - // trying to make it bigger isn't a good thing to do.. - return false; - } + + // can't move beyond the end of the stream.. + return false; } int MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite) @@ -137,10 +182,11 @@ int MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNumB if (availableData > 0) { - if (maxNumBytesToWrite > 0 && maxNumBytesToWrite < availableData) - availableData = maxNumBytesToWrite; + if (maxNumBytesToWrite > availableData) + maxNumBytesToWrite = availableData; - preallocate (data.getSize() + (size_t) maxNumBytesToWrite); + if (blockToUse != nullptr) + preallocate (blockToUse->getSize() + (size_t) maxNumBytesToWrite); } return OutputStream::writeFromInputStream (source, maxNumBytesToWrite); @@ -159,7 +205,8 @@ String MemoryOutputStream::toString() const OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryOutputStream& streamToRead) { - const int dataSize = (int) streamToRead.getDataSize(); + const size_t dataSize = streamToRead.getDataSize(); + if (dataSize > 0) stream.write (streamToRead.getData(), dataSize); diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h index af137a2..95d7be4 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_MemoryOutputStream.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ -#define __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ - -#include "juce_OutputStream.h" -#include "../memory/juce_MemoryBlock.h" -#include "../memory/juce_ScopedPointer.h" +#ifndef JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED +#define JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED //============================================================================== @@ -42,8 +41,7 @@ class JUCE_API MemoryOutputStream : public OutputStream { public: //============================================================================== - /** Creates an empty memory stream ready for writing into. - + /** Creates an empty memory stream, ready to be written into. @param initialSize the intial amount of capacity to allocate for writing into */ MemoryOutputStream (size_t initialSize = 256); @@ -63,6 +61,13 @@ public: MemoryOutputStream (MemoryBlock& memoryBlockToWriteTo, bool appendToExistingBlockContent); + /** Creates a MemoryOutputStream that will write into a user-supplied, fixed-size + block of memory. + When using this mode, the stream will write directly into this memory area until + it's full, at which point write operations will fail. + */ + MemoryOutputStream (void* destBuffer, size_t destBufferSize); + /** Destructor. This will free any data that was written to it. */ @@ -70,13 +75,11 @@ public: //============================================================================== /** Returns a pointer to the data that has been written to the stream. - @see getDataSize */ const void* getData() const noexcept; /** Returns the number of bytes of data that have been written to the stream. - @see getData */ size_t getDataSize() const noexcept { return size; } @@ -89,6 +92,9 @@ public: */ void preallocate (size_t bytesToPreallocate); + /** Appends the utf-8 bytes for a unicode character */ + bool appendUTF8Char (juce_wchar character); + /** Returns a String created from the (UTF8) data that has been written to the stream. */ String toUTF8() const; @@ -107,26 +113,27 @@ public: */ void flush(); - bool write (const void* buffer, int howMany); - int64 getPosition() { return position; } - bool setPosition (int64 newPosition); - int writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite); - void writeRepeatedByte (uint8 byte, int numTimesToRepeat); + bool write (const void*, size_t) override; + int64 getPosition() override { return position; } + bool setPosition (int64) override; + int writeFromInputStream (InputStream&, int64 maxNumBytesToWrite) override; + bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) override; private: //============================================================================== - MemoryBlock& data; + MemoryBlock* const blockToUse; MemoryBlock internalBlock; - size_t position, size; + void* externalData; + size_t position, size, availableSize; void trimExternalBlockSize(); - void prepareToWrite (int numBytes); + char* prepareToWrite (size_t); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryOutputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryOutputStream) }; /** Copies all the data that has been written to a MemoryOutputStream into another stream. */ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryOutputStream& streamToRead); -#endif // __JUCE_MEMORYOUTPUTSTREAM_JUCEHEADER__ +#endif // JUCE_MEMORYOUTPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp index 45b41cb..7c6d0bd 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -58,53 +61,56 @@ OutputStream::OutputStream() OutputStream::~OutputStream() { #if JUCE_DEBUG - danglingStreamChecker.activeStreams.removeValue (this); + danglingStreamChecker.activeStreams.removeFirstMatchingValue (this); #endif } //============================================================================== -void OutputStream::writeBool (const bool b) +bool OutputStream::writeBool (const bool b) { - writeByte (b ? (char) 1 - : (char) 0); + return writeByte (b ? (char) 1 + : (char) 0); } -void OutputStream::writeByte (char byte) +bool OutputStream::writeByte (char byte) { - write (&byte, 1); + return write (&byte, 1); } -void OutputStream::writeRepeatedByte (uint8 byte, int numTimesToRepeat) +bool OutputStream::writeRepeatedByte (uint8 byte, size_t numTimesToRepeat) { - while (--numTimesToRepeat >= 0) - writeByte ((char) byte); + for (size_t i = 0; i < numTimesToRepeat; ++i) + if (! writeByte ((char) byte)) + return false; + + return true; } -void OutputStream::writeShort (short value) +bool OutputStream::writeShort (short value) { const unsigned short v = ByteOrder::swapIfBigEndian ((unsigned short) value); - write (&v, 2); + return write (&v, 2); } -void OutputStream::writeShortBigEndian (short value) +bool OutputStream::writeShortBigEndian (short value) { const unsigned short v = ByteOrder::swapIfLittleEndian ((unsigned short) value); - write (&v, 2); + return write (&v, 2); } -void OutputStream::writeInt (int value) +bool OutputStream::writeInt (int value) { const unsigned int v = ByteOrder::swapIfBigEndian ((unsigned int) value); - write (&v, 4); + return write (&v, 4); } -void OutputStream::writeIntBigEndian (int value) +bool OutputStream::writeIntBigEndian (int value) { const unsigned int v = ByteOrder::swapIfLittleEndian ((unsigned int) value); - write (&v, 4); + return write (&v, 4); } -void OutputStream::writeCompressedInt (int value) +bool OutputStream::writeCompressedInt (int value) { unsigned int un = (value < 0) ? (unsigned int) -value : (unsigned int) value; @@ -123,60 +129,64 @@ void OutputStream::writeCompressedInt (int value) if (value < 0) data[0] |= 0x80; - write (data, num + 1); + return write (data, (size_t) num + 1); } -void OutputStream::writeInt64 (int64 value) +bool OutputStream::writeInt64 (int64 value) { const uint64 v = ByteOrder::swapIfBigEndian ((uint64) value); - write (&v, 8); + return write (&v, 8); } -void OutputStream::writeInt64BigEndian (int64 value) +bool OutputStream::writeInt64BigEndian (int64 value) { const uint64 v = ByteOrder::swapIfLittleEndian ((uint64) value); - write (&v, 8); + return write (&v, 8); } -void OutputStream::writeFloat (float value) +bool OutputStream::writeFloat (float value) { union { int asInt; float asFloat; } n; n.asFloat = value; - writeInt (n.asInt); + return writeInt (n.asInt); } -void OutputStream::writeFloatBigEndian (float value) +bool OutputStream::writeFloatBigEndian (float value) { union { int asInt; float asFloat; } n; n.asFloat = value; - writeIntBigEndian (n.asInt); + return writeIntBigEndian (n.asInt); } -void OutputStream::writeDouble (double value) +bool OutputStream::writeDouble (double value) { union { int64 asInt; double asDouble; } n; n.asDouble = value; - writeInt64 (n.asInt); + return writeInt64 (n.asInt); } -void OutputStream::writeDoubleBigEndian (double value) +bool OutputStream::writeDoubleBigEndian (double value) { union { int64 asInt; double asDouble; } n; n.asDouble = value; - writeInt64BigEndian (n.asInt); + return writeInt64BigEndian (n.asInt); } -void OutputStream::writeString (const String& text) +bool OutputStream::writeString (const String& text) { + #if (JUCE_STRING_UTF_TYPE == 8) + return write (text.toRawUTF8(), text.getNumBytesAsUTF8() + 1); + #else // (This avoids using toUTF8() to prevent the memory bloat that it would leave behind // if lots of large, persistent strings were to be written to streams). - const int numBytes = text.getNumBytesAsUTF8() + 1; - HeapBlock temp ((size_t) numBytes); + const size_t numBytes = text.getNumBytesAsUTF8() + 1; + HeapBlock temp (numBytes); text.copyToUTF8 (temp, numBytes); - write (temp, numBytes); + return write (temp, numBytes); + #endif } -void OutputStream::writeText (const String& text, const bool asUTF16, +bool OutputStream::writeText (const String& text, const bool asUTF16, const bool writeUTF16ByteOrderMark) { if (asUTF16) @@ -198,7 +208,9 @@ void OutputStream::writeText (const String& text, const bool asUTF16, writeShort ((short) '\r'); lastCharWasReturn = (c == L'\r'); - writeShort ((short) c); + + if (! writeShort ((short) c)) + return false; } } else @@ -211,9 +223,12 @@ void OutputStream::writeText (const String& text, const bool asUTF16, if (*t == '\n') { if (t > src) - write (src, (int) (t - src)); + if (! write (src, (size_t) (t - src))) + return false; + + if (! write ("\r\n", 2)) + return false; - write ("\r\n", 2); src = t + 1; } else if (*t == '\r') @@ -224,7 +239,8 @@ void OutputStream::writeText (const String& text, const bool asUTF16, else if (*t == 0) { if (t > src) - write (src, (int) (t - src)); + if (! write (src, (size_t) (t - src))) + return false; break; } @@ -232,6 +248,8 @@ void OutputStream::writeText (const String& text, const bool asUTF16, ++t; } } + + return true; } int OutputStream::writeFromInputStream (InputStream& source, int64 numBytesToWrite) @@ -241,7 +259,7 @@ int OutputStream::writeFromInputStream (InputStream& source, int64 numBytesToWri int numWritten = 0; - while (numBytesToWrite > 0 && ! source.isExhausted()) + while (numBytesToWrite > 0) { char buffer [8192]; const int num = source.read (buffer, (int) jmin (numBytesToWrite, (int64) sizeof (buffer))); @@ -249,7 +267,7 @@ int OutputStream::writeFromInputStream (InputStream& source, int64 numBytesToWri if (num <= 0) break; - write (buffer, num); + write (buffer, (size_t) num); numBytesToWrite -= num; numWritten += num; @@ -265,37 +283,53 @@ void OutputStream::setNewLineString (const String& newLineString_) } //============================================================================== -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int number) +template +static void writeIntToStream (OutputStream& stream, IntegerType number) +{ + char buffer [NumberToStringConverters::charsNeededForInt]; + char* end = buffer + numElementsInArray (buffer); + const char* start = NumberToStringConverters::numberToString (end, number); + stream.write (start, (size_t) (end - start - 1)); +} + +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int number) +{ + writeIntToStream (stream, number); + return stream; +} + +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const int64 number) +{ + writeIntToStream (stream, number); + return stream; +} + +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const double number) { return stream << String (number); } -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const double number) -{ - return stream << String (number); -} - -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char character) +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char character) { stream.writeByte (character); return stream; } -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char* const text) +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const char* const text) { - stream.write (text, (int) strlen (text)); + stream.write (text, strlen (text)); return stream; } -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data) +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const MemoryBlock& data) { if (data.getSize() > 0) - stream.write (data.getData(), (int) data.getSize()); + stream.write (data.getData(), data.getSize()); return stream; } -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead) +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead) { FileInputStream in (fileToRead); @@ -305,13 +339,13 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileTo return stream; } -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead) +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead) { stream.writeFromInputStream (streamToRead, -1); return stream; } -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&) +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&) { return stream << stream.getNewLineString(); } diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h index 36f2dcb..de40888 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_OutputStream.h @@ -1,36 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_OUTPUTSTREAM_JUCEHEADER__ -#define __JUCE_OUTPUTSTREAM_JUCEHEADER__ - -#include "../text/juce_String.h" -#include "../text/juce_NewLine.h" -class InputStream; -class MemoryBlock; -class File; +#ifndef JUCE_OUTPUTSTREAM_H_INCLUDED +#define JUCE_OUTPUTSTREAM_H_INCLUDED //============================================================================== @@ -84,83 +81,96 @@ public: types of data which use this to do the work. @param dataToWrite the target buffer to receive the data. This must not be null. - @param numberOfBytes the number of bytes to write. This must not be negative. + @param numberOfBytes the number of bytes to write. @returns false if the write operation fails for some reason */ virtual bool write (const void* dataToWrite, - int numberOfBytes) = 0; + size_t numberOfBytes) = 0; //============================================================================== /** Writes a single byte to the stream. - + @returns false if the write operation fails for some reason @see InputStream::readByte */ - virtual void writeByte (char byte); + virtual bool 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. + @returns false if the write operation fails for some reason @see InputStream::readBool */ - virtual void writeBool (bool boolValue); + virtual bool 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). + @returns false if the write operation fails for some reason @see InputStream::readShort */ - virtual void writeShort (short value); + virtual bool 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). + @returns false if the write operation fails for some reason @see InputStream::readShortBigEndian */ - virtual void writeShortBigEndian (short value); + virtual bool writeShortBigEndian (short value); /** Writes a 32-bit integer to the stream in a little-endian byte order. + @returns false if the write operation fails for some reason @see InputStream::readInt */ - virtual void writeInt (int value); + virtual bool writeInt (int value); /** Writes a 32-bit integer to the stream in a big-endian byte order. + @returns false if the write operation fails for some reason @see InputStream::readIntBigEndian */ - virtual void writeIntBigEndian (int value); + virtual bool writeIntBigEndian (int value); /** Writes a 64-bit integer to the stream in a little-endian byte order. + @returns false if the write operation fails for some reason @see InputStream::readInt64 */ - virtual void writeInt64 (int64 value); + virtual bool writeInt64 (int64 value); /** Writes a 64-bit integer to the stream in a big-endian byte order. + @returns false if the write operation fails for some reason @see InputStream::readInt64BigEndian */ - virtual void writeInt64BigEndian (int64 value); + virtual bool 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. + @returns false if the write operation fails for some reason @see InputStream::readFloat */ - virtual void writeFloat (float value); + virtual bool 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. + @returns false if the write operation fails for some reason @see InputStream::readFloatBigEndian */ - virtual void writeFloatBigEndian (float value); + virtual bool 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. + @returns false if the write operation fails for some reason @see InputStream::readDouble */ - virtual void writeDouble (double value); + virtual bool 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 + @returns false if the write operation fails for some reason */ - virtual void writeDoubleBigEndian (double value); + virtual bool writeDoubleBigEndian (double value); - /** Writes a byte to the output stream a given number of times. */ - virtual void writeRepeatedByte (uint8 byte, int numTimesToRepeat); + /** Writes a byte to the output stream a given number of times. + @returns false if the write operation fails for some reason + */ + virtual bool writeRepeatedByte (uint8 byte, size_t numTimesToRepeat); /** Writes a condensed binary encoding of a 32-bit integer. @@ -170,9 +180,10 @@ public: The format used is: number of significant bytes + up to 4 bytes in little-endian order. + @returns false if the write operation fails for some reason @see InputStream::readCompressedInt */ - virtual void writeCompressedInt (int value); + virtual bool writeCompressedInt (int value); /** Stores a string in the stream in a binary format. @@ -184,9 +195,10 @@ public: For appending text to a file, instead use writeText, or operator<< + @returns false if the write operation fails for some reason @see InputStream::readString, writeText, operator<< */ - virtual void writeString (const String& text); + virtual bool writeString (const String& text); /** Writes a string of text to the stream. @@ -195,8 +207,9 @@ public: of a file). The method also replaces '\\n' characters in the text with '\\r\\n'. + @returns false if the write operation fails for some reason */ - virtual void writeText (const String& text, + virtual bool writeText (const String& text, bool asUTF16, bool writeUTF16ByteOrderMark); @@ -206,13 +219,14 @@ public: @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) + @returns the number of bytes written */ virtual int writeFromInputStream (InputStream& source, int64 maxNumBytesToWrite); //============================================================================== /** Sets the string that will be written to the stream when the writeNewLine() method is called. - By default this will be set the the value of NewLine::getDefault(). + By default this will be set the value of NewLine::getDefault(). */ void setNewLineString (const String& newLineString); @@ -223,30 +237,33 @@ private: //============================================================================== String newLineString; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OutputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OutputStream) }; //============================================================================== /** Writes a number to a stream as 8-bit characters in the default system encoding. */ -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, int number); +JUCE_API 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); +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, int64 number); + +/** Writes a number to a stream as 8-bit characters in the default system encoding. */ +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, double number); /** Writes a character to a stream. */ -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, char character); +JUCE_API 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); +JUCE_API 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); +JUCE_API 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); +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const File& fileToRead); /** Writes the complete contents of an input stream to an output stream. */ -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead); +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& streamToRead); /** Writes a new-line to a stream. You can use the predefined symbol 'newLine' to invoke this, e.g. @@ -255,7 +272,7 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, InputStream& strea @endcode @see OutputStream::setNewLineString */ -OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&); +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const NewLine&); -#endif // __JUCE_OUTPUTSTREAM_JUCEHEADER__ +#endif // JUCE_OUTPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp index 65790b7..c998c79 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp +++ b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.cpp @@ -1,35 +1,37 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ SubregionStream::SubregionStream (InputStream* const sourceStream, - const int64 startPositionInSourceStream_, - const int64 lengthOfSourceStream_, + const int64 start, const int64 length, const bool deleteSourceWhenDestroyed) : source (sourceStream, deleteSourceWhenDestroyed), - startPositionInSourceStream (startPositionInSourceStream_), - lengthOfSourceStream (lengthOfSourceStream_) + startPositionInSourceStream (start), + lengthOfSourceStream (length) { SubregionStream::setPosition (0); } @@ -42,8 +44,8 @@ int64 SubregionStream::getTotalLength() { const int64 srcLen = source->getTotalLength() - startPositionInSourceStream; - return (lengthOfSourceStream >= 0) ? jmin (lengthOfSourceStream, srcLen) - : srcLen; + return lengthOfSourceStream >= 0 ? jmin (lengthOfSourceStream, srcLen) + : srcLen; } int64 SubregionStream::getPosition() @@ -61,24 +63,20 @@ int SubregionStream::read (void* destBuffer, int maxBytesToRead) jassert (destBuffer != nullptr && maxBytesToRead >= 0); if (lengthOfSourceStream < 0) - { return source->read (destBuffer, maxBytesToRead); - } - else - { - maxBytesToRead = (int) jmin ((int64) maxBytesToRead, lengthOfSourceStream - getPosition()); - if (maxBytesToRead <= 0) - return 0; + maxBytesToRead = (int) jmin ((int64) maxBytesToRead, lengthOfSourceStream - getPosition()); - return source->read (destBuffer, maxBytesToRead); - } + if (maxBytesToRead <= 0) + return 0; + + return source->read (destBuffer, maxBytesToRead); } bool SubregionStream::isExhausted() { - if (lengthOfSourceStream >= 0) - return (getPosition() >= lengthOfSourceStream) || source->isExhausted(); - else - return source->isExhausted(); + if (lengthOfSourceStream >= 0 && getPosition() >= lengthOfSourceStream) + return true; + + return source->isExhausted(); } diff --git a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h index 2b3a712..d291a03 100644 --- a/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h +++ b/JuceLibraryCode/modules/juce_core/streams/juce_SubregionStream.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SUBREGIONSTREAM_JUCEHEADER__ -#define __JUCE_SUBREGIONSTREAM_JUCEHEADER__ - -#include "juce_InputStream.h" -#include "../memory/juce_OptionalScopedPointer.h" +#ifndef JUCE_SUBREGIONSTREAM_H_INCLUDED +#define JUCE_SUBREGIONSTREAM_H_INCLUDED //============================================================================== @@ -71,19 +71,18 @@ public: //============================================================================== - int64 getTotalLength(); - int64 getPosition(); - bool setPosition (int64 newPosition); - int read (void* destBuffer, int maxBytesToRead); - bool isExhausted(); + int64 getTotalLength() override; + int64 getPosition() override; + bool setPosition (int64 newPosition) override; + int read (void* destBuffer, int maxBytesToRead) override; + bool isExhausted() override; - - //============================================================================== private: + //============================================================================== OptionalScopedPointer source; const int64 startPositionInSourceStream, lengthOfSourceStream; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubregionStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubregionStream) }; -#endif // __JUCE_SUBREGIONSTREAM_JUCEHEADER__ +#endif // JUCE_SUBREGIONSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h b/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h index 353e7d5..6f59979 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_PlatformDefs.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_PLATFORMDEFS_JUCEHEADER__ -#define __JUCE_PLATFORMDEFS_JUCEHEADER__ +#ifndef JUCE_PLATFORMDEFS_H_INCLUDED +#define JUCE_PLATFORMDEFS_H_INCLUDED //============================================================================== /* This file defines miscellaneous macros for debugging, assertions, etc. @@ -51,10 +54,8 @@ //============================================================================== // Debugging and assertion macros -#if JUCE_LOG_ASSERTIONS +#if JUCE_LOG_ASSERTIONS || JUCE_DEBUG #define juce_LogCurrentAssertion juce::logAssertion (__FILE__, __LINE__); -#elif JUCE_DEBUG - #define juce_LogCurrentAssertion std::cerr << "JUCE Assertion failure in " << __FILE__ << ", line " << __LINE__ << std::endl; #else #define juce_LogCurrentAssertion #endif @@ -73,11 +74,25 @@ #endif #define juce_breakDebugger { __debugbreak(); } #elif JUCE_GCC || JUCE_MAC - #define juce_breakDebugger { asm ("int $3"); } + #if JUCE_NO_INLINE_ASM + #define juce_breakDebugger { } + #else + #define juce_breakDebugger { asm ("int $3"); } + #endif #else #define juce_breakDebugger { __asm int 3 } #endif +#if JUCE_CLANG && defined (__has_feature) && ! defined (JUCE_ANALYZER_NORETURN) + #if __has_feature (attribute_analyzer_noreturn) + inline void __attribute__((analyzer_noreturn)) juce_assert_noreturn() {} + #define JUCE_ANALYZER_NORETURN juce_assert_noreturn(); + #endif +#endif + +#ifndef JUCE_ANALYZER_NORETURN + #define JUCE_ANALYZER_NORETURN +#endif //============================================================================== #if JUCE_DEBUG || DOXYGEN @@ -92,7 +107,7 @@ It is only compiled in a debug build, (unless JUCE_LOG_ASSERTIONS is enabled for your build). @see jassert */ - #define jassertfalse { juce_LogCurrentAssertion; if (juce::juce_isRunningUnderDebugger()) juce_breakDebugger; } + #define jassertfalse { juce_LogCurrentAssertion; if (juce::juce_isRunningUnderDebugger()) juce_breakDebugger; JUCE_ANALYZER_NORETURN } //============================================================================== /** Platform-independent assertion macro. @@ -156,18 +171,18 @@ namespace juce etc.. private: - JUCE_DECLARE_NON_COPYABLE (MyClass); + JUCE_DECLARE_NON_COPYABLE (MyClass) };@endcode */ #define JUCE_DECLARE_NON_COPYABLE(className) \ - className (const className&);\ - className& operator= (const className&) + className (const className&) JUCE_DELETED_FUNCTION;\ + className& operator= (const className&) JUCE_DELETED_FUNCTION; /** This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for a class. */ #define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className) \ - JUCE_DECLARE_NON_COPYABLE(className);\ + JUCE_DECLARE_NON_COPYABLE(className) \ JUCE_LEAK_DETECTOR(className) /** This macro can be added to class definitions to disable the use of new/delete to @@ -175,8 +190,8 @@ namespace juce */ #define JUCE_PREVENT_HEAP_ALLOCATION \ private: \ - static void* operator new (size_t); \ - static void operator delete (void*); + static void* operator new (size_t) JUCE_DELETED_FUNCTION; \ + static void operator delete (void*) JUCE_DELETED_FUNCTION; //============================================================================== @@ -207,17 +222,17 @@ namespace juce #if ! JUCE_MODULE_AVAILABLE_juce_gui_basics #define JUCE_CATCH_EXCEPTION JUCE_CATCH_ALL #else - /** Used in try-catch blocks, this macro will send exceptions to the JUCEApplication + /** Used in try-catch blocks, this macro will send exceptions to the JUCEApplicationBase object so they can be logged by the application if it wants to. */ #define JUCE_CATCH_EXCEPTION \ catch (const std::exception& e) \ { \ - JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__); \ + juce::JUCEApplicationBase::sendUnhandledException (&e, __FILE__, __LINE__); \ } \ catch (...) \ { \ - JUCEApplication::sendUnhandledException (nullptr, __FILE__, __LINE__); \ + juce::JUCEApplicationBase::sendUnhandledException (nullptr, __FILE__, __LINE__); \ } #endif @@ -256,34 +271,55 @@ namespace juce //============================================================================== // Cross-compiler deprecation macros.. -#if DOXYGEN || (JUCE_MSVC && ! JUCE_NO_DEPRECATION_WARNINGS) - /** This can be used to wrap a function which has been deprecated. */ - #define JUCE_DEPRECATED(functionDef) __declspec(deprecated) functionDef -#elif JUCE_GCC && ! JUCE_NO_DEPRECATION_WARNINGS - #define JUCE_DEPRECATED(functionDef) functionDef __attribute__ ((deprecated)) +#ifdef DOXYGEN + /** This macro can be used to wrap a function which has been deprecated. */ + #define JUCE_DEPRECATED(functionDef) + #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) +#elif JUCE_MSVC && ! JUCE_NO_DEPRECATION_WARNINGS + #define JUCE_DEPRECATED(functionDef) __declspec(deprecated) functionDef + #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) __declspec(deprecated) functionDef body +#elif JUCE_GCC && ! JUCE_NO_DEPRECATION_WARNINGS + #define JUCE_DEPRECATED(functionDef) functionDef __attribute__ ((deprecated)) + #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef __attribute__ ((deprecated)) body #else - #define JUCE_DEPRECATED(functionDef) functionDef + #define JUCE_DEPRECATED(functionDef) functionDef + #define JUCE_DEPRECATED_WITH_BODY(functionDef, body) functionDef body #endif //============================================================================== #if JUCE_ANDROID && ! DOXYGEN #define JUCE_MODAL_LOOPS_PERMITTED 0 -#else +#elif ! defined (JUCE_MODAL_LOOPS_PERMITTED) /** Some operating environments don't provide a modal loop mechanism, so this flag can be used to disable any functions that try to run a modal loop. */ #define JUCE_MODAL_LOOPS_PERMITTED 1 #endif +//============================================================================== +#if JUCE_GCC + #define JUCE_PACKED __attribute__((packed)) +#elif ! DOXYGEN + #define JUCE_PACKED +#endif + //============================================================================== // Here, we'll check for C++11 compiler support, and if it's not available, define // a few workarounds, so that we can still use some of the newer language features. -#if defined (__GXX_EXPERIMENTAL_CXX0X__) && defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#if (__cplusplus >= 201103L || defined (__GXX_EXPERIMENTAL_CXX0X__)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 + + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL) + #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 + #endif + + #if (__GNUC__ * 100 + __GNUC_MINOR__) >= 407 && ! defined (JUCE_DELETED_FUNCTION) + #define JUCE_DELETED_FUNCTION = delete + #endif #endif -#if defined (__clang__) && defined (__has_feature) +#if JUCE_CLANG && defined (__has_feature) #if __has_feature (cxx_nullptr) #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 #endif @@ -295,26 +331,55 @@ namespace juce #if __has_feature (cxx_rvalue_references) #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 #endif + + #if __has_feature (cxx_deleted_functions) + #define JUCE_DELETED_FUNCTION = delete + #endif + + #ifndef JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL + #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 + #endif + + #ifndef JUCE_COMPILER_SUPPORTS_ARC + #define JUCE_COMPILER_SUPPORTS_ARC 1 + #endif #endif #if defined (_MSC_VER) && _MSC_VER >= 1600 - #if _MSC_VER >= 1700 - #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 1 - #else - #define JUCE_COMPILER_SUPPORTS_NOEXCEPT 0 - #endif #define JUCE_COMPILER_SUPPORTS_NULLPTR 1 #define JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS 1 #endif +#if defined (_MSC_VER) && _MSC_VER >= 1700 + #define JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL 1 +#endif + +#ifndef JUCE_DELETED_FUNCTION + #define JUCE_DELETED_FUNCTION +#endif + //============================================================================== // Declare some fake versions of nullptr and noexcept, for older compilers: #if ! (DOXYGEN || JUCE_COMPILER_SUPPORTS_NOEXCEPT) + #ifdef noexcept + #undef noexcept + #endif #define noexcept throw() + #if defined (_MSC_VER) && _MSC_VER > 1600 + #define _ALLOW_KEYWORD_MACROS 1 // (to stop VC2012 complaining) + #endif #endif #if ! (DOXYGEN || JUCE_COMPILER_SUPPORTS_NULLPTR) + #ifdef nullptr + #undef nullptr + #endif #define nullptr (0) #endif -#endif // __JUCE_PLATFORMDEFS_JUCEHEADER__ +#if ! (DOXYGEN || JUCE_COMPILER_SUPPORTS_OVERRIDE_AND_FINAL) + #undef override + #define override +#endif + +#endif // JUCE_PLATFORMDEFS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index aaf0533..edd6a54 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -1,39 +1,42 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_STANDARDHEADER_JUCEHEADER__ -#define __JUCE_STANDARDHEADER_JUCEHEADER__ +#ifndef JUCE_STANDARDHEADER_H_INCLUDED +#define JUCE_STANDARDHEADER_H_INCLUDED //============================================================================== /** Current JUCE version number. See also SystemStats::getJUCEVersion() for a string version. */ -#define JUCE_MAJOR_VERSION 2 +#define JUCE_MAJOR_VERSION 3 #define JUCE_MINOR_VERSION 0 -#define JUCE_BUILDNUMBER 21 +#define JUCE_BUILDNUMBER 6 /** Current Juce version number. @@ -43,24 +46,14 @@ See also SystemStats::getJUCEVersion() for a string version. */ -#define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) +#define JUCE_VERSION ((JUCE_MAJOR_VERSION << 16) + (JUCE_MINOR_VERSION << 8) + JUCE_BUILDNUMBER) -//============================================================================== -#include "juce_TargetPlatform.h" // (sets up the various JUCE_WINDOWS, JUCE_MAC, etc flags) - -//============================================================================== -#ifndef DOXYGEN - // These are old macros that are now deprecated: you should just use the juce namespace directly. - #define JUCE_NAMESPACE juce - #define BEGIN_JUCE_NAMESPACE namespace juce { - #define END_JUCE_NAMESPACE } -#endif - //============================================================================== #include "juce_PlatformDefs.h" -// Now we'll include any OS headers we need.. (at this point we are outside the Juce namespace). +//============================================================================== +// Now we'll include some common OS headers.. #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4245 4100) @@ -78,6 +71,7 @@ #include #include #include +#include #if JUCE_USE_INTRINSICS #include @@ -112,6 +106,12 @@ #include #endif +// undef symbols that are sometimes set by misguided 3rd-party headers.. +#undef check +#undef TYPE_BOOL +#undef max +#undef min + //============================================================================== // DLL building settings on Windows #if JUCE_MSVC @@ -125,76 +125,33 @@ #ifdef __INTEL_COMPILER #pragma warning (disable: 1125) // (virtual override warning) #endif -#elif defined (__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) - #ifdef JUCE_DLL_BUILD - #define JUCE_API __attribute__ ((visibility("default"))) - #endif +#elif defined (JUCE_DLL) || defined (JUCE_DLL_BUILD) + #define JUCE_API __attribute__ ((visibility("default"))) #endif +//============================================================================== #ifndef JUCE_API - /** This macro is added to all juce public class declarations. */ - #define JUCE_API + #define JUCE_API /**< This macro is added to all juce public class declarations. */ +#endif + +#if JUCE_MSVC && JUCE_DLL_BUILD + #define JUCE_PUBLIC_IN_DLL_BUILD(declaration) public: declaration; private: +#else + #define JUCE_PUBLIC_IN_DLL_BUILD(declaration) declaration; #endif /** This macro is added to all juce public function declarations. */ #define JUCE_PUBLIC_FUNCTION JUCE_API JUCE_CALLTYPE -/** This turns on some non-essential bits of code that should prevent old code from compiling - in cases where method signatures have changed, etc. -*/ #if (! defined (JUCE_CATCH_DEPRECATED_CODE_MISUSE)) && JUCE_DEBUG && ! DOXYGEN + /** This turns on some non-essential bits of code that should prevent old code from compiling + in cases where method signatures have changed, etc. + */ #define JUCE_CATCH_DEPRECATED_CODE_MISUSE 1 #endif -//============================================================================== -// Now include some basics that are needed by most of the Juce classes... -BEGIN_JUCE_NAMESPACE - -extern JUCE_API bool JUCE_CALLTYPE juce_isRunningUnderDebugger(); - -#if JUCE_LOG_ASSERTIONS - extern JUCE_API void logAssertion (const char* filename, int lineNum) noexcept; +#ifndef DOXYGEN + #define JUCE_NAMESPACE juce // This old macro is deprecated: you should just use the juce namespace directly. #endif -#undef max -#undef min - -#include "../memory/juce_Memory.h" -#include "../maths/juce_MathsFunctions.h" -#include "../memory/juce_ByteOrder.h" -#include "../logging/juce_Logger.h" -#include "../memory/juce_LeakedObjectDetector.h" - -// unbelievably, some system headers actually use macros to define these symbols: -#undef check -#undef TYPE_BOOL - -//============================================================================== -#if JUCE_MAC || JUCE_IOS || DOXYGEN - - /** A handy C++ wrapper that creates and deletes an NSAutoreleasePool object using RAII. - You should use the JUCE_AUTORELEASEPOOL macro to create a local auto-release pool on the stack. - */ - class JUCE_API ScopedAutoReleasePool - { - public: - ScopedAutoReleasePool(); - ~ScopedAutoReleasePool(); - - private: - void* pool; - - JUCE_DECLARE_NON_COPYABLE (ScopedAutoReleasePool); - }; - - /** A macro that can be used to easily declare a local ScopedAutoReleasePool object for RAII-based obj-C autoreleasing. */ - #define JUCE_AUTORELEASEPOOL const juce::ScopedAutoReleasePool JUCE_JOIN_MACRO (autoReleasePool_, __LINE__); - -#else - #define JUCE_AUTORELEASEPOOL -#endif - -END_JUCE_NAMESPACE - - -#endif // __JUCE_STANDARDHEADER_JUCEHEADER__ +#endif // JUCE_STANDARDHEADER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp index 78118ae..365e94d 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.cpp @@ -1,34 +1,31 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -const SystemStats::CPUFlags& SystemStats::getCPUFlags() -{ - static CPUFlags cpuFlags; - return cpuFlags; -} - String SystemStats::getJUCEVersion() { // Some basic tests, to keep an eye on things and make sure these types work ok @@ -48,7 +45,11 @@ String SystemStats::getJUCEVersion() "." JUCE_STRINGIFY(JUCE_BUILDNUMBER); } -#if JUCE_DEBUG && ! JUCE_ANDROID +#if JUCE_ANDROID && ! defined (JUCE_DISABLE_JUCE_VERSION_PRINTING) + #define JUCE_DISABLE_JUCE_VERSION_PRINTING 1 +#endif + +#if JUCE_DEBUG && ! JUCE_DISABLE_JUCE_VERSION_PRINTING struct JuceVersionPrinter { JuceVersionPrinter() @@ -59,3 +60,124 @@ String SystemStats::getJUCEVersion() static JuceVersionPrinter juceVersionPrinter; #endif + + +//============================================================================== +struct CPUInformation +{ + CPUInformation() noexcept + : numCpus (0), hasMMX (false), hasSSE (false), + hasSSE2 (false), hasSSE3 (false), has3DNow (false) + { + initialise(); + } + + void initialise() noexcept; + + int numCpus; + bool hasMMX, hasSSE, hasSSE2, hasSSE3, has3DNow; +}; + +static const CPUInformation& getCPUInformation() noexcept +{ + static CPUInformation info; + return info; +} + +int SystemStats::getNumCpus() noexcept { return getCPUInformation().numCpus; } +bool SystemStats::hasMMX() noexcept { return getCPUInformation().hasMMX; } +bool SystemStats::hasSSE() noexcept { return getCPUInformation().hasSSE; } +bool SystemStats::hasSSE2() noexcept { return getCPUInformation().hasSSE2; } +bool SystemStats::hasSSE3() noexcept { return getCPUInformation().hasSSE3; } +bool SystemStats::has3DNow() noexcept { return getCPUInformation().has3DNow; } + + +//============================================================================== +String SystemStats::getStackBacktrace() +{ + String result; + + #if JUCE_ANDROID || JUCE_MINGW + jassertfalse; // sorry, not implemented yet! + + #elif JUCE_WINDOWS + HANDLE process = GetCurrentProcess(); + SymInitialize (process, nullptr, TRUE); + + void* stack[128]; + int frames = (int) CaptureStackBackTrace (0, numElementsInArray (stack), stack, nullptr); + + HeapBlock symbol; + symbol.calloc (sizeof (SYMBOL_INFO) + 256, 1); + symbol->MaxNameLen = 255; + symbol->SizeOfStruct = sizeof (SYMBOL_INFO); + + for (int i = 0; i < frames; ++i) + { + DWORD64 displacement = 0; + + if (SymFromAddr (process, (DWORD64) stack[i], &displacement, symbol)) + { + result << i << ": "; + + IMAGEHLP_MODULE64 moduleInfo; + zerostruct (moduleInfo); + moduleInfo.SizeOfStruct = sizeof (moduleInfo); + + if (::SymGetModuleInfo64 (process, symbol->ModBase, &moduleInfo)) + result << moduleInfo.ModuleName << ": "; + + result << symbol->Name << " + 0x" << String::toHexString ((int64) displacement) << newLine; + } + } + + #else + void* stack[128]; + int frames = backtrace (stack, numElementsInArray (stack)); + char** frameStrings = backtrace_symbols (stack, frames); + + for (int i = 0; i < frames; ++i) + result << frameStrings[i] << newLine; + + ::free (frameStrings); + #endif + + return result; +} + +//============================================================================== +static SystemStats::CrashHandlerFunction globalCrashHandler = nullptr; + +#if JUCE_WINDOWS +static LONG WINAPI handleCrash (LPEXCEPTION_POINTERS) +{ + globalCrashHandler(); + return EXCEPTION_EXECUTE_HANDLER; +} +#else +static void handleCrash (int) +{ + globalCrashHandler(); + kill (getpid(), SIGKILL); +} + +int juce_siginterrupt (int sig, int flag); +#endif + +void SystemStats::setApplicationCrashHandler (CrashHandlerFunction handler) +{ + jassert (handler != nullptr); // This must be a valid function. + globalCrashHandler = handler; + + #if JUCE_WINDOWS + SetUnhandledExceptionFilter (handleCrash); + #else + const int signals[] = { SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGABRT, SIGSYS }; + + for (int i = 0; i < numElementsInArray (signals); ++i) + { + ::signal (signals[i], handleCrash); + juce_siginterrupt (signals[i], 1); + } + #endif +} diff --git a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h index 7d67ace..833bf5a 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_SystemStats.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SYSTEMSTATS_JUCEHEADER__ -#define __JUCE_SYSTEMSTATS_JUCEHEADER__ - -#include "../text/juce_StringArray.h" +#ifndef JUCE_SYSTEMSTATS_H_INCLUDED +#define JUCE_SYSTEMSTATS_H_INCLUDED //============================================================================== @@ -38,35 +39,35 @@ class JUCE_API SystemStats public: //============================================================================== /** Returns the current version of JUCE, - See also the JUCE_VERSION, JUCE_MAJOR_VERSION and JUCE_MINOR_VERSION macros. */ static String getJUCEVersion(); //============================================================================== - /** The set of possible results of the getOperatingSystemType() method. - */ + /** The set of possible results of the getOperatingSystemType() method. */ enum OperatingSystemType { UnknownOS = 0, - MacOSX = 0x1000, Linux = 0x2000, Android = 0x3000, + iOS = 0x8000, + + MacOSX_10_4 = 0x1004, + MacOSX_10_5 = 0x1005, + MacOSX_10_6 = 0x1006, + MacOSX_10_7 = 0x1007, + MacOSX_10_8 = 0x1008, + MacOSX_10_9 = 0x1009, - Win95 = 0x4001, - Win98 = 0x4002, - WinNT351 = 0x4103, - WinNT40 = 0x4104, Win2000 = 0x4105, WinXP = 0x4106, WinVista = 0x4107, Windows7 = 0x4108, + Windows8 = 0x4109, Windows = 0x4000, /**< To test whether any version of Windows is running, you can use the expression ((getOperatingSystemType() & Windows) != 0). */ - WindowsNT = 0x0100, /**< To test whether the platform is Windows NT or later (i.e. not Win95 or 98), - you can use the expression ((getOperatingSystemType() & WindowsNT) != 0). */ }; /** Returns the type of operating system we're running on. @@ -83,16 +84,13 @@ public: */ static String getOperatingSystemName(); - /** Returns true if the OS is 64-bit, or false for a 32-bit OS. - */ + /** Returns true if the OS is 64-bit, or false for a 32-bit OS. */ static bool isOperatingSystem64Bit(); - #if JUCE_MAC || DOXYGEN - /** OSX 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. + /** Returns an environment variable. + If the named value isn't set, this will return the defaultValue string instead. */ - static int getOSXMinorVersionNumber(); - #endif + static String getEnvironmentVariable (const String& name, const String& defaultValue); //============================================================================== /** Returns the current user's name, if available. @@ -109,70 +107,88 @@ public: /** Returns the host-name of the computer. */ static String getComputerName(); + /** Returns the language of the user's locale. + The return value is a 2 or 3 letter language code (ISO 639-1 or ISO 639-2) + */ + static String getUserLanguage(); + + /** Returns the region of the user's locale. + The return value is a 2 letter country code (ISO 3166-1 alpha-2). + */ + static String getUserRegion(); + + /** Returns the user's display language. + The return value is a 2 or 3 letter language code (ISO 639-1 or ISO 639-2). + Note that depending on the OS and region, this may also be followed by a dash + and a sub-region code, e.g "en-GB" + */ + static String getDisplayLanguage(); + + /** This will attempt to return some kind of string describing the device. + If no description is available, it'll just return an empty string. You may + want to use this for things like determining the type of phone/iPad, etc. + */ + static String getDeviceDescription(); + //============================================================================== // CPU and memory information.. - /** Returns the number of CPUs. */ - static int getNumCpus() noexcept { return getCPUFlags().numCpus; } + /** Returns the number of CPU cores. */ + static int getNumCpus() noexcept; /** 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(); /** Returns a string to indicate the CPU vendor. - Might not be known on some systems. */ static String getCpuVendor(); - /** Checks whether Intel MMX instructions are available. */ - static bool hasMMX() noexcept { return getCPUFlags().hasMMX; } - - /** Checks whether Intel SSE instructions are available. */ - static bool hasSSE() noexcept { return getCPUFlags().hasSSE; } - - /** Checks whether Intel SSE2 instructions are available. */ - static bool hasSSE2() noexcept { return getCPUFlags().hasSSE2; } - - /** Checks whether AMD 3DNOW instructions are available. */ - static bool has3DNow() noexcept { return getCPUFlags().has3DNow; } + static bool hasMMX() noexcept; /**< Returns true if Intel MMX instructions are available. */ + static bool hasSSE() noexcept; /**< Returns true if Intel SSE instructions are available. */ + static bool hasSSE2() noexcept; /**< Returns true if Intel SSE2 instructions are available. */ + static bool hasSSE3() noexcept; /**< Returns true if Intel SSE2 instructions are available. */ + static bool has3DNow() noexcept; /**< Returns true if AMD 3DNOW instructions are available. */ //============================================================================== /** 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(); /** Returns the system page-size. - This is only used by programmers with beards. */ static int getPageSize(); + //============================================================================== + /** Returns a backtrace of the current call-stack. + The usefulness of the result will depend on the level of debug symbols + that are available in the executable. + */ + static String getStackBacktrace(); + + /** A void() function type, used by setApplicationCrashHandler(). */ + typedef void (*CrashHandlerFunction)(); + + /** Sets up a global callback function that will be called if the application + executes some kind of illegal instruction. + + You may want to call getStackBacktrace() in your handler function, to find out + where the problem happened and log it, etc. + */ + static void setApplicationCrashHandler (CrashHandlerFunction); private: //============================================================================== - struct CPUFlags - { - CPUFlags(); - - int numCpus; - bool hasMMX : 1; - bool hasSSE : 1; - bool hasSSE2 : 1; - bool has3DNow : 1; - }; - SystemStats(); - static const CPUFlags& getCPUFlags(); - JUCE_DECLARE_NON_COPYABLE (SystemStats); + JUCE_DECLARE_NON_COPYABLE (SystemStats) }; -#endif // __JUCE_SYSTEMSTATS_JUCEHEADER__ +#endif // JUCE_SYSTEMSTATS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h b/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h index 1351722..86adc38 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_TargetPlatform.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_TARGETPLATFORM_JUCEHEADER__ -#define __JUCE_TARGETPLATFORM_JUCEHEADER__ +#ifndef JUCE_TARGETPLATFORM_H_INCLUDED +#define JUCE_TARGETPLATFORM_H_INCLUDED //============================================================================== /* This file figures out which platform is being built, and defines some macros @@ -61,6 +64,8 @@ #else #define JUCE_MAC 1 #endif +#elif defined (__FreeBSD__) + #define JUCE_BSD 1 #else #error "Unknown platform!" #endif @@ -81,6 +86,11 @@ #ifdef __MINGW32__ #define JUCE_MINGW 1 + #ifdef __MINGW64__ + #define JUCE_64BIT 1 + #else + #define JUCE_32BIT 1 + #endif #endif /** If defined, this indicates that the processor is little-endian. */ @@ -105,15 +115,6 @@ #else #define JUCE_BIG_ENDIAN 1 #endif -#endif - -#if JUCE_MAC - - #if defined (__ppc__) || defined (__ppc64__) - #define JUCE_PPC 1 - #else - #define JUCE_INTEL 1 - #endif #ifdef __LP64__ #define JUCE_64BIT 1 @@ -121,14 +122,21 @@ #define JUCE_32BIT 1 #endif - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 + #if defined (__ppc__) || defined (__ppc64__) + #define JUCE_PPC 1 + #elif defined (__arm__) || defined (__arm64__) + #define JUCE_ARM 1 + #else + #define JUCE_INTEL 1 + #endif + + #if JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 #error "Building for OSX 10.3 is no longer supported!" #endif - #ifndef MAC_OS_X_VERSION_10_5 + #if JUCE_MAC && ! defined (MAC_OS_X_VERSION_10_5) #error "To build with 10.4 compatibility, use a 10.5 or 10.6 SDK and set the deployment target to 10.4" #endif - #endif //============================================================================== @@ -153,7 +161,9 @@ #define JUCE_32BIT 1 #endif - #if __MMX__ || __SSE__ || __amd64__ + #if defined (__arm__) || defined (__arm64__) + #define JUCE_ARM 1 + #elif __MMX__ || __SSE__ || __amd64__ #define JUCE_INTEL 1 #endif #endif @@ -161,7 +171,10 @@ //============================================================================== // Compiler type macros. -#ifdef __GNUC__ +#ifdef __clang__ + #define JUCE_CLANG 1 + #define JUCE_GCC 1 +#elif defined (__GNUC__) #define JUCE_GCC 1 #elif defined (_MSC_VER) #define JUCE_MSVC 1 @@ -185,5 +198,4 @@ #error unknown compiler #endif - -#endif // __JUCE_TARGETPLATFORM_JUCEHEADER__ +#endif // JUCE_TARGETPLATFORM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h index 66630ad..a966543 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_ASCII.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ -#define __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ +#ifndef JUCE_CHARPOINTER_ASCII_H_INCLUDED +#define JUCE_CHARPOINTER_ASCII_H_INCLUDED //============================================================================== @@ -51,25 +54,25 @@ public: { } - inline CharPointer_ASCII& operator= (const CharPointer_ASCII& other) noexcept + inline CharPointer_ASCII operator= (const CharPointer_ASCII other) noexcept { data = other.data; return *this; } - inline CharPointer_ASCII& operator= (const CharType* text) noexcept + inline CharPointer_ASCII operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ - inline bool operator== (const CharPointer_ASCII& other) const noexcept { return data == other.data; } - inline bool operator!= (const CharPointer_ASCII& other) const noexcept { return data != other.data; } - inline bool operator<= (const CharPointer_ASCII& other) const noexcept { return data <= other.data; } - inline bool operator< (const CharPointer_ASCII& other) const noexcept { return data < other.data; } - inline bool operator>= (const CharPointer_ASCII& other) const noexcept { return data >= other.data; } - inline bool operator> (const CharPointer_ASCII& other) const noexcept { return data > other.data; } + inline bool operator== (CharPointer_ASCII other) const noexcept { return data == other.data; } + inline bool operator!= (CharPointer_ASCII other) const noexcept { return data != other.data; } + inline bool operator<= (CharPointer_ASCII other) const noexcept { return data <= other.data; } + inline bool operator< (CharPointer_ASCII other) const noexcept { return data < other.data; } + inline bool operator>= (CharPointer_ASCII other) const noexcept { return data >= other.data; } + inline bool operator> (CharPointer_ASCII other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } @@ -84,14 +87,14 @@ public: inline juce_wchar operator*() const noexcept { return (juce_wchar) (uint8) *data; } /** Moves this pointer along to the next character in the string. */ - inline CharPointer_ASCII& operator++() noexcept + inline CharPointer_ASCII operator++() noexcept { ++data; return *this; } /** Moves this pointer to the previous character in the string. */ - inline CharPointer_ASCII& operator--() noexcept + inline CharPointer_ASCII operator--() noexcept { --data; return *this; @@ -168,7 +171,7 @@ public: } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ - size_t lengthUpTo (const CharPointer_ASCII& end) const noexcept + size_t lengthUpTo (const CharPointer_ASCII end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } @@ -194,7 +197,7 @@ public: The value returned does NOT include the terminating null character. */ template - static size_t getBytesRequiredFor (const CharPointer& text) noexcept + static size_t getBytesRequiredFor (const CharPointer text) noexcept { return text.length(); } @@ -207,13 +210,13 @@ public: /** Copies a source string to this pointer, advancing this pointer as it goes. */ template - void writeAll (const CharPointer& src) noexcept + void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ - void writeAll (const CharPointer_ASCII& src) noexcept + void writeAll (const CharPointer_ASCII src) noexcept { strcpy (data, src.data); } @@ -223,7 +226,7 @@ public: to the destination buffer before stopping. */ template - int writeWithDestByteLimit (const CharPointer& src, const int maxDestBytes) noexcept + size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } @@ -233,45 +236,45 @@ public: written to the destination buffer before stopping (including the terminating null). */ template - void writeWithCharLimit (const CharPointer& src, const int maxChars) noexcept + void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template - int compare (const CharPointer& other) const noexcept + int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } /** Compares this string with another one. */ - int compare (const CharPointer_ASCII& other) const noexcept + int compare (const CharPointer_ASCII other) const noexcept { return strcmp (data, other.data); } /** Compares this string with another one, up to a specified number of characters. */ template - int compareUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one, up to a specified number of characters. */ - int compareUpTo (const CharPointer_ASCII& other, const int maxChars) const noexcept + int compareUpTo (const CharPointer_ASCII other, const int maxChars) const noexcept { return strncmp (data, other.data, (size_t) maxChars); } /** Compares this string with another one. */ template - int compareIgnoreCase (const CharPointer& other) const + int compareIgnoreCase (const CharPointer other) const { return CharacterFunctions::compareIgnoreCase (*this, other); } - int compareIgnoreCase (const CharPointer_ASCII& other) const + int compareIgnoreCase (const CharPointer_ASCII other) const { #if JUCE_WINDOWS return stricmp (data, other.data); @@ -282,14 +285,14 @@ public: /** Compares this string with another one, up to a specified number of characters. */ template - int compareIgnoreCaseUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } /** Returns the character index of a substring, or -1 if it isn't found. */ template - int indexOf (const CharPointer& stringToFind) const noexcept + int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } @@ -381,4 +384,4 @@ private: }; -#endif // __JUCE_CHARPOINTER_ASCII_JUCEHEADER__ +#endif // JUCE_CHARPOINTER_ASCII_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h index c44fb8e..d0eba5c 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF16.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ -#define __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ +#ifndef JUCE_CHARPOINTER_UTF16_H_INCLUDED +#define JUCE_CHARPOINTER_UTF16_H_INCLUDED //============================================================================== @@ -52,25 +55,25 @@ public: { } - inline CharPointer_UTF16& operator= (const CharPointer_UTF16& other) noexcept + inline CharPointer_UTF16 operator= (CharPointer_UTF16 other) noexcept { data = other.data; return *this; } - inline CharPointer_UTF16& operator= (const CharType* text) noexcept + inline CharPointer_UTF16 operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ - inline bool operator== (const CharPointer_UTF16& other) const noexcept { return data == other.data; } - inline bool operator!= (const CharPointer_UTF16& other) const noexcept { return data != other.data; } - inline bool operator<= (const CharPointer_UTF16& other) const noexcept { return data <= other.data; } - inline bool operator< (const CharPointer_UTF16& other) const noexcept { return data < other.data; } - inline bool operator>= (const CharPointer_UTF16& other) const noexcept { return data >= other.data; } - inline bool operator> (const CharPointer_UTF16& other) const noexcept { return data > other.data; } + inline bool operator== (CharPointer_UTF16 other) const noexcept { return data == other.data; } + inline bool operator!= (CharPointer_UTF16 other) const noexcept { return data != other.data; } + inline bool operator<= (CharPointer_UTF16 other) const noexcept { return data <= other.data; } + inline bool operator< (CharPointer_UTF16 other) const noexcept { return data < other.data; } + inline bool operator>= (CharPointer_UTF16 other) const noexcept { return data >= other.data; } + inline bool operator> (CharPointer_UTF16 other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } @@ -93,7 +96,7 @@ public: } /** Moves this pointer along to the next character in the string. */ - CharPointer_UTF16& operator++() noexcept + CharPointer_UTF16 operator++() noexcept { const juce_wchar n = *data++; @@ -104,7 +107,7 @@ public: } /** Moves this pointer back to the previous character in the string. */ - CharPointer_UTF16& operator--() noexcept + CharPointer_UTF16 operator--() noexcept { const juce_wchar n = *--data; @@ -231,7 +234,7 @@ public: } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ - size_t lengthUpTo (const CharPointer_UTF16& end) const noexcept + size_t lengthUpTo (const CharPointer_UTF16 end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } @@ -281,13 +284,13 @@ public: /** Copies a source string to this pointer, advancing this pointer as it goes. */ template - void writeAll (const CharPointer& src) noexcept + void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ - void writeAll (const CharPointer_UTF16& src) noexcept + void writeAll (const CharPointer_UTF16 src) noexcept { const CharType* s = src.data; @@ -303,7 +306,7 @@ public: to the destination buffer before stopping. */ template - int writeWithDestByteLimit (const CharPointer& src, const int maxDestBytes) noexcept + size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } @@ -313,51 +316,51 @@ public: written to the destination buffer before stopping (including the terminating null). */ template - void writeWithCharLimit (const CharPointer& src, const int maxChars) noexcept + void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template - int compare (const CharPointer& other) const noexcept + int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template - int compareUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one. */ template - int compareIgnoreCase (const CharPointer& other) const noexcept + int compareIgnoreCase (const CharPointer other) const noexcept { return CharacterFunctions::compareIgnoreCase (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template - int compareIgnoreCaseUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } #if JUCE_WINDOWS && ! DOXYGEN - int compareIgnoreCase (const CharPointer_UTF16& other) const noexcept + int compareIgnoreCase (const CharPointer_UTF16 other) const noexcept { return _wcsicmp (data, other.data); } - int compareIgnoreCaseUpTo (const CharPointer_UTF16& other, int maxChars) const noexcept + int compareIgnoreCaseUpTo (const CharPointer_UTF16 other, int maxChars) const noexcept { return _wcsnicmp (data, other.data, (size_t) maxChars); } - int indexOf (const CharPointer_UTF16& stringToFind) const noexcept + int indexOf (const CharPointer_UTF16 stringToFind) const noexcept { const CharType* const t = wcsstr (data, stringToFind.getAddress()); return t == nullptr ? -1 : (int) (t - data); @@ -366,7 +369,7 @@ public: /** Returns the character index of a substring, or -1 if it isn't found. */ template - int indexOf (const CharPointer& stringToFind) const noexcept + int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } @@ -438,7 +441,7 @@ public: /** Returns true if this data contains a valid string in this encoding. */ static bool isValidString (const CharType* dataToTest, int maxBytesToRead) { - maxBytesToRead /= sizeof (CharType); + maxBytesToRead /= (int) sizeof (CharType); while (--maxBytesToRead >= 0 && *dataToTest != 0) { @@ -466,7 +469,7 @@ public: } /** Atomically swaps this pointer for a new value, returning the previous value. */ - CharPointer_UTF16 atomicSwap (const CharPointer_UTF16& newValue) + CharPointer_UTF16 atomicSwap (const CharPointer_UTF16 newValue) { return CharPointer_UTF16 (reinterpret_cast &> (data).exchange (newValue.data)); } @@ -480,6 +483,30 @@ public: byteOrderMarkLE2 = 0xfe }; + /** Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (big endian). + The pointer must not be null, and must contain at least two valid bytes. + */ + static bool isByteOrderMarkBigEndian (const void* possibleByteOrder) noexcept + { + jassert (possibleByteOrder != nullptr); + const uint8* const c = static_cast (possibleByteOrder); + + return c[0] == (uint8) byteOrderMarkBE1 + && c[1] == (uint8) byteOrderMarkBE2; + } + + /** Returns true if the first pair of bytes in this pointer are the UTF16 byte-order mark (little endian). + The pointer must not be null, and must contain at least two valid bytes. + */ + static bool isByteOrderMarkLittleEndian (const void* possibleByteOrder) noexcept + { + jassert (possibleByteOrder != nullptr); + const uint8* const c = static_cast (possibleByteOrder); + + return c[0] == (uint8) byteOrderMarkLE1 + && c[1] == (uint8) byteOrderMarkLE2; + } + private: CharType* data; @@ -495,4 +522,4 @@ private: }; -#endif // __JUCE_CHARPOINTER_UTF16_JUCEHEADER__ +#endif // JUCE_CHARPOINTER_UTF16_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h index 7d707b9..42cfe48 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF32.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ -#define __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ +#ifndef JUCE_CHARPOINTER_UTF32_H_INCLUDED +#define JUCE_CHARPOINTER_UTF32_H_INCLUDED //============================================================================== @@ -48,25 +51,25 @@ public: { } - inline CharPointer_UTF32& operator= (const CharPointer_UTF32& other) noexcept + inline CharPointer_UTF32 operator= (CharPointer_UTF32 other) noexcept { data = other.data; return *this; } - inline CharPointer_UTF32& operator= (const CharType* text) noexcept + inline CharPointer_UTF32 operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ - inline bool operator== (const CharPointer_UTF32& other) const noexcept { return data == other.data; } - inline bool operator!= (const CharPointer_UTF32& other) const noexcept { return data != other.data; } - inline bool operator<= (const CharPointer_UTF32& other) const noexcept { return data <= other.data; } - inline bool operator< (const CharPointer_UTF32& other) const noexcept { return data < other.data; } - inline bool operator>= (const CharPointer_UTF32& other) const noexcept { return data >= other.data; } - inline bool operator> (const CharPointer_UTF32& other) const noexcept { return data > other.data; } + inline bool operator== (CharPointer_UTF32 other) const noexcept { return data == other.data; } + inline bool operator!= (CharPointer_UTF32 other) const noexcept { return data != other.data; } + inline bool operator<= (CharPointer_UTF32 other) const noexcept { return data <= other.data; } + inline bool operator< (CharPointer_UTF32 other) const noexcept { return data < other.data; } + inline bool operator>= (CharPointer_UTF32 other) const noexcept { return data >= other.data; } + inline bool operator> (CharPointer_UTF32 other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } @@ -81,14 +84,14 @@ public: inline juce_wchar operator*() const noexcept { return *data; } /** Moves this pointer along to the next character in the string. */ - inline CharPointer_UTF32& operator++() noexcept + inline CharPointer_UTF32 operator++() noexcept { ++data; return *this; } /** Moves this pointer to the previous character in the string. */ - inline CharPointer_UTF32& operator--() noexcept + inline CharPointer_UTF32 operator--() noexcept { --data; return *this; @@ -172,7 +175,7 @@ public: } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ - size_t lengthUpTo (const CharPointer_UTF32& end) const noexcept + size_t lengthUpTo (const CharPointer_UTF32 end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } @@ -198,7 +201,7 @@ public: The value returned does NOT include the terminating null character. */ template - static size_t getBytesRequiredFor (const CharPointer& text) noexcept + static size_t getBytesRequiredFor (const CharPointer text) noexcept { return sizeof (CharType) * text.length(); } @@ -211,13 +214,13 @@ public: /** Copies a source string to this pointer, advancing this pointer as it goes. */ template - void writeAll (const CharPointer& src) noexcept + void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ - void writeAll (const CharPointer_UTF32& src) noexcept + void writeAll (const CharPointer_UTF32 src) noexcept { const CharType* s = src.data; @@ -233,7 +236,7 @@ public: to the destination buffer before stopping. */ template - int writeWithDestByteLimit (const CharPointer& src, const int maxDestBytes) noexcept + size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } @@ -243,21 +246,21 @@ public: written to the destination buffer before stopping (including the terminating null). */ template - void writeWithCharLimit (const CharPointer& src, const int maxChars) noexcept + void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template - int compare (const CharPointer& other) const noexcept + int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } #if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID /** Compares this string with another one. */ - int compare (const CharPointer_UTF32& other) const noexcept + int compare (const CharPointer_UTF32 other) const noexcept { return wcscmp (data, other.data); } @@ -265,28 +268,28 @@ public: /** Compares this string with another one, up to a specified number of characters. */ template - int compareUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one. */ template - int compareIgnoreCase (const CharPointer& other) const + int compareIgnoreCase (const CharPointer other) const { return CharacterFunctions::compareIgnoreCase (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template - int compareIgnoreCaseUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } /** Returns the character index of a substring, or -1 if it isn't found. */ template - int indexOf (const CharPointer& stringToFind) const noexcept + int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } @@ -352,7 +355,7 @@ public: /** Returns true if this data contains a valid string in this encoding. */ static bool isValidString (const CharType* dataToTest, int maxBytesToRead) { - maxBytesToRead /= sizeof (CharType); + maxBytesToRead /= (int) sizeof (CharType); while (--maxBytesToRead >= 0 && *dataToTest != 0) if (! canRepresent (*dataToTest++)) @@ -362,7 +365,7 @@ public: } /** Atomically swaps this pointer for a new value, returning the previous value. */ - CharPointer_UTF32 atomicSwap (const CharPointer_UTF32& newValue) + CharPointer_UTF32 atomicSwap (const CharPointer_UTF32 newValue) { return CharPointer_UTF32 (reinterpret_cast &> (data).exchange (newValue.data)); } @@ -372,4 +375,4 @@ private: }; -#endif // __JUCE_CHARPOINTER_UTF32_JUCEHEADER__ +#endif // JUCE_CHARPOINTER_UTF32_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h index 6df77e1..78d9a97 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharPointer_UTF8.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ -#define __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ +#ifndef JUCE_CHARPOINTER_UTF8_H_INCLUDED +#define JUCE_CHARPOINTER_UTF8_H_INCLUDED //============================================================================== /** @@ -47,25 +50,25 @@ public: { } - inline CharPointer_UTF8& operator= (const CharPointer_UTF8& other) noexcept + inline CharPointer_UTF8 operator= (CharPointer_UTF8 other) noexcept { data = other.data; return *this; } - inline CharPointer_UTF8& operator= (const CharType* text) noexcept + inline CharPointer_UTF8 operator= (const CharType* text) noexcept { data = const_cast (text); return *this; } /** This is a pointer comparison, it doesn't compare the actual text. */ - inline bool operator== (const CharPointer_UTF8& other) const noexcept { return data == other.data; } - inline bool operator!= (const CharPointer_UTF8& other) const noexcept { return data != other.data; } - inline bool operator<= (const CharPointer_UTF8& other) const noexcept { return data <= other.data; } - inline bool operator< (const CharPointer_UTF8& other) const noexcept { return data < other.data; } - inline bool operator>= (const CharPointer_UTF8& other) const noexcept { return data >= other.data; } - inline bool operator> (const CharPointer_UTF8& other) const noexcept { return data > other.data; } + inline bool operator== (CharPointer_UTF8 other) const noexcept { return data == other.data; } + inline bool operator!= (CharPointer_UTF8 other) const noexcept { return data != other.data; } + inline bool operator<= (CharPointer_UTF8 other) const noexcept { return data <= other.data; } + inline bool operator< (CharPointer_UTF8 other) const noexcept { return data < other.data; } + inline bool operator>= (CharPointer_UTF8 other) const noexcept { return data >= other.data; } + inline bool operator> (CharPointer_UTF8 other) const noexcept { return data > other.data; } /** Returns the address that this pointer is pointing to. */ inline CharType* getAddress() const noexcept { return data; } @@ -115,6 +118,7 @@ public: /** Moves this pointer along to the next character in the string. */ CharPointer_UTF8& operator++() noexcept { + jassert (*data != 0); // trying to advance past the end of the string? const signed char n = (signed char) *data++; if (n < 0) @@ -132,20 +136,12 @@ public: } /** Moves this pointer back to the previous character in the string. */ - CharPointer_UTF8& operator--() noexcept + CharPointer_UTF8 operator--() noexcept { - const char n = *--data; + int count = 0; - if ((n & 0xc0) == 0xc0) - { - int count = 3; - - do - { - --data; - } - while ((*data & 0xc0) == 0xc0 && --count >= 0); - } + while ((*--data & 0xc0) == 0x80 && ++count < 4) + {} return *this; } @@ -175,11 +171,12 @@ public: while (--numExtraValues >= 0) { - const uint32 nextByte = (uint32) (uint8) *data++; + const uint32 nextByte = (uint32) (uint8) *data; if ((nextByte & 0xc0) != 0x80) break; + ++data; n <<= 6; n |= (nextByte & 0x3f); } @@ -252,16 +249,8 @@ public: if ((n & 0x80) != 0) { - uint32 bit = 0x40; - - while ((n & bit) != 0) - { + while ((*d & 0xc0) == 0x80) ++d; - bit >>= 1; - - if (bit == 0) - break; // illegal utf-8 sequence - } } else if (n == 0) break; @@ -279,7 +268,7 @@ public: } /** Returns the number of characters in this string, or up to the given end pointer, whichever is lower. */ - size_t lengthUpTo (const CharPointer_UTF8& end) const noexcept + size_t lengthUpTo (const CharPointer_UTF8 end) const noexcept { return CharacterFunctions::lengthUpTo (*this, end); } @@ -371,13 +360,13 @@ public: /** Copies a source string to this pointer, advancing this pointer as it goes. */ template - void writeAll (const CharPointer& src) noexcept + void writeAll (const CharPointer src) noexcept { CharacterFunctions::copyAll (*this, src); } /** Copies a source string to this pointer, advancing this pointer as it goes. */ - void writeAll (const CharPointer_UTF8& src) noexcept + void writeAll (const CharPointer_UTF8 src) noexcept { const CharType* s = src.data; @@ -393,7 +382,7 @@ public: to the destination buffer before stopping. */ template - int writeWithDestByteLimit (const CharPointer& src, const int maxDestBytes) noexcept + size_t writeWithDestByteLimit (const CharPointer src, const size_t maxDestBytes) noexcept { return CharacterFunctions::copyWithDestByteLimit (*this, src, maxDestBytes); } @@ -403,34 +392,34 @@ public: written to the destination buffer before stopping (including the terminating null). */ template - void writeWithCharLimit (const CharPointer& src, const int maxChars) noexcept + void writeWithCharLimit (const CharPointer src, const int maxChars) noexcept { CharacterFunctions::copyWithCharLimit (*this, src, maxChars); } /** Compares this string with another one. */ template - int compare (const CharPointer& other) const noexcept + int compare (const CharPointer other) const noexcept { return CharacterFunctions::compare (*this, other); } /** Compares this string with another one, up to a specified number of characters. */ template - int compareUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareUpTo (*this, other, maxChars); } /** Compares this string with another one. */ template - int compareIgnoreCase (const CharPointer& other) const noexcept + int compareIgnoreCase (const CharPointer other) const noexcept { return CharacterFunctions::compareIgnoreCase (*this, other); } /** Compares this string with another one. */ - int compareIgnoreCase (const CharPointer_UTF8& other) const noexcept + int compareIgnoreCase (const CharPointer_UTF8 other) const noexcept { #if JUCE_WINDOWS return stricmp (data, other.data); @@ -441,14 +430,14 @@ public: /** Compares this string with another one, up to a specified number of characters. */ template - int compareIgnoreCaseUpTo (const CharPointer& other, const int maxChars) const noexcept + int compareIgnoreCaseUpTo (const CharPointer other, const int maxChars) const noexcept { return CharacterFunctions::compareIgnoreCaseUpTo (*this, other, maxChars); } /** Returns the character index of a substring, or -1 if it isn't found. */ template - int indexOf (const CharPointer& stringToFind) const noexcept + int indexOf (const CharPointer stringToFind) const noexcept { return CharacterFunctions::indexOf (*this, stringToFind); } @@ -516,34 +505,33 @@ public: { while (--maxBytesToRead >= 0 && *dataToTest != 0) { - const signed char byte = (signed char) *dataToTest; + const signed char byte = (signed char) *dataToTest++; if (byte < 0) { - uint32 n = (uint32) (uint8) byte; - uint32 mask = 0x7f; - uint32 bit = 0x40; + uint8 bit = 0x40; int numExtraValues = 0; - while ((n & bit) != 0) + while ((byte & bit) != 0) { - if (bit <= 0x10) + if (bit < 8) return false; - mask >>= 1; ++numExtraValues; bit >>= 1; - } - n &= mask; - - while (--numExtraValues >= 0) - { - const uint32 nextByte = (uint32) (uint8) *dataToTest++; - - if ((nextByte & 0xc0) != 0x80) + if (bit == 8 && (numExtraValues > maxBytesToRead + || *CharPointer_UTF8 (dataToTest - 1) > 0x10ffff)) return false; } + + maxBytesToRead -= numExtraValues; + if (maxBytesToRead < 0) + return false; + + while (--numExtraValues >= 0) + if ((*dataToTest++ & 0xc0) != 0x80) + return false; } } @@ -551,12 +539,12 @@ public: } /** Atomically swaps this pointer for a new value, returning the previous value. */ - CharPointer_UTF8 atomicSwap (const CharPointer_UTF8& newValue) + CharPointer_UTF8 atomicSwap (const CharPointer_UTF8 newValue) { return CharPointer_UTF8 (reinterpret_cast &> (data).exchange (newValue.data)); } - /** These values are the byte-order-mark (BOM) values for a UTF-8 stream. */ + /** These values are the byte-order mark (BOM) values for a UTF-8 stream. */ enum { byteOrderMark1 = 0xef, @@ -564,8 +552,21 @@ public: byteOrderMark3 = 0xbf }; + /** Returns true if the first three bytes in this pointer are the UTF8 byte-order mark (BOM). + The pointer must not be null, and must point to at least 3 valid bytes. + */ + static bool isByteOrderMark (const void* possibleByteOrder) noexcept + { + jassert (possibleByteOrder != nullptr); + const uint8* const c = static_cast (possibleByteOrder); + + return c[0] == (uint8) byteOrderMark1 + && c[1] == (uint8) byteOrderMark2 + && c[2] == (uint8) byteOrderMark3; + } + private: CharType* data; }; -#endif // __JUCE_CHARPOINTER_UTF8_JUCEHEADER__ +#endif // JUCE_CHARPOINTER_UTF8_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp index e58814f..11eaa18 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -107,7 +110,7 @@ bool CharacterFunctions::isLetterOrDigit (const juce_wchar character) noexcept int CharacterFunctions::getHexDigitValue (const juce_wchar digit) noexcept { - unsigned int d = digit - '0'; + unsigned int d = (unsigned int) digit - '0'; if (d < (unsigned int) 10) return (int) d; diff --git a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h index b5e25c0..bade0dd 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_CharacterFunctions.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ -#define __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ +#ifndef JUCE_CHARACTERFUNCTIONS_H_INCLUDED +#define JUCE_CHARACTERFUNCTIONS_H_INCLUDED //============================================================================== @@ -255,7 +258,7 @@ public: //============================================================================== /** Parses a character string, to read an integer value. */ template - static IntType getIntValue (const CharPointerType& text) noexcept + static IntType getIntValue (const CharPointerType text) noexcept { IntType v = 0; CharPointerType s (text.findEndOfWhitespace()); @@ -277,6 +280,26 @@ public: return isNeg ? -v : v; } + template + struct HexParser + { + template + static ResultType parse (CharPointerType t) noexcept + { + ResultType result = 0; + + while (! t.isEmpty()) + { + const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); + + if (hexValue >= 0) + result = (result << 4) | hexValue; + } + + return result; + } + }; + //============================================================================== /** Counts the number of characters in a given string, stopping if the count exceeds a specified limit. */ @@ -294,7 +317,7 @@ public: /** Counts the number of characters in a given string, stopping if the count exceeds a specified end-pointer. */ template - static size_t lengthUpTo (CharPointerType start, const CharPointerType& end) noexcept + static size_t lengthUpTo (CharPointerType start, const CharPointerType end) noexcept { size_t len = 0; @@ -324,15 +347,16 @@ public: /** Copies characters from one string to another, up to a null terminator or a given byte size limit. */ template - static int copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, int maxBytes) noexcept + static size_t copyWithDestByteLimit (DestCharPointerType& dest, SrcCharPointerType src, size_t maxBytesToWrite) noexcept { typename DestCharPointerType::CharType const* const startAddress = dest.getAddress(); + ssize_t maxBytes = (ssize_t) maxBytesToWrite; maxBytes -= sizeof (typename DestCharPointerType::CharType); // (allow for a terminating null) for (;;) { const juce_wchar c = src.getAndAdvance(); - const int bytesNeeded = (int) DestCharPointerType::getBytesRequiredFor (c); + const size_t bytesNeeded = DestCharPointerType::getBytesRequiredFor (c); maxBytes -= bytesNeeded; if (c == 0 || maxBytes < 0) @@ -343,7 +367,8 @@ public: dest.writeNull(); - return (int) (getAddressDifference (dest.getAddress(), startAddress) + sizeof (typename DestCharPointerType::CharType)); + return (size_t) getAddressDifference (dest.getAddress(), startAddress) + + sizeof (typename DestCharPointerType::CharType); } /** Copies characters from one string to another, up to a null terminator @@ -371,12 +396,10 @@ public: { const int c1 = (int) s1.getAndAdvance(); const int c2 = (int) s2.getAndAdvance(); - const int diff = c1 - c2; - if (diff != 0) - return diff < 0 ? -1 : 1; - else if (c1 == 0) - break; + + if (diff != 0) return diff < 0 ? -1 : 1; + if (c1 == 0) break; } return 0; @@ -390,12 +413,10 @@ public: { const int c1 = (int) s1.getAndAdvance(); const int c2 = (int) s2.getAndAdvance(); - const int diff = c1 - c2; - if (diff != 0) - return diff < 0 ? -1 : 1; - else if (c1 == 0) - break; + + if (diff != 0) return diff < 0 ? -1 : 1; + if (c1 == 0) break; } return 0; @@ -407,16 +428,14 @@ public: { for (;;) { - int c1 = (int) s1.toUpperCase(); - int c2 = (int) s2.toUpperCase(); - ++s1; - ++s2; - + const int c1 = (int) s1.toUpperCase(); + const int c2 = (int) s2.toUpperCase(); const int diff = c1 - c2; - if (diff != 0) - return diff < 0 ? -1 : 1; - else if (c1 == 0) - break; + + if (diff != 0) return diff < 0 ? -1 : 1; + if (c1 == 0) break; + + ++s1; ++s2; } return 0; @@ -428,16 +447,14 @@ public: { while (--maxChars >= 0) { - int c1 = s1.toUpperCase(); - int c2 = s2.toUpperCase(); - ++s1; - ++s2; - + const int c1 = (int) s1.toUpperCase(); + const int c2 = (int) s2.toUpperCase(); const int diff = c1 - c2; - if (diff != 0) - return diff < 0 ? -1 : 1; - else if (c1 == 0) - break; + + if (diff != 0) return diff < 0 ? -1 : 1; + if (c1 == 0) break; + + ++s1; ++s2; } return 0; @@ -447,29 +464,63 @@ public: Returns -1 if the substring is not found. */ template - static int indexOf (CharPointerType1 haystack, const CharPointerType2& needle) noexcept + static int indexOf (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept { int index = 0; - const int needleLength = (int) needle.length(); + const int substringLength = (int) substringToLookFor.length(); for (;;) { - if (haystack.compareUpTo (needle, needleLength) == 0) + if (textToSearch.compareUpTo (substringToLookFor, substringLength) == 0) return index; - if (haystack.getAndAdvance() == 0) + if (textToSearch.getAndAdvance() == 0) return -1; ++index; } } + /** Returns a pointer to the first occurrence of a substring in a string. + If the substring is not found, this will return a pointer to the string's + null terminator. + */ + template + static CharPointerType1 find (CharPointerType1 textToSearch, const CharPointerType2 substringToLookFor) noexcept + { + const int substringLength = (int) substringToLookFor.length(); + + while (textToSearch.compareUpTo (substringToLookFor, substringLength) != 0 + && ! textToSearch.isEmpty()) + ++textToSearch; + + return textToSearch; + } + + /** Returns a pointer to the first occurrence of a substring in a string. + If the substring is not found, this will return a pointer to the string's + null terminator. + */ + template + static CharPointerType find (CharPointerType textToSearch, const juce_wchar charToLookFor) noexcept + { + for (;; ++textToSearch) + { + const juce_wchar c = *textToSearch; + + if (c == charToLookFor || c == 0) + break; + } + + return textToSearch; + } + /** Finds the character index of a given substring in another string, using a case-independent match. Returns -1 if the substring is not found. */ template - static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2& needle) noexcept + static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2 needle) noexcept { int index = 0; const int needleLength = (int) needle.length(); @@ -532,32 +583,29 @@ public: to its null terminator. */ template - static Type findEndOfWhitespace (const Type& text) noexcept + static Type findEndOfWhitespace (Type text) noexcept { - Type p (text); + while (text.isWhitespace()) + ++text; - while (p.isWhitespace()) - ++p; - - return p; + return text; } /** Returns a pointer to the first character in the string which is found in the breakCharacters string. */ - template - static Type findEndOfToken (const Type& text, const Type& breakCharacters, const Type& quoteCharacters) + template + static Type findEndOfToken (Type text, const BreakType breakCharacters, const Type quoteCharacters) { - Type t (text); juce_wchar currentQuoteChar = 0; - while (! t.isEmpty()) + while (! text.isEmpty()) { - const juce_wchar c = t.getAndAdvance(); + const juce_wchar c = text.getAndAdvance(); if (currentQuoteChar == 0 && breakCharacters.indexOf (c) >= 0) { - --t; + --text; break; } @@ -570,7 +618,7 @@ public: } } - return t; + return text; } private: @@ -578,4 +626,4 @@ private: }; -#endif // __JUCE_CHARACTERFUNCTIONS_JUCEHEADER__ +#endif // JUCE_CHARACTERFUNCTIONS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp index 2bfe8e4..bac4dd5 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.cpp @@ -1,70 +1,68 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -StringPool& Identifier::getPool() -{ - static StringPool pool; - return pool; -} +Identifier::Identifier() noexcept {} +Identifier::~Identifier() noexcept {} -Identifier::Identifier() noexcept - : name (nullptr) -{ -} +Identifier::Identifier (const Identifier& other) noexcept : name (other.name) {} -Identifier::Identifier (const Identifier& other) noexcept - : name (other.name) -{ -} - -Identifier& Identifier::operator= (const Identifier& other) noexcept +Identifier& Identifier::operator= (const Identifier other) noexcept { name = other.name; return *this; } -Identifier::Identifier (const String& name_) - : name (Identifier::getPool().getPooledString (name_)) -{ - /* An Identifier string must be suitable for use as a script variable or XML - attribute, so it can only contain this limited set of characters.. */ - jassert (isValidIdentifier (name_)); -} - -Identifier::Identifier (const char* const name_) - : name (Identifier::getPool().getPooledString (name_)) +Identifier::Identifier (const String& nm) + : name (StringPool::getGlobalPool().getPooledString (nm)) { /* An Identifier string must be suitable for use as a script variable or XML attribute, so it can only contain this limited set of characters.. */ jassert (isValidIdentifier (toString())); } -Identifier::~Identifier() +Identifier::Identifier (const char* nm) + : name (StringPool::getGlobalPool().getPooledString (nm)) { + /* An Identifier string must be suitable for use as a script variable or XML + attribute, so it can only contain this limited set of characters.. */ + jassert (isValidIdentifier (toString())); } +Identifier::Identifier (String::CharPointerType start, String::CharPointerType end) + : name (StringPool::getGlobalPool().getPooledString (start, end)) +{ + /* An Identifier string must be suitable for use as a script variable or XML + attribute, so it can only contain this limited set of characters.. */ + jassert (isValidIdentifier (toString())); +} + +Identifier Identifier::null; + bool Identifier::isValidIdentifier (const String& possibleIdentifier) noexcept { return possibleIdentifier.isNotEmpty() diff --git a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h index 875b409..f60eec8 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_Identifier.h @@ -1,43 +1,44 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_IDENTIFIER_JUCEHEADER__ -#define __JUCE_IDENTIFIER_JUCEHEADER__ - -class StringPool; +#ifndef JUCE_IDENTIFIER_H_INCLUDED +#define JUCE_IDENTIFIER_H_INCLUDED //============================================================================== /** Represents a string identifier, designed for accessing properties by name. - Identifier objects are very light and fast to copy, but slower to initialise - from a string, so it's much faster to keep a static identifier object to refer - to frequently-used names, rather than constructing them each time you need it. + Comparing two Identifier objects is very fast (an O(1) operation), but creating + them can be slower than just using a String directly, so the optimal way to use them + is to keep some static Identifier objects for the things you use often. - @see NamedPropertySet, ValueTree + @see NamedValueSet, ValueTree */ class JUCE_API Identifier { @@ -57,29 +58,53 @@ public: */ Identifier (const String& 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 (String::CharPointerType nameStart, String::CharPointerType nameEnd); + /** Creates a copy of another identifier. */ Identifier (const Identifier& other) noexcept; /** Creates a copy of another identifier. */ - Identifier& operator= (const Identifier& other) noexcept; + Identifier& operator= (const Identifier other) noexcept; /** Destructor */ - ~Identifier(); + ~Identifier() noexcept; /** Compares two identifiers. This is a very fast operation. */ - inline bool operator== (const Identifier& other) const noexcept { return name == other.name; } + inline bool operator== (Identifier other) const noexcept { return name.getCharPointer() == other.name.getCharPointer(); } /** Compares two identifiers. This is a very fast operation. */ - inline bool operator!= (const Identifier& other) const noexcept { return name != other.name; } + inline bool operator!= (Identifier other) const noexcept { return name.getCharPointer() != other.name.getCharPointer(); } + + /** Compares the identifier with a string. */ + inline bool operator== (StringRef other) const noexcept { return name == other; } + + /** Compares the identifier with a string. */ + inline bool operator!= (StringRef other) const noexcept { return name != other; } /** Returns this identifier as a string. */ - String toString() const { return name; } + const String& toString() const noexcept { return name; } /** Returns this identifier's raw string pointer. */ - operator const String::CharPointerType() const noexcept { return name; } + operator String::CharPointerType() const noexcept { return name.getCharPointer(); } /** Returns this identifier's raw string pointer. */ - const String::CharPointerType getCharPointer() const noexcept { return name; } + String::CharPointerType getCharPointer() const noexcept { return name.getCharPointer(); } + + /** Returns this identifier as a StringRef. */ + operator StringRef() const noexcept { return name; } + + /** Returns true if this Identifier is not null */ + bool isValid() const noexcept { return name.isNotEmpty(); } + + /** Returns true if this Identifier is null */ + bool isNull() const noexcept { return name.isEmpty(); } + + /** A null identifier. */ + static Identifier null; /** Checks a given string for characters that might not be valid in an Identifier. Since Identifiers are used as a script variables and XML attributes, they should only contain @@ -87,13 +112,9 @@ public: */ static bool isValidIdentifier (const String& possibleIdentifier) noexcept; - private: - //============================================================================== - String::CharPointerType name; - - static StringPool& getPool(); + String name; }; -#endif // __JUCE_IDENTIFIER_JUCEHEADER__ +#endif // JUCE_IDENTIFIER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp index 2ac7fae..e6118d3 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp @@ -1,36 +1,54 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -LocalisedStrings::LocalisedStrings (const String& fileContents) +LocalisedStrings::LocalisedStrings (const String& fileContents, bool ignoreCase) { - loadFromText (fileContents); + loadFromText (fileContents, ignoreCase); } -LocalisedStrings::LocalisedStrings (const File& fileToLoad) +LocalisedStrings::LocalisedStrings (const File& fileToLoad, bool ignoreCase) { - loadFromText (fileToLoad.loadFileAsString()); + loadFromText (fileToLoad.loadFileAsString(), ignoreCase); +} + +LocalisedStrings::LocalisedStrings (const LocalisedStrings& other) + : languageName (other.languageName), countryCodes (other.countryCodes), + translations (other.translations), fallback (createCopyIfNotNull (other.fallback.get())) +{ +} + +LocalisedStrings& LocalisedStrings::operator= (const LocalisedStrings& other) +{ + languageName = other.languageName; + countryCodes = other.countryCodes; + translations = other.translations; + fallback = createCopyIfNotNull (other.fallback.get()); + return *this; } LocalisedStrings::~LocalisedStrings() @@ -40,11 +58,17 @@ LocalisedStrings::~LocalisedStrings() //============================================================================== String LocalisedStrings::translate (const String& text) const { + if (fallback != nullptr && ! translations.containsKey (text)) + return fallback->translate (text); + return translations.getValue (text, text); } String LocalisedStrings::translate (const String& text, const String& resultIfNotFound) const { + if (fallback != nullptr && ! translations.containsKey (text)) + return fallback->translate (text, resultIfNotFound); + return translations.getValue (text, resultIfNotFound); } @@ -60,7 +84,7 @@ namespace { LeakAvoidanceTrick() { - const ScopedPointer dummy (new LocalisedStrings (String())); + const ScopedPointer dummy (new LocalisedStrings (String(), false)); } }; @@ -70,7 +94,7 @@ namespace SpinLock currentMappingsLock; ScopedPointer currentMappings; - int findCloseQuote (const String& text, int startPos) + static int findCloseQuote (const String& text, int startPos) { juce_wchar lastChar = 0; String::CharPointerType t (text.getCharPointer() + startPos); @@ -89,7 +113,7 @@ namespace return startPos; } - String unescapeString (const String& s) + static String unescapeString (const String& s) { return s.replace ("\\\"", "\"") .replace ("\\\'", "\'") @@ -99,8 +123,10 @@ namespace } } -void LocalisedStrings::loadFromText (const String& fileContents) +void LocalisedStrings::loadFromText (const String& fileContents, bool ignoreCase) { + translations.setIgnoresCase (ignoreCase); + StringArray lines; lines.addLines (fileContents); @@ -136,11 +162,21 @@ void LocalisedStrings::loadFromText (const String& fileContents) countryCodes.removeEmptyStrings(); } } + + translations.minimiseStorageOverheads(); } -void LocalisedStrings::setIgnoresCase (const bool shouldIgnoreCase) +void LocalisedStrings::addStrings (const LocalisedStrings& other) { - translations.setIgnoresCase (shouldIgnoreCase); + jassert (languageName == other.languageName); + jassert (countryCodes == other.countryCodes); + + translations.addArray (other.translations); +} + +void LocalisedStrings::setFallback (LocalisedStrings* f) +{ + fallback = f; } //============================================================================== @@ -155,34 +191,19 @@ LocalisedStrings* LocalisedStrings::getCurrentMappings() return currentMappings; } -String LocalisedStrings::translateWithCurrentMappings (const String& text) -{ - return juce::translate (text); -} +String LocalisedStrings::translateWithCurrentMappings (const String& text) { return juce::translate (text); } +String LocalisedStrings::translateWithCurrentMappings (const char* text) { return juce::translate (text); } -String LocalisedStrings::translateWithCurrentMappings (const char* text) -{ - return juce::translate (String (text)); -} +JUCE_API String translate (const String& text) { return juce::translate (text, text); } +JUCE_API String translate (const char* text) { return juce::translate (String (text)); } +JUCE_API String translate (CharPointer_UTF8 text) { return juce::translate (String (text)); } -String translate (const String& text) -{ - return translate (text, text); -} - -String translate (const char* const literal) -{ - const String text (literal); - return translate (text, text); -} - -String translate (const String& text, const String& resultIfNotFound) +JUCE_API String translate (const String& text, const String& resultIfNotFound) { const SpinLock::ScopedLockType sl (currentMappingsLock); - const LocalisedStrings* const currentMappings = LocalisedStrings::getCurrentMappings(); - if (currentMappings != nullptr) - return currentMappings->translate (text, resultIfNotFound); + if (const LocalisedStrings* const mappings = LocalisedStrings::getCurrentMappings()) + return mappings->translate (text, resultIfNotFound); return resultIfNotFound; } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h index ce552cd..a594919 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h @@ -1,33 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ -#define __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ +#ifndef JUCE_LOCALISEDSTRINGS_H_INCLUDED +#define JUCE_LOCALISEDSTRINGS_H_INCLUDED -#include "juce_StringPairArray.h" -#include "../files/juce_File.h" //============================================================================== /** @@ -81,14 +82,17 @@ public: 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); + LocalisedStrings (const String& fileContents, bool ignoreCaseOfKeys); /** 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); + LocalisedStrings (const File& fileToLoad, bool ignoreCaseOfKeys); + + LocalisedStrings (const LocalisedStrings&); + LocalisedStrings& operator= (const LocalisedStrings&); /** Destructor. */ ~LocalisedStrings(); @@ -97,7 +101,7 @@ public: /** 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 + don't keep a pointer to it. You can also pass in nullptr to remove the current mappings. See also the TRANS() macro, which uses the current set to do its translation. @@ -109,7 +113,7 @@ public: /** 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. + be nullptr if none has been created. */ static LocalisedStrings* getCurrentMappings(); @@ -166,22 +170,37 @@ public: */ const StringArray& getCountryCodes() const { return countryCodes; } + /** Provides access to the actual list of mappings. */ + const StringPairArray& getMappings() const { return translations; } //============================================================================== - /** Indicates whether to use a case-insensitive search when looking up a string. - This defaults to true. + /** Adds and merges another set of translations into this set. + + Note that the language name and country codes of the new LocalisedStrings + object must match that of this object - an assertion will be thrown if they + don't match. + + Any existing values will have their mappings overwritten by the new ones. */ - void setIgnoresCase (bool shouldIgnoreCase); + void addStrings (const LocalisedStrings&); + + /** Gives this object a set of strings to use as a fallback if a string isn't found. + The object that is passed-in will be owned and deleted by this object + when no longer needed. It can be nullptr to clear the existing fallback object. + */ + void setFallback (LocalisedStrings* fallbackStrings); private: //============================================================================== String languageName; StringArray countryCodes; StringPairArray translations; + ScopedPointer fallback; + friend struct ContainerDeletePolicy; - void loadFromText (const String& fileContents); + void loadFromText (const String&, bool ignoreCase); - JUCE_LEAK_DETECTOR (LocalisedStrings); + JUCE_LEAK_DETECTOR (LocalisedStrings) }; //============================================================================== @@ -195,20 +214,34 @@ private: #define TRANS(stringLiteral) juce::translate (stringLiteral) #endif -/** Uses the LocalisedStrings class to translate the given string literal. - @see LocalisedStrings +/** A dummy version of the TRANS macro, used to indicate a string literal that should be + added to the translation file by source-code scanner tools. + + Wrapping a string literal in this macro has no effect, but by using it around strings + that your app needs to translate at a later stage, it lets automatic code-scanning tools + find this string and add it to the list of strings that need translation. */ -String translate (const String& stringLiteral); +#define NEEDS_TRANS(stringLiteral) (stringLiteral) /** Uses the LocalisedStrings class to translate the given string literal. @see LocalisedStrings */ -String translate (const char* stringLiteral); +JUCE_API String translate (const String& stringLiteral); /** Uses the LocalisedStrings class to translate the given string literal. @see LocalisedStrings */ -String translate (const String& stringLiteral, const String& resultIfNotFound); +JUCE_API String translate (const char* stringLiteral); + +/** Uses the LocalisedStrings class to translate the given string literal. + @see LocalisedStrings +*/ +JUCE_API String translate (CharPointer_UTF8 stringLiteral); + +/** Uses the LocalisedStrings class to translate the given string literal. + @see LocalisedStrings +*/ +JUCE_API String translate (const String& stringLiteral, const String& resultIfNotFound); -#endif // __JUCE_LOCALISEDSTRINGS_JUCEHEADER__ +#endif // JUCE_LOCALISEDSTRINGS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h b/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h index c8dda47..980ac23 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_NewLine.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_NEWLINE_JUCEHEADER__ -#define __JUCE_NEWLINE_JUCEHEADER__ +#ifndef JUCE_NEWLINE_H_INCLUDED +#define JUCE_NEWLINE_H_INCLUDED //============================================================================== @@ -50,6 +53,11 @@ public: @see getDefault() */ operator String() const { return getDefault(); } + + /** Returns the default new-line sequence that the library uses. + @see OutputStream::setNewLineString() + */ + operator StringRef() const noexcept { return getDefault(); } }; //============================================================================== @@ -71,5 +79,8 @@ extern NewLine newLine; */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const NewLine&); +#if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) + inline String operator+ (String s1, const NewLine&) { return s1 += NewLine::getDefault(); } +#endif -#endif // __JUCE_NEWLINE_JUCEHEADER__ +#endif // JUCE_NEWLINE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp index 9ef1479..81e234e 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -44,36 +47,44 @@ NewLine newLine; static inline CharPointer_wchar_t castToCharPointer_wchar_t (const void* t) noexcept { - return CharPointer_wchar_t (static_cast (t)); + return CharPointer_wchar_t (static_cast (t)); } +//============================================================================== +// (Mirrors the structure of StringHolder, but without the atomic member, so can be statically constructed) +struct EmptyString +{ + int refCount; + size_t allocatedBytes; + String::CharPointerType::CharType text; +}; + +static const EmptyString emptyString = { 0x3fffffff, sizeof (String::CharPointerType::CharType), 0 }; + //============================================================================== class StringHolder { public: - StringHolder() - : refCount (0x3fffffff), allocatedNumBytes (sizeof (*text)) - { - text[0] = 0; - } + StringHolder() JUCE_DELETED_FUNCTION; typedef String::CharPointerType CharPointerType; typedef String::CharPointerType::CharType CharType; //============================================================================== - static const CharPointerType createUninitialisedBytes (const size_t numBytes) + static CharPointerType createUninitialisedBytes (size_t numBytes) { - StringHolder* const s = reinterpret_cast (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]); + numBytes = (numBytes + 3) & ~(size_t) 3; + StringHolder* const s = reinterpret_cast (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]); s->refCount.value = 0; s->allocatedNumBytes = numBytes; return CharPointerType (s->text); } template - static const CharPointerType createFromCharPointer (const CharPointer& text) + static CharPointerType createFromCharPointer (const CharPointer text) { if (text.getAddress() == nullptr || text.isEmpty()) - return getEmpty(); + return CharPointerType (&(emptyString.text)); CharPointer t (text); size_t bytesNeeded = sizeof (CharType); @@ -87,10 +98,10 @@ public: } template - static const CharPointerType createFromCharPointer (const CharPointer& text, size_t maxChars) + static CharPointerType createFromCharPointer (const CharPointer text, size_t maxChars) { if (text.getAddress() == nullptr || text.isEmpty() || maxChars == 0) - return getEmpty(); + return CharPointerType (&(emptyString.text)); CharPointer end (text); size_t numChars = 0; @@ -108,10 +119,10 @@ public: } template - static const CharPointerType createFromCharPointer (const CharPointer& start, const CharPointer& end) + static CharPointerType createFromCharPointer (const CharPointer start, const CharPointer end) { if (start.getAddress() == nullptr || start.isEmpty()) - return getEmpty(); + return CharPointerType (&(emptyString.text)); CharPointer e (start); int numChars = 0; @@ -128,55 +139,65 @@ public: return dest; } - static const CharPointerType createFromFixedLength (const char* const src, const size_t numChars) + static CharPointerType createFromCharPointer (const CharPointerType start, const CharPointerType end) + { + if (start.getAddress() == nullptr || start.isEmpty()) + return CharPointerType (&(emptyString.text)); + + const size_t numBytes = (size_t) (reinterpret_cast (end.getAddress()) + - reinterpret_cast (start.getAddress())); + const CharPointerType dest (createUninitialisedBytes (numBytes + sizeof (CharType))); + memcpy (dest.getAddress(), start, numBytes); + dest.getAddress()[numBytes / sizeof (CharType)] = 0; + return dest; + } + + static CharPointerType createFromFixedLength (const char* const src, const size_t numChars) { const CharPointerType dest (createUninitialisedBytes (numChars * sizeof (CharType) + sizeof (CharType))); CharPointerType (dest).writeWithCharLimit (CharPointer_UTF8 (src), (int) (numChars + 1)); return dest; } - static inline const CharPointerType getEmpty() noexcept - { - return CharPointerType (empty.text); - } - //============================================================================== - static void retain (const CharPointerType& text) noexcept + static void retain (const CharPointerType text) noexcept { - ++(bufferFromText (text)->refCount); + StringHolder* const b = bufferFromText (text); + + if (b != (StringHolder*) &emptyString) + ++(b->refCount); } static inline void release (StringHolder* const b) noexcept { - if (--(b->refCount) == -1 && b != &empty) - delete[] reinterpret_cast (b); + if (b != (StringHolder*) &emptyString) + if (--(b->refCount) == -1) + delete[] reinterpret_cast (b); } - static void release (const CharPointerType& text) noexcept + static void release (const CharPointerType text) noexcept { release (bufferFromText (text)); } - //============================================================================== - static CharPointerType makeUnique (const CharPointerType& text) + static inline int getReferenceCount (const CharPointerType text) noexcept { - StringHolder* const b = bufferFromText (text); - - if (b->refCount.get() <= 0) - return text; - - CharPointerType newText (createUninitialisedBytes (b->allocatedNumBytes)); - memcpy (newText.getAddress(), text.getAddress(), b->allocatedNumBytes); - release (b); - - return newText; + return bufferFromText (text)->refCount.get() + 1; } - static CharPointerType makeUniqueWithByteSize (const CharPointerType& text, size_t numBytes) + //============================================================================== + static CharPointerType makeUniqueWithByteSize (const CharPointerType text, size_t numBytes) { StringHolder* const b = bufferFromText (text); - if (b->refCount.get() <= 0 && b->allocatedNumBytes >= numBytes) + if (b == (StringHolder*) &emptyString) + { + CharPointerType newText (createUninitialisedBytes (numBytes)); + newText.writeNull(); + return newText; + } + + if (b->allocatedNumBytes >= numBytes && b->refCount.get() <= 0) return text; CharPointerType newText (createUninitialisedBytes (jmax (b->allocatedNumBytes, numBytes))); @@ -186,7 +207,7 @@ public: return newText; } - static size_t getAllocatedNumBytes (const CharPointerType& text) noexcept + static size_t getAllocatedNumBytes (const CharPointerType text) noexcept { return bufferFromText (text)->allocatedNumBytes; } @@ -196,10 +217,8 @@ public: size_t allocatedNumBytes; CharType text[1]; - static StringHolder empty; - private: - static inline StringHolder* bufferFromText (const CharPointerType& text) noexcept + static inline StringHolder* bufferFromText (const CharPointerType text) noexcept { // (Can't use offsetof() here because of warnings about this not being a POD) return reinterpret_cast (reinterpret_cast (text.getAddress()) @@ -218,21 +237,15 @@ private: #else #error "native wchar_t size is unknown" #endif + + static_jassert (sizeof (EmptyString) == sizeof (StringHolder)); } }; -StringHolder StringHolder::empty; const String String::empty; //============================================================================== -void String::preallocateBytes (const size_t numBytesNeeded) -{ - text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); -} - -//============================================================================== -String::String() noexcept - : text (StringHolder::getEmpty()) +String::String() noexcept : text (&(emptyString.text)) { } @@ -241,8 +254,7 @@ String::~String() noexcept StringHolder::release (text); } -String::String (const String& other) noexcept - : text (other.text) +String::String (const String& other) noexcept : text (other.text) { StringHolder::retain (text); } @@ -252,6 +264,12 @@ void String::swapWith (String& other) noexcept std::swap (text, other.text); } +void String::clear() noexcept +{ + StringHolder::release (text); + text = &(emptyString.text); +} + String& String::operator= (const String& other) noexcept { StringHolder::retain (other.text); @@ -260,10 +278,9 @@ String& String::operator= (const String& other) noexcept } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS -String::String (String&& other) noexcept - : text (other.text) +String::String (String&& other) noexcept : text (other.text) { - other.text = StringHolder::getEmpty(); + other.text = &(emptyString.text); } String& String::operator= (String&& other) noexcept @@ -273,13 +290,23 @@ String& String::operator= (String&& other) noexcept } #endif -inline String::PreallocationBytes::PreallocationBytes (const size_t numBytes_) : numBytes (numBytes_) {} +inline String::PreallocationBytes::PreallocationBytes (const size_t num) noexcept : numBytes (num) {} String::String (const PreallocationBytes& preallocationSize) : text (StringHolder::createUninitialisedBytes (preallocationSize.numBytes + sizeof (CharPointerType::CharType))) { } +void String::preallocateBytes (const size_t numBytesNeeded) +{ + text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); +} + +int String::getReferenceCount() const noexcept +{ + return StringHolder::getReferenceCount (text); +} + //============================================================================== String::String (const char* const t) : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t))) @@ -296,6 +323,10 @@ String::String (const char* const t) you use UTF-8 with escape characters in your source code to represent extended characters, because there's no other way to represent these strings in a way that isn't dependent on the compiler, source code editor and platform. + + Note that the Introjucer has a handy string literal generator utility that will convert + any unicode string to a valid C++ string literal, creating ascii escape sequences that will + work in any compiler. */ jassert (t == nullptr || CharPointer_ASCII::isValidString (t, std::numeric_limits::max())); } @@ -315,24 +346,30 @@ String::String (const char* const t, const size_t maxChars) you use UTF-8 with escape characters in your source code to represent extended characters, because there's no other way to represent these strings in a way that isn't dependent on the compiler, source code editor and platform. + + Note that the Introjucer has a handy string literal generator utility that will convert + any unicode string to a valid C++ string literal, creating ascii escape sequences that will + work in any compiler. */ jassert (t == nullptr || CharPointer_ASCII::isValidString (t, (int) maxChars)); } String::String (const wchar_t* const t) : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t))) {} -String::String (const CharPointer_UTF8& t) : text (StringHolder::createFromCharPointer (t)) {} -String::String (const CharPointer_UTF16& t) : text (StringHolder::createFromCharPointer (t)) {} -String::String (const CharPointer_UTF32& t) : text (StringHolder::createFromCharPointer (t)) {} -String::String (const CharPointer_ASCII& t) : text (StringHolder::createFromCharPointer (t)) {} +String::String (const CharPointer_UTF8 t) : text (StringHolder::createFromCharPointer (t)) {} +String::String (const CharPointer_UTF16 t) : text (StringHolder::createFromCharPointer (t)) {} +String::String (const CharPointer_UTF32 t) : text (StringHolder::createFromCharPointer (t)) {} +String::String (const CharPointer_ASCII t) : text (StringHolder::createFromCharPointer (t)) {} -String::String (const CharPointer_UTF8& t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} -String::String (const CharPointer_UTF16& t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} -String::String (const CharPointer_UTF32& t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} +String::String (const CharPointer_UTF8 t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} +String::String (const CharPointer_UTF16 t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} +String::String (const CharPointer_UTF32 t, const size_t maxChars) : text (StringHolder::createFromCharPointer (t, maxChars)) {} String::String (const wchar_t* const t, size_t maxChars) : text (StringHolder::createFromCharPointer (castToCharPointer_wchar_t (t), maxChars)) {} -String::String (const CharPointer_UTF8& start, const CharPointer_UTF8& end) : text (StringHolder::createFromCharPointer (start, end)) {} -String::String (const CharPointer_UTF16& start, const CharPointer_UTF16& end) : text (StringHolder::createFromCharPointer (start, end)) {} -String::String (const CharPointer_UTF32& start, const CharPointer_UTF32& end) : text (StringHolder::createFromCharPointer (start, end)) {} +String::String (const CharPointer_UTF8 start, const CharPointer_UTF8 end) : text (StringHolder::createFromCharPointer (start, end)) {} +String::String (const CharPointer_UTF16 start, const CharPointer_UTF16 end) : text (StringHolder::createFromCharPointer (start, end)) {} +String::String (const CharPointer_UTF32 start, const CharPointer_UTF32 end) : text (StringHolder::createFromCharPointer (start, end)) {} + +String::String (const std::string& s) : text (StringHolder::createFromFixedLength (s.data(), s.size())) {} String String::charToString (const juce_wchar character) { @@ -346,77 +383,89 @@ String String::charToString (const juce_wchar character) //============================================================================== namespace NumberToStringConverters { - // pass in a pointer to the END of a buffer.. - static char* numberToString (char* t, const int64 n) noexcept + enum + { + charsNeededForInt = 32, + charsNeededForDouble = 48 + }; + + template + static char* printDigits (char* t, Type v) noexcept { *--t = 0; - int64 v = (n >= 0) ? n : -n; do { - *--t = (char) ('0' + (int) (v % 10)); + *--t = '0' + (char) (v % 10); v /= 10; } while (v > 0); - if (n < 0) - *--t = '-'; + return t; + } + // pass in a pointer to the END of a buffer.. + static char* numberToString (char* t, const int64 n) noexcept + { + if (n >= 0) + return printDigits (t, static_cast (n)); + + // NB: this needs to be careful not to call -std::numeric_limits::min(), + // which has undefined behaviour + t = printDigits (t, static_cast (-(n + 1)) + 1); + *--t = '-'; return t; } static char* numberToString (char* t, uint64 v) noexcept { - *--t = 0; - - do - { - *--t = (char) ('0' + (int) (v % 10)); - v /= 10; - - } while (v > 0); - - return t; + return printDigits (t, v); } static char* numberToString (char* t, const int n) noexcept { - if (n == (int) 0x80000000) // (would cause an overflow) - return numberToString (t, (int64) n); - - *--t = 0; - int v = abs (n); - - do - { - *--t = (char) ('0' + (v % 10)); - v /= 10; - - } while (v > 0); - - if (n < 0) - *--t = '-'; + if (n >= 0) + return printDigits (t, static_cast (n)); + // NB: this needs to be careful not to call -std::numeric_limits::min(), + // which has undefined behaviour + t = printDigits (t, static_cast (-(n + 1)) + 1); + *--t = '-'; return t; } static char* numberToString (char* t, unsigned int v) noexcept { - *--t = 0; - - do - { - *--t = (char) ('0' + (v % 10)); - v /= 10; - - } while (v > 0); - - return t; + return printDigits (t, v); } + struct StackArrayStream : public std::basic_streambuf > + { + explicit StackArrayStream (char* d) + { + static const std::locale classicLocale (std::locale::classic()); + imbue (classicLocale); + setp (d, d + charsNeededForDouble); + } + + size_t writeDouble (double n, int numDecPlaces) + { + { + std::ostream o (this); + + if (numDecPlaces > 0) + o.precision ((std::streamsize) numDecPlaces); + + o << n; + } + + return (size_t) (pptr() - pbase()); + } + }; + static char* doubleToString (char* buffer, const int numChars, double n, int numDecPlaces, size_t& len) noexcept { - if (numDecPlaces > 0 && n > -1.0e20 && n < 1.0e20) + if (numDecPlaces > 0 && numDecPlaces < 7 && n > -1.0e20 && n < 1.0e20) { char* const end = buffer + numChars; char* t = end; @@ -440,34 +489,25 @@ namespace NumberToStringConverters len = (size_t) (end - t - 1); return t; } - else - { - // Use a locale-free sprintf where possible (not available on linux AFAICT) - #if JUCE_WINDOWS && ! JUCE_MINGW - len = (size_t) _sprintf_l (buffer, "%.9g", _create_locale (LC_NUMERIC, "C"), n); - #elif JUCE_MAC || JUCE_IOS - len = (size_t) sprintf_l (buffer, nullptr, "%.9g", n); - #else - len = (size_t) sprintf (buffer, "%.9g", n); - #endif - return buffer; - } + StackArrayStream strm (buffer); + len = strm.writeDouble (n, numDecPlaces); + jassert (len <= charsNeededForDouble); + return buffer; } template - String::CharPointerType createFromInteger (const IntegerType number) + static String::CharPointerType createFromInteger (const IntegerType number) { - char buffer [32]; + char buffer [charsNeededForInt]; char* const end = buffer + numElementsInArray (buffer); char* const start = numberToString (end, number); - return StringHolder::createFromFixedLength (start, (size_t) (end - start - 1)); } static String::CharPointerType createFromDouble (const double number, const int numberOfDecimalPlaces) { - char buffer [48]; + char buffer [charsNeededForDouble]; size_t len; char* const start = doubleToString (buffer, numElementsInArray (buffer), (double) number, numberOfDecimalPlaces, len); return StringHolder::createFromFixedLength (start, len); @@ -479,12 +519,12 @@ String::String (const int number) : text (NumberToStringConverters::c String::String (const unsigned int number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const short number) : text (NumberToStringConverters::createFromInteger ((int) number)) {} String::String (const unsigned short number) : text (NumberToStringConverters::createFromInteger ((unsigned int) number)) {} -String::String (const int64 number) : text (NumberToStringConverters::createFromInteger (number)) {} +String::String (const int64 number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const uint64 number) : text (NumberToStringConverters::createFromInteger (number)) {} -String::String (const float number) : text (NumberToStringConverters::createFromDouble ((double) number, 0)) {} +String::String (const float number) : text (NumberToStringConverters::createFromDouble ((double) number, 0)) {} String::String (const double number) : text (NumberToStringConverters::createFromDouble (number, 0)) {} -String::String (const float number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble ((double) number, numberOfDecimalPlaces)) {} +String::String (const float number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble ((double) number, numberOfDecimalPlaces)) {} String::String (const double number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble (number, numberOfDecimalPlaces)) {} //============================================================================== @@ -493,52 +533,58 @@ int String::length() const noexcept return (int) text.length(); } -size_t String::getByteOffsetOfEnd() const noexcept +static size_t findByteOffsetOfEnd (String::CharPointerType text) noexcept { return (size_t) (((char*) text.findTerminatingNull().getAddress()) - (char*) text.getAddress()); } -const juce_wchar String::operator[] (int index) const noexcept +size_t String::getByteOffsetOfEnd() const noexcept +{ + return findByteOffsetOfEnd (text); +} + +juce_wchar String::operator[] (int index) const noexcept { jassert (index == 0 || (index > 0 && index <= (int) text.lengthUpTo ((size_t) index + 1))); return text [index]; } -int String::hashCode() const noexcept +template +struct HashGenerator { - CharPointerType t (text); - int result = 0; + template + static Type calculate (CharPointer t) noexcept + { + Type result = Type(); - while (! t.isEmpty()) - result = 31 * result + (int) t.getAndAdvance(); + while (! t.isEmpty()) + result = multiplier * result + t.getAndAdvance(); - return result; -} + return result; + } -int64 String::hashCode64() const noexcept -{ - CharPointerType t (text); - int64 result = 0; + enum { multiplier = sizeof (Type) > 4 ? 101 : 31 }; +}; - while (! t.isEmpty()) - result = 101 * result + t.getAndAdvance(); - - return result; -} +int String::hashCode() const noexcept { return HashGenerator ::calculate (text); } +int64 String::hashCode64() const noexcept { return HashGenerator ::calculate (text); } +std::size_t String::hash() const noexcept { return HashGenerator::calculate (text); } //============================================================================== JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const String& s2) noexcept { return s1.compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const char* const s2) noexcept { return s1.compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* const s2) noexcept { return s1.compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8& s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16& s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } -JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32& s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const String& s2) noexcept { return s1.compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const char* const s2) noexcept { return s1.compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* const s2) noexcept { return s1.compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF8& s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF16& s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } -JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF32& s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const char* s2) noexcept { return s1.compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const char* s2) noexcept { return s1.compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* s2) noexcept { return s1.compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* s2) noexcept { return s1.compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator> (const String& s1, const String& s2) noexcept { return s1.compare (s2) > 0; } JUCE_API bool JUCE_CALLTYPE operator< (const String& s1, const String& s2) noexcept { return s1.compare (s2) < 0; } JUCE_API bool JUCE_CALLTYPE operator>= (const String& s1, const String& s2) noexcept { return s1.compare (s2) >= 0; } @@ -556,6 +602,11 @@ bool String::equalsIgnoreCase (const char* const t) const noexcept : isEmpty(); } +bool String::equalsIgnoreCase (StringRef t) const noexcept +{ + return text.compareIgnoreCase (t.text) == 0; +} + bool String::equalsIgnoreCase (const String& other) const noexcept { return text == other.text @@ -588,6 +639,31 @@ void String::append (const String& textToAppend, size_t maxCharsToTake) appendCharPointer (textToAppend.text, maxCharsToTake); } +void String::appendCharPointer (const CharPointerType textToAppend) +{ + appendCharPointer (textToAppend, textToAppend.findTerminatingNull()); +} + +void String::appendCharPointer (const CharPointerType startOfTextToAppend, + const CharPointerType endOfTextToAppend) +{ + jassert (startOfTextToAppend.getAddress() != nullptr && endOfTextToAppend.getAddress() != nullptr); + + const int extraBytesNeeded = getAddressDifference (endOfTextToAppend.getAddress(), + startOfTextToAppend.getAddress()); + jassert (extraBytesNeeded >= 0); + + if (extraBytesNeeded > 0) + { + const size_t byteOffsetOfNull = getByteOffsetOfEnd(); + preallocateBytes (byteOffsetOfNull + (size_t) extraBytesNeeded); + + CharPointerType::CharType* const newStringStart = addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull); + memcpy (newStringStart, startOfTextToAppend.getAddress(), (size_t) extraBytesNeeded); + CharPointerType (addBytesToPointer (newStringStart, extraBytesNeeded)).writeNull(); + } +} + String& String::operator+= (const wchar_t* const t) { appendCharPointer (castToCharPointer_wchar_t (t)); @@ -596,22 +672,7 @@ String& String::operator+= (const wchar_t* const t) String& String::operator+= (const char* const t) { - /* If you get an assertion here, then you're trying to create a string from 8-bit data - that contains values greater than 127. These can NOT be correctly converted to unicode - because there's no way for the String class to know what encoding was used to - create them. The source data could be UTF-8, ASCII or one of many local code-pages. - - To get around this problem, you must be more explicit when you pass an ambiguous 8-bit - string to the String class - so for example if your source data is actually UTF-8, - you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to - correctly convert the multi-byte characters to unicode. It's *highly* recommended that - you use UTF-8 with escape characters in your source code to represent extended characters, - because there's no other way to represent these strings in a way that isn't dependent on - the compiler, source code editor and platform. - */ - jassert (t == nullptr || CharPointer_ASCII::isValidString (t, std::numeric_limits::max())); - - appendCharPointer (CharPointer_ASCII (t)); + appendCharPointer (CharPointer_UTF8 (t)); // (using UTF8 here triggers a faster code-path than ascii) return *this; } @@ -648,82 +709,82 @@ String& String::operator+= (const juce_wchar ch) String& String::operator+= (const int number) { char buffer [16]; - char* const end = buffer + numElementsInArray (buffer); - char* const start = NumberToStringConverters::numberToString (end, number); + char* end = buffer + numElementsInArray (buffer); + char* start = NumberToStringConverters::numberToString (end, number); - const int numExtraChars = (int) (end - start); + #if (JUCE_STRING_UTF_TYPE == 8) + appendCharPointer (CharPointerType (start), CharPointerType (end)); + #else + appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end)); + #endif + return *this; +} - if (numExtraChars > 0) - { - const size_t byteOffsetOfNull = getByteOffsetOfEnd(); - const size_t newBytesNeeded = sizeof (CharPointerType::CharType) + byteOffsetOfNull - + sizeof (CharPointerType::CharType) * numExtraChars; - - text = StringHolder::makeUniqueWithByteSize (text, newBytesNeeded); - - CharPointerType newEnd (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)); - newEnd.writeWithCharLimit (CharPointer_ASCII (start), numExtraChars); - } +String& String::operator+= (int64 number) +{ + char buffer [32]; + char* end = buffer + numElementsInArray (buffer); + char* start = NumberToStringConverters::numberToString (end, number); + #if (JUCE_STRING_UTF_TYPE == 8) + appendCharPointer (CharPointerType (start), CharPointerType (end)); + #else + appendCharPointer (CharPointer_ASCII (start), CharPointer_ASCII (end)); + #endif return *this; } //============================================================================== -JUCE_API String JUCE_CALLTYPE operator+ (const char* const string1, const String& string2) -{ - String s (string1); - return s += string2; -} +JUCE_API String JUCE_CALLTYPE operator+ (const char* const s1, const String& s2) { String s (s1); return s += s2; } +JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t* const s1, const String& s2) { String s (s1); return s += s2; } -JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t* const string1, const String& string2) -{ - String s (string1); - return s += string2; -} +JUCE_API String JUCE_CALLTYPE operator+ (const char s1, const String& s2) { return String::charToString ((juce_wchar) (uint8) s1) + s2; } +JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t s1, const String& s2) { return String::charToString (s1) + s2; } + +JUCE_API String JUCE_CALLTYPE operator+ (String s1, const String& s2) { return s1 += s2; } +JUCE_API String JUCE_CALLTYPE operator+ (String s1, const char* const s2) { return s1 += s2; } +JUCE_API String JUCE_CALLTYPE operator+ (String s1, const wchar_t* s2) { return s1 += s2; } + +JUCE_API String JUCE_CALLTYPE operator+ (String s1, const char s2) { return s1 += s2; } +JUCE_API String JUCE_CALLTYPE operator+ (String s1, const wchar_t s2) { return s1 += s2; } -JUCE_API String JUCE_CALLTYPE operator+ (const char s1, const String& s2) { return String::charToString ((juce_wchar) (uint8) s1) + s2; } -JUCE_API String JUCE_CALLTYPE operator+ (const wchar_t s1, const String& s2) { return String::charToString (s1) + s2; } #if ! JUCE_NATIVE_WCHAR_IS_UTF32 -JUCE_API String JUCE_CALLTYPE operator+ (const juce_wchar s1, const String& s2) { return String::charToString (s1) + s2; } +JUCE_API String JUCE_CALLTYPE operator+ (const juce_wchar s1, const String& s2) { return String::charToString (s1) + s2; } +JUCE_API String JUCE_CALLTYPE operator+ (String s1, const juce_wchar s2) { return s1 += s2; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const juce_wchar s2) { return s1 += s2; } #endif -JUCE_API String JUCE_CALLTYPE operator+ (String s1, const String& s2) { return s1 += s2; } -JUCE_API String JUCE_CALLTYPE operator+ (String s1, const char* const s2) { return s1 += s2; } -JUCE_API String JUCE_CALLTYPE operator+ (String s1, const wchar_t* s2) { return s1 += s2; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char s2) { return s1 += s2; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t s2) { return s1 += s2; } -JUCE_API String JUCE_CALLTYPE operator+ (String s1, const char s2) { return s1 += s2; } -JUCE_API String JUCE_CALLTYPE operator+ (String s1, const wchar_t s2) { return s1 += s2; } -#if ! JUCE_NATIVE_WCHAR_IS_UTF32 -JUCE_API String JUCE_CALLTYPE operator+ (String s1, const juce_wchar s2) { return s1 += s2; } -#endif +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char* const s2) { return s1 += s2; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t* const s2) { return s1 += s2; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const String& s2) { return s1 += s2; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char s2) { return s1 += s2; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t s2) { return s1 += s2; } -#if ! JUCE_NATIVE_WCHAR_IS_UTF32 -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const juce_wchar s2) { return s1 += s2; } -#endif - -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char* const s2) { return s1 += s2; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t* const s2) { return s1 += s2; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const String& s2) { return s1 += s2; } - -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const short number) { return s1 += (int) number; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int number) { return s1 += number; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const long number) { return s1 += (int) number; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const float number) { return s1 += String (number); } -JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const double number) { return s1 += String (number); } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int number) { return s1 += number; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const short number) { return s1 += (int) number; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const long number) { return s1 += (int) number; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int64 number) { return s1 += String (number); } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const float number) { return s1 += String (number); } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const double number) { return s1 += String (number); } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const uint64 number) { return s1 += String (number); } JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text) { - const int numBytes = text.getNumBytesAsUTF8(); + return operator<< (stream, StringRef (text)); +} + +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef text) +{ + const size_t numBytes = CharPointer_UTF8::getBytesRequiredFor (text.text); #if (JUCE_STRING_UTF_TYPE == 8) - stream.write (text.getCharPointer().getAddress(), numBytes); + stream.write (text.text.getAddress(), numBytes); #else // (This avoids using toUTF8() to prevent the memory bloat that it would leave behind // if lots of large, persistent strings were to be written to streams). HeapBlock temp (numBytes + 1); - CharPointer_UTF8 (temp).writeAll (text.getCharPointer()); + CharPointer_UTF8 (temp).writeAll (text.text); stream.write (temp, numBytes); #endif @@ -773,7 +834,7 @@ int String::lastIndexOfChar (const juce_wchar character) const noexcept return last; } -int String::indexOfAnyOf (const String& charactersToLookFor, const int startIndex, const bool ignoreCase) const noexcept +int String::indexOfAnyOf (StringRef charactersToLookFor, const int startIndex, const bool ignoreCase) const noexcept { CharPointerType t (text); @@ -793,17 +854,17 @@ int String::indexOfAnyOf (const String& charactersToLookFor, const int startInde return -1; } -int String::indexOf (const String& other) const noexcept +int String::indexOf (StringRef other) const noexcept { return other.isEmpty() ? 0 : text.indexOf (other.text); } -int String::indexOfIgnoreCase (const String& other) const noexcept +int String::indexOfIgnoreCase (StringRef other) const noexcept { return other.isEmpty() ? 0 : CharacterFunctions::indexOfIgnoreCase (text, other.text); } -int String::indexOf (const int startIndex, const String& other) const noexcept +int String::indexOf (const int startIndex, StringRef other) const noexcept { if (other.isEmpty()) return -1; @@ -824,7 +885,7 @@ int String::indexOf (const int startIndex, const String& other) const noexcept return found; } -int String::indexOfIgnoreCase (const int startIndex, const String& other) const noexcept +int String::indexOfIgnoreCase (const int startIndex, StringRef other) const noexcept { if (other.isEmpty()) return -1; @@ -845,7 +906,7 @@ int String::indexOfIgnoreCase (const int startIndex, const String& other) const return found; } -int String::lastIndexOf (const String& other) const noexcept +int String::lastIndexOf (StringRef other) const noexcept { if (other.isNotEmpty()) { @@ -854,15 +915,12 @@ int String::lastIndexOf (const String& other) const noexcept if (i >= 0) { - CharPointerType n (text + i); - - while (i >= 0) + for (CharPointerType n (text + i); i >= 0; --i) { if (n.compareUpTo (other.text, len) == 0) return i; --n; - --i; } } } @@ -870,7 +928,7 @@ int String::lastIndexOf (const String& other) const noexcept return -1; } -int String::lastIndexOfIgnoreCase (const String& other) const noexcept +int String::lastIndexOfIgnoreCase (StringRef other) const noexcept { if (other.isNotEmpty()) { @@ -879,15 +937,12 @@ int String::lastIndexOfIgnoreCase (const String& other) const noexcept if (i >= 0) { - CharPointerType n (text + i); - - while (i >= 0) + for (CharPointerType n (text + i); i >= 0; --i) { if (n.compareIgnoreCaseUpTo (other.text, len) == 0) return i; --n; - --i; } } } @@ -895,7 +950,7 @@ int String::lastIndexOfIgnoreCase (const String& other) const noexcept return -1; } -int String::lastIndexOfAnyOf (const String& charactersToLookFor, const bool ignoreCase) const noexcept +int String::lastIndexOfAnyOf (StringRef charactersToLookFor, const bool ignoreCase) const noexcept { CharPointerType t (text); int last = -1; @@ -907,7 +962,7 @@ int String::lastIndexOfAnyOf (const String& charactersToLookFor, const bool igno return last; } -bool String::contains (const String& other) const noexcept +bool String::contains (StringRef other) const noexcept { return indexOf (other) >= 0; } @@ -917,12 +972,12 @@ bool String::containsChar (const juce_wchar character) const noexcept return text.indexOf (character) >= 0; } -bool String::containsIgnoreCase (const String& t) const noexcept +bool String::containsIgnoreCase (StringRef t) const noexcept { return indexOfIgnoreCase (t) >= 0; } -int String::indexOfWholeWord (const String& word) const noexcept +int String::indexOfWholeWord (StringRef word) const noexcept { if (word.isNotEmpty()) { @@ -944,7 +999,7 @@ int String::indexOfWholeWord (const String& word) const noexcept return -1; } -int String::indexOfWholeWordIgnoreCase (const String& word) const noexcept +int String::indexOfWholeWordIgnoreCase (StringRef word) const noexcept { if (word.isNotEmpty()) { @@ -966,12 +1021,12 @@ int String::indexOfWholeWordIgnoreCase (const String& word) const noexcept return -1; } -bool String::containsWholeWord (const String& wordToLookFor) const noexcept +bool String::containsWholeWord (StringRef wordToLookFor) const noexcept { return indexOfWholeWord (wordToLookFor) >= 0; } -bool String::containsWholeWordIgnoreCase (const String& wordToLookFor) const noexcept +bool String::containsWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept { return indexOfWholeWordIgnoreCase (wordToLookFor) >= 0; } @@ -1003,7 +1058,7 @@ struct WildCardMatcher || (ignoreCase && CharacterFunctions::toLowerCase (wc) == CharacterFunctions::toLowerCase (tc)); } - static bool matchesAnywhere (const CharPointer& wildcard, CharPointer test, const bool ignoreCase) noexcept + static bool matchesAnywhere (const CharPointer wildcard, CharPointer test, const bool ignoreCase) noexcept { for (; ! test.isEmpty(); ++test) if (matches (wildcard, test, ignoreCase)) @@ -1013,18 +1068,18 @@ struct WildCardMatcher } }; -bool String::matchesWildcard (const String& wildcard, const bool ignoreCase) const noexcept +bool String::matchesWildcard (StringRef wildcard, const bool ignoreCase) const noexcept { return WildCardMatcher::matches (wildcard.text, text, ignoreCase); } //============================================================================== -String String::repeatedString (const String& stringToRepeat, int numberOfTimesToRepeat) +String String::repeatedString (StringRef stringToRepeat, int numberOfTimesToRepeat) { if (numberOfTimesToRepeat <= 0) - return empty; + return String(); - String result (PreallocationBytes (stringToRepeat.getByteOffsetOfEnd() * numberOfTimesToRepeat)); + String result (PreallocationBytes (findByteOffsetOfEnd (stringToRepeat) * (size_t) numberOfTimesToRepeat)); CharPointerType n (result.text); while (--numberOfTimesToRepeat >= 0) @@ -1050,7 +1105,7 @@ String String::paddedLeft (const juce_wchar padCharacter, int minimumLength) con return *this; const size_t currentByteSize = (size_t) (((char*) end.getAddress()) - (char*) text.getAddress()); - String result (PreallocationBytes (currentByteSize + extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); + String result (PreallocationBytes (currentByteSize + (size_t) extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); CharPointerType n (result.text); while (--extraChars >= 0) @@ -1077,7 +1132,7 @@ String String::paddedRight (const juce_wchar padCharacter, int minimumLength) co return *this; const size_t currentByteSize = (size_t) (((char*) end.getAddress()) - (char*) text.getAddress()); - String result (PreallocationBytes (currentByteSize + extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); + String result (PreallocationBytes (currentByteSize + (size_t) extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); CharPointerType n (result.text); n.writeAll (text); @@ -1090,7 +1145,7 @@ String String::paddedRight (const juce_wchar padCharacter, int minimumLength) co } //============================================================================== -String String::replaceSection (int index, int numCharsToReplace, const String& stringToInsert) const +String String::replaceSection (int index, int numCharsToReplace, StringRef stringToInsert) const { if (index < 0) { @@ -1106,10 +1161,9 @@ String String::replaceSection (int index, int numCharsToReplace, const String& s jassertfalse; } - int i = 0; CharPointerType insertPoint (text); - while (i < index) + for (int i = 0; i < index; ++i) { if (insertPoint.isEmpty()) { @@ -1119,28 +1173,23 @@ String String::replaceSection (int index, int numCharsToReplace, const String& s } ++insertPoint; - ++i; } CharPointerType startOfRemainder (insertPoint); - i = 0; - while (i < numCharsToReplace && ! startOfRemainder.isEmpty()) - { + for (int i = 0; i < numCharsToReplace && ! startOfRemainder.isEmpty(); ++i) ++startOfRemainder; - ++i; - } if (insertPoint == text && startOfRemainder.isEmpty()) - return stringToInsert; + return stringToInsert.text; const size_t initialBytes = (size_t) (((char*) insertPoint.getAddress()) - (char*) text.getAddress()); - const size_t newStringBytes = stringToInsert.getByteOffsetOfEnd(); + const size_t newStringBytes = findByteOffsetOfEnd (stringToInsert); const size_t remainderBytes = (size_t) (((char*) startOfRemainder.findTerminatingNull().getAddress()) - (char*) startOfRemainder.getAddress()); const size_t newTotalBytes = initialBytes + newStringBytes + remainderBytes; if (newTotalBytes <= 0) - return String::empty; + return String(); String result (PreallocationBytes ((size_t) newTotalBytes)); @@ -1156,7 +1205,7 @@ String String::replaceSection (int index, int numCharsToReplace, const String& s return result; } -String String::replace (const String& stringToReplace, const String& stringToInsert, const bool ignoreCase) const +String String::replace (StringRef stringToReplace, StringRef stringToInsert, const bool ignoreCase) const { const int stringToReplaceLen = stringToReplace.length(); const int stringToInsertLen = stringToInsert.length(); @@ -1184,8 +1233,8 @@ public: dest = result.getCharPointer(); } - StringCreationHelper (const String::CharPointerType& source_) - : source (source_), dest (nullptr), allocatedBytes (StringHolder::getAllocatedNumBytes (source)), bytesWritten (0) + StringCreationHelper (const String::CharPointerType s) + : source (s), dest (nullptr), allocatedBytes (StringHolder::getAllocatedNumBytes (s)), bytesWritten (0) { result.preallocateBytes (allocatedBytes); dest = result.getCharPointer(); @@ -1237,7 +1286,7 @@ String String::replaceCharacter (const juce_wchar charToReplace, const juce_wcha return builder.result; } -String String::replaceCharacters (const String& charactersToReplace, const String& charactersToInsertInstead) const +String String::replaceCharacters (StringRef charactersToReplace, StringRef charactersToInsertInstead) const { StringCreationHelper builder (text); @@ -1245,7 +1294,7 @@ String String::replaceCharacters (const String& charactersToReplace, const Strin { juce_wchar c = builder.source.getAndAdvance(); - const int index = charactersToReplace.indexOfChar (c); + const int index = charactersToReplace.text.indexOf (c); if (index >= 0) c = charactersToInsertInstead [index]; @@ -1259,12 +1308,12 @@ String String::replaceCharacters (const String& charactersToReplace, const Strin } //============================================================================== -bool String::startsWith (const String& other) const noexcept +bool String::startsWith (StringRef other) const noexcept { return text.compareUpTo (other.text, other.length()) == 0; } -bool String::startsWithIgnoreCase (const String& other) const noexcept +bool String::startsWithIgnoreCase (StringRef other) const noexcept { return text.compareIgnoreCaseUpTo (other.text, other.length()) == 0; } @@ -1287,7 +1336,7 @@ bool String::endsWithChar (const juce_wchar character) const noexcept return *--t == character; } -bool String::endsWith (const String& other) const noexcept +bool String::endsWith (StringRef other) const noexcept { CharPointerType end (text.findTerminatingNull()); CharPointerType otherEnd (other.text.findTerminatingNull()); @@ -1304,7 +1353,7 @@ bool String::endsWith (const String& other) const noexcept return otherEnd == other.text; } -bool String::endsWithIgnoreCase (const String& other) const noexcept +bool String::endsWithIgnoreCase (StringRef other) const noexcept { CharPointerType end (text.findTerminatingNull()); CharPointerType otherEnd (other.text.findTerminatingNull()); @@ -1329,11 +1378,12 @@ String String::toUpperCase() const for (;;) { const juce_wchar c = builder.source.toUpperCase(); - ++(builder.source); builder.write (c); if (c == 0) break; + + ++(builder.source); } return builder.result; @@ -1346,11 +1396,12 @@ String String::toLowerCase() const for (;;) { const juce_wchar c = builder.source.toLowerCase(); - ++(builder.source); builder.write (c); if (c == 0) break; + + ++(builder.source); } return builder.result; @@ -1368,7 +1419,7 @@ String String::substring (int start, const int end) const start = 0; if (end <= start) - return empty; + return String(); int i = 0; CharPointerType t1 (text); @@ -1376,7 +1427,7 @@ String String::substring (int start, const int end) const while (i < start) { if (t1.isEmpty()) - return empty; + return String(); ++i; ++t1; @@ -1410,7 +1461,7 @@ String String::substring (int start) const while (--start >= 0) { if (t.isEmpty()) - return empty; + return String(); ++t; } @@ -1428,19 +1479,19 @@ String String::getLastCharacters (const int numCharacters) const return String (text + jmax (0, length() - jmax (0, numCharacters))); } -String String::fromFirstOccurrenceOf (const String& sub, +String String::fromFirstOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? indexOfIgnoreCase (sub) : indexOf (sub); if (i < 0) - return empty; + return String(); return substring (includeSubString ? i : i + sub.length()); } -String String::fromLastOccurrenceOf (const String& sub, +String String::fromLastOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { @@ -1452,7 +1503,7 @@ String String::fromLastOccurrenceOf (const String& sub, return substring (includeSubString ? i : i + sub.length()); } -String String::upToFirstOccurrenceOf (const String& sub, +String String::upToFirstOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { @@ -1464,7 +1515,7 @@ String String::upToFirstOccurrenceOf (const String& sub, return substring (0, includeSubString ? i + sub.length() : i); } -String String::upToLastOccurrenceOf (const String& sub, +String String::upToLastOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { @@ -1489,7 +1540,7 @@ String String::unquoted() const const int len = length(); if (len == 0) - return empty; + return String(); const juce_wchar lastChar = text [len - 1]; const int dropAtStart = (*text == '"' || *text == '\'') ? 1 : 0; @@ -1515,7 +1566,8 @@ String String::quoted (const juce_wchar quoteCharacter) const } //============================================================================== -static String::CharPointerType findTrimmedEnd (const String::CharPointerType& start, String::CharPointerType end) +static String::CharPointerType findTrimmedEnd (const String::CharPointerType start, + String::CharPointerType end) { while (end > start) { @@ -1539,8 +1591,9 @@ String String::trim() const CharPointerType trimmedEnd (findTrimmedEnd (start, end)); if (trimmedEnd <= start) - return empty; - else if (text < start || trimmedEnd < end) + return String(); + + if (text < start || trimmedEnd < end) return String (start, trimmedEnd); } @@ -1574,17 +1627,17 @@ String String::trimEnd() const return *this; } -String String::trimCharactersAtStart (const String& charactersToTrim) const +String String::trimCharactersAtStart (StringRef charactersToTrim) const { CharPointerType t (text); - while (charactersToTrim.containsChar (*t)) + while (charactersToTrim.text.indexOf (*t) >= 0) ++t; return t == text ? *this : String (t); } -String String::trimCharactersAtEnd (const String& charactersToTrim) const +String String::trimCharactersAtEnd (StringRef charactersToTrim) const { if (isNotEmpty()) { @@ -1593,7 +1646,7 @@ String String::trimCharactersAtEnd (const String& charactersToTrim) const while (trimmedEnd > text) { - if (! charactersToTrim.containsChar (*--trimmedEnd)) + if (charactersToTrim.text.indexOf (*--trimmedEnd) < 0) { ++trimmedEnd; break; @@ -1608,10 +1661,10 @@ String String::trimCharactersAtEnd (const String& charactersToTrim) const } //============================================================================== -String String::retainCharacters (const String& charactersToRetain) const +String String::retainCharacters (StringRef charactersToRetain) const { if (isEmpty()) - return empty; + return String(); StringCreationHelper builder (text); @@ -1619,7 +1672,7 @@ String String::retainCharacters (const String& charactersToRetain) const { juce_wchar c = builder.source.getAndAdvance(); - if (charactersToRetain.containsChar (c)) + if (charactersToRetain.text.indexOf (c) >= 0) builder.write (c); if (c == 0) @@ -1630,10 +1683,10 @@ String String::retainCharacters (const String& charactersToRetain) const return builder.result; } -String String::removeCharacters (const String& charactersToRemove) const +String String::removeCharacters (StringRef charactersToRemove) const { if (isEmpty()) - return empty; + return String(); StringCreationHelper builder (text); @@ -1641,7 +1694,7 @@ String String::removeCharacters (const String& charactersToRemove) const { juce_wchar c = builder.source.getAndAdvance(); - if (! charactersToRemove.containsChar (c)) + if (charactersToRemove.text.indexOf (c) < 0) builder.write (c); if (c == 0) @@ -1651,53 +1704,37 @@ String String::removeCharacters (const String& charactersToRemove) const return builder.result; } -String String::initialSectionContainingOnly (const String& permittedCharacters) const +String String::initialSectionContainingOnly (StringRef permittedCharacters) const { - CharPointerType t (text); - - while (! t.isEmpty()) - { - if (! permittedCharacters.containsChar (*t)) + for (CharPointerType t (text); ! t.isEmpty(); ++t) + if (permittedCharacters.text.indexOf (*t) < 0) return String (text, t); - ++t; - } - return *this; } -String String::initialSectionNotContaining (const String& charactersToStopAt) const +String String::initialSectionNotContaining (StringRef charactersToStopAt) const { - CharPointerType t (text); - - while (! t.isEmpty()) - { - if (charactersToStopAt.containsChar (*t)) + for (CharPointerType t (text); ! t.isEmpty(); ++t) + if (charactersToStopAt.text.indexOf (*t) >= 0) return String (text, t); - ++t; - } - return *this; } -bool String::containsOnly (const String& chars) const noexcept +bool String::containsOnly (StringRef chars) const noexcept { - CharPointerType t (text); - - while (! t.isEmpty()) - if (! chars.containsChar (t.getAndAdvance())) + for (CharPointerType t (text); ! t.isEmpty();) + if (chars.text.indexOf (t.getAndAdvance()) < 0) return false; return true; } -bool String::containsAnyOf (const String& chars) const noexcept +bool String::containsAnyOf (StringRef chars) const noexcept { - CharPointerType t (text); - - while (! t.isEmpty()) - if (chars.containsChar (t.getAndAdvance())) + for (CharPointerType t (text); ! t.isEmpty();) + if (chars.text.indexOf (t.getAndAdvance()) >= 0) return true; return false; @@ -1705,16 +1742,10 @@ bool String::containsAnyOf (const String& chars) const noexcept bool String::containsNonWhitespaceChars() const noexcept { - CharPointerType t (text); - - while (! t.isEmpty()) - { + for (CharPointerType t (text); ! t.isEmpty(); ++t) if (! t.isWhitespace()) return true; - ++t; - } - return false; } @@ -1750,14 +1781,14 @@ String String::formatted (const String pf, ... ) break; // returns -1 because of an error rather than because it needs more space. } - return empty; + return String(); } //============================================================================== -int String::getIntValue() const noexcept -{ - return text.getIntValue32(); -} +int String::getIntValue() const noexcept { return text.getIntValue32(); } +int64 String::getLargeIntValue() const noexcept { return text.getIntValue64(); } +float String::getFloatValue() const noexcept { return (float) getDoubleValue(); } +double String::getDoubleValue() const noexcept { return text.getDoubleValue(); } int String::getTrailingIntValue() const noexcept { @@ -1782,78 +1813,35 @@ int String::getTrailingIntValue() const noexcept return n; } -int64 String::getLargeIntValue() const noexcept -{ - return text.getIntValue64(); -} - -float String::getFloatValue() const noexcept -{ - return (float) getDoubleValue(); -} - -double String::getDoubleValue() const noexcept -{ - return text.getDoubleValue(); -} - static const char hexDigits[] = "0123456789abcdef"; template -struct HexConverter +static String hexToString (Type v) { - static String hexToString (Type v) + String::CharPointerType::CharType buffer[32]; + String::CharPointerType::CharType* const end = buffer + numElementsInArray (buffer) - 1; + String::CharPointerType::CharType* t = end; + *t = 0; + + do { - char buffer[32]; - char* const end = buffer + 32; - char* t = end; - *--t = 0; + *--t = hexDigits [(int) (v & 15)]; + v >>= 4; - do - { - *--t = hexDigits [(int) (v & 15)]; - v >>= 4; + } while (v != 0); - } while (v != 0); - - return String (t, (size_t) (end - t) - 1); - } - - static Type stringToHex (String::CharPointerType t) noexcept - { - Type result = 0; - - while (! t.isEmpty()) - { - const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); - - if (hexValue >= 0) - result = (result << 4) | hexValue; - } - - return result; - } -}; - -String String::toHexString (const int number) -{ - return HexConverter ::hexToString ((unsigned int) number); + return String (String::CharPointerType (t), + String::CharPointerType (end)); } -String String::toHexString (const int64 number) -{ - return HexConverter ::hexToString ((uint64) number); -} - -String String::toHexString (const short number) -{ - return toHexString ((int) (unsigned short) number); -} +String String::toHexString (int number) { return hexToString ((unsigned int) number); } +String String::toHexString (int64 number) { return hexToString ((uint64) number); } +String String::toHexString (short number) { return toHexString ((int) (unsigned short) number); } String String::toHexString (const void* const d, const int size, const int groupSize) { if (size <= 0) - return empty; + return String(); int numChars = (size * 2) + 2; if (groupSize > 0) @@ -1878,40 +1866,30 @@ String String::toHexString (const void* const d, const int size, const int group return s; } -int String::getHexValue32() const noexcept -{ - return HexConverter::stringToHex (text); -} - -int64 String::getHexValue64() const noexcept -{ - return HexConverter::stringToHex (text); -} +int String::getHexValue32() const noexcept { return CharacterFunctions::HexParser ::parse (text); } +int64 String::getHexValue64() const noexcept { return CharacterFunctions::HexParser::parse (text); } //============================================================================== -String String::createStringFromData (const void* const data_, const int size) +String String::createStringFromData (const void* const unknownData, const int size) { - const uint8* const data = static_cast (data_); + const uint8* const data = static_cast (unknownData); if (size <= 0 || data == nullptr) - { - return empty; - } - else if (size == 1) - { + return String(); + + if (size == 1) return charToString ((juce_wchar) data[0]); - } - else if ((data[0] == (uint8) CharPointer_UTF16::byteOrderMarkBE1 && data[1] == (uint8) CharPointer_UTF16::byteOrderMarkBE2) - || (data[0] == (uint8) CharPointer_UTF16::byteOrderMarkLE1 && data[1] == (uint8) CharPointer_UTF16::byteOrderMarkLE2)) + + if (CharPointer_UTF16::isByteOrderMarkBigEndian (data) + || CharPointer_UTF16::isByteOrderMarkLittleEndian (data)) { - const bool bigEndian = (data[0] == (uint8) CharPointer_UTF16::byteOrderMarkBE1); const int numChars = size / 2 - 1; StringCreationHelper builder ((size_t) numChars); const uint16* const src = (const uint16*) (data + 2); - if (bigEndian) + if (CharPointer_UTF16::isByteOrderMarkBigEndian (data)) { for (int i = 0; i < numChars; ++i) builder.write ((juce_wchar) ByteOrder::swapIfLittleEndian (src[i])); @@ -1925,20 +1903,14 @@ String String::createStringFromData (const void* const data_, const int size) builder.write (0); return builder.result; } - else - { - const uint8* start = data; - const uint8* end = data + size; - if (size >= 3 - && data[0] == (uint8) CharPointer_UTF8::byteOrderMark1 - && data[1] == (uint8) CharPointer_UTF8::byteOrderMark2 - && data[2] == (uint8) CharPointer_UTF8::byteOrderMark3) - start += 3; + const uint8* start = data; - return String (CharPointer_UTF8 ((const char*) start), - CharPointer_UTF8 ((const char*) end)); - } + if (size >= 3 && CharPointer_UTF8::isByteOrderMark (data)) + start += 3; + + return String (CharPointer_UTF8 ((const char*) start), + CharPointer_UTF8 ((const char*) (data + size))); } //============================================================================== @@ -1957,8 +1929,8 @@ struct StringEncodingConverter return CharPointerType_Dest (reinterpret_cast (&emptyChar)); CharPointerType_Src text (source.getCharPointer()); - const size_t extraBytesNeeded = CharPointerType_Dest::getBytesRequiredFor (text); - const size_t endOffset = (text.sizeInBytes() + 3) & ~3; // the new string must be word-aligned or many Windows + const size_t extraBytesNeeded = CharPointerType_Dest::getBytesRequiredFor (text) + sizeof (typename CharPointerType_Dest::CharType); + const size_t endOffset = (text.sizeInBytes() + 3) & ~3u; // the new string must be word-aligned or many Windows // functions will fail to read it correctly! source.preallocateBytes (endOffset + extraBytesNeeded); text = source.getCharPointer(); @@ -1967,8 +1939,8 @@ struct StringEncodingConverter const CharPointerType_Dest extraSpace (static_cast (newSpace)); #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) - const int bytesToClear = jmin ((int) extraBytesNeeded, 4); - zeromem (addBytesToPointer (newSpace, (int) (extraBytesNeeded - bytesToClear)), (size_t) bytesToClear); + const size_t bytesToClear = (size_t) jmin ((int) extraBytesNeeded, 4); + zeromem (addBytesToPointer (newSpace, extraBytesNeeded - bytesToClear), bytesToClear); #endif CharPointerType_Dest (extraSpace).writeAll (text); @@ -1977,66 +1949,76 @@ struct StringEncodingConverter }; template <> -struct StringEncodingConverter +struct StringEncodingConverter { static CharPointer_UTF8 convert (const String& source) noexcept { return CharPointer_UTF8 ((CharPointer_UTF8::CharType*) source.getCharPointer().getAddress()); } }; template <> -struct StringEncodingConverter +struct StringEncodingConverter { static CharPointer_UTF16 convert (const String& source) noexcept { return CharPointer_UTF16 ((CharPointer_UTF16::CharType*) source.getCharPointer().getAddress()); } }; template <> -struct StringEncodingConverter +struct StringEncodingConverter { static CharPointer_UTF32 convert (const String& source) noexcept { return CharPointer_UTF32 ((CharPointer_UTF32::CharType*) source.getCharPointer().getAddress()); } }; -CharPointer_UTF8 String::toUTF8() const { return StringEncodingConverter ::convert (*this); } -CharPointer_UTF16 String::toUTF16() const { return StringEncodingConverter ::convert (*this); } -CharPointer_UTF32 String::toUTF32() const { return StringEncodingConverter ::convert (*this); } +CharPointer_UTF8 String::toUTF8() const { return StringEncodingConverter::convert (*this); } +CharPointer_UTF16 String::toUTF16() const { return StringEncodingConverter::convert (*this); } +CharPointer_UTF32 String::toUTF32() const { return StringEncodingConverter::convert (*this); } + +const char* String::toRawUTF8() const +{ + return toUTF8().getAddress(); +} const wchar_t* String::toWideCharPointer() const { - return StringEncodingConverter ::convert (*this).getAddress(); + return StringEncodingConverter::convert (*this).getAddress(); +} + +std::string String::toStdString() const +{ + return std::string (toRawUTF8()); } //============================================================================== template struct StringCopier { - static int copyToBuffer (const CharPointerType_Src& source, typename CharPointerType_Dest::CharType* const buffer, const int maxBufferSizeBytes) + static size_t copyToBuffer (const CharPointerType_Src source, typename CharPointerType_Dest::CharType* const buffer, const size_t maxBufferSizeBytes) { - jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! + jassert (((ssize_t) maxBufferSizeBytes) >= 0); // keep this value positive! if (buffer == nullptr) - return (int) (CharPointerType_Dest::getBytesRequiredFor (source) + sizeof (typename CharPointerType_Dest::CharType)); + return CharPointerType_Dest::getBytesRequiredFor (source) + sizeof (typename CharPointerType_Dest::CharType); return CharPointerType_Dest (buffer).writeWithDestByteLimit (source, maxBufferSizeBytes); } }; -int String::copyToUTF8 (CharPointer_UTF8::CharType* const buffer, const int maxBufferSizeBytes) const noexcept +size_t String::copyToUTF8 (CharPointer_UTF8::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { - return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); + return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } -int String::copyToUTF16 (CharPointer_UTF16::CharType* const buffer, int maxBufferSizeBytes) const noexcept +size_t String::copyToUTF16 (CharPointer_UTF16::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { - return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); + return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } -int String::copyToUTF32 (CharPointer_UTF32::CharType* const buffer, int maxBufferSizeBytes) const noexcept +size_t String::copyToUTF32 (CharPointer_UTF32::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { - return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); + return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } //============================================================================== -int String::getNumBytesAsUTF8() const noexcept +size_t String::getNumBytesAsUTF8() const noexcept { - return (int) CharPointer_UTF8::getBytesRequiredFor (text); + return CharPointer_UTF8::getBytesRequiredFor (text); } String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) @@ -2045,17 +2027,65 @@ String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) { if (bufferSizeBytes < 0) return String (CharPointer_UTF8 (buffer)); - else if (bufferSizeBytes > 0) + + if (bufferSizeBytes > 0) + { + jassert (CharPointer_UTF8::isValidString (buffer, bufferSizeBytes)); return String (CharPointer_UTF8 (buffer), CharPointer_UTF8 (buffer + bufferSizeBytes)); + } } - return String::empty; + return String(); } #if JUCE_MSVC #pragma warning (pop) #endif +//============================================================================== +StringRef::StringRef() noexcept : text ((const String::CharPointerType::CharType*) "\0\0\0") +{ +} + +StringRef::StringRef (const char* stringLiteral) noexcept + #if JUCE_STRING_UTF_TYPE != 8 + : text (nullptr), stringCopy (stringLiteral) + #else + : text (stringLiteral) + #endif +{ + #if JUCE_STRING_UTF_TYPE != 8 + text = stringCopy.getCharPointer(); + #endif + + jassert (stringLiteral != nullptr); // This must be a valid string literal, not a null pointer!! + + #if JUCE_NATIVE_WCHAR_IS_UTF8 + /* If you get an assertion here, then you're trying to create a string from 8-bit data + that contains values greater than 127. These can NOT be correctly converted to unicode + because there's no way for the String class to know what encoding was used to + create them. The source data could be UTF-8, ASCII or one of many local code-pages. + + To get around this problem, you must be more explicit when you pass an ambiguous 8-bit + string to the StringRef class - so for example if your source data is actually UTF-8, + you'd call StringRef (CharPointer_UTF8 ("my utf8 string..")), and it would be able to + correctly convert the multi-byte characters to unicode. It's *highly* recommended that + you use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent these strings in a way that isn't dependent on + the compiler, source code editor and platform. + */ + jassert (CharPointer_ASCII::isValidString (stringLiteral, std::numeric_limits::max())); + #endif +} + +StringRef::StringRef (String::CharPointerType stringLiteral) noexcept : text (stringLiteral) +{ + jassert (stringLiteral.getAddress() != nullptr); // This must be a valid string literal, not a null pointer!! +} + +StringRef::StringRef (const String& string) noexcept : text (string.getCharPointer()) {} + + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -2068,9 +2098,9 @@ public: template struct TestUTFConversion { - static void test (UnitTest& test) + static void test (UnitTest& test, Random& r) { - String s (createRandomWideCharString()); + String s (createRandomWideCharString (r)); typename CharPointerType::CharType buffer [300]; @@ -2085,13 +2115,14 @@ public: memset (buffer, 0xff, sizeof (buffer)); CharPointerType (buffer).writeAll (s.toUTF8()); test.expectEquals (String (CharPointerType (buffer)), s); + + test.expect (CharPointerType::isValidString (buffer, (int) strlen ((const char*) buffer))); } }; - static String createRandomWideCharString() + static String createRandomWideCharString (Random& r) { juce_wchar buffer[50] = { 0 }; - Random r; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { @@ -2112,6 +2143,8 @@ public: void runTest() { + Random r = getRandom(); + { beginTest ("Basics"); @@ -2164,12 +2197,12 @@ public: expect (s.substring (0, 100) == s); expect (s.substring (-1, 100) == s); expect (s.substring (3) == "345678"); - expect (s.indexOf (L"45") == 4); + expect (s.indexOf (String (L"45")) == 4); expect (String ("444445").indexOf ("45") == 4); expect (String ("444445").lastIndexOfChar ('4') == 4); - expect (String ("45454545x").lastIndexOf (L"45") == 6); + expect (String ("45454545x").lastIndexOf (String (L"45")) == 6); expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7); - expect (String ("45454545x").lastIndexOfAnyOf (L"456x") == 8); + expect (String ("45454545x").lastIndexOfAnyOf (String (L"456x")) == 8); expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("aB") == 6); expect (s.indexOfChar (L'4') == 4); expect (s + s == "012345678012345678"); @@ -2190,6 +2223,10 @@ public: s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; s2 += "xyz"; expect (s2 == "1234567890xyz"); + s2 += (int) 123; + expect (s2 == "1234567890xyz123"); + s2 += (int64) 123; + expect (s2 == "1234567890xyz123123"); beginTest ("Numeric conversions"); expect (String::empty.getIntValue() == 0); @@ -2203,6 +2240,10 @@ public: expect (String ((int64) -1234).getLargeIntValue() == -1234); expect (String (-1234.56).getDoubleValue() == -1234.56); expect (String (-1234.56f).getFloatValue() == -1234.56f); + expect (String (std::numeric_limits::max()).getIntValue() == std::numeric_limits::max()); + expect (String (std::numeric_limits::min()).getIntValue() == std::numeric_limits::min()); + expect (String (std::numeric_limits::max()).getLargeIntValue() == std::numeric_limits::max()); + expect (String (std::numeric_limits::min()).getLargeIntValue() == std::numeric_limits::min()); expect (("xyz" + s).getTrailingIntValue() == s.getIntValue()); expect (s.getHexValue32() == 0x12345678); expect (s.getHexValue64() == (int64) 0x12345678); @@ -2222,21 +2263,22 @@ public: expect (s3.compareIgnoreCase (L"ABCdeFGhiJ") == 0); expect (s3.containsIgnoreCase (s3.substring (3))); expect (s3.indexOfAnyOf ("xyzf", 2, true) == 5); - expect (s3.indexOfAnyOf (L"xyzf", 2, false) == -1); + expect (s3.indexOfAnyOf (String (L"xyzf"), 2, false) == -1); expect (s3.indexOfAnyOf ("xyzF", 2, false) == 5); - expect (s3.containsAnyOf (L"zzzFs")); + expect (s3.containsAnyOf (String (L"zzzFs"))); expect (s3.startsWith ("abcd")); - expect (s3.startsWithIgnoreCase (L"abCD")); + expect (s3.startsWithIgnoreCase (String (L"abCD"))); expect (s3.startsWith (String::empty)); expect (s3.startsWithChar ('a')); expect (s3.endsWith (String ("HIJ"))); - expect (s3.endsWithIgnoreCase (L"Hij")); + expect (s3.endsWithIgnoreCase (String (L"Hij"))); expect (s3.endsWith (String::empty)); expect (s3.endsWithChar (L'J')); expect (s3.indexOf ("HIJ") == 7); - expect (s3.indexOf (L"HIJK") == -1); + expect (s3.indexOf (String (L"HIJK")) == -1); expect (s3.indexOfIgnoreCase ("hij") == 7); - expect (s3.indexOfIgnoreCase (L"hijk") == -1); + expect (s3.indexOfIgnoreCase (String (L"hijk")) == -1); + expect (s3.toStdString() == s3.toRawUTF8()); String s4 (s3); s4.append (String ("xyz123"), 3); @@ -2252,25 +2294,25 @@ public: String s5 ("word word2 word3"); expect (s5.containsWholeWord (String ("word2"))); expect (s5.indexOfWholeWord ("word2") == 5); - expect (s5.containsWholeWord (L"word")); + expect (s5.containsWholeWord (String (L"word"))); expect (s5.containsWholeWord ("word3")); expect (s5.containsWholeWord (s5)); - expect (s5.containsWholeWordIgnoreCase (L"Word2")); + expect (s5.containsWholeWordIgnoreCase (String (L"Word2"))); expect (s5.indexOfWholeWordIgnoreCase ("Word2") == 5); - expect (s5.containsWholeWordIgnoreCase (L"Word")); + expect (s5.containsWholeWordIgnoreCase (String (L"Word"))); expect (s5.containsWholeWordIgnoreCase ("Word3")); - expect (! s5.containsWholeWordIgnoreCase (L"Wordx")); - expect (!s5.containsWholeWordIgnoreCase ("xWord2")); + expect (! s5.containsWholeWordIgnoreCase (String (L"Wordx"))); + expect (! s5.containsWholeWordIgnoreCase ("xWord2")); expect (s5.containsNonWhitespaceChars()); expect (s5.containsOnly ("ordw23 ")); expect (! String (" \n\r\t").containsNonWhitespaceChars()); - expect (s5.matchesWildcard (L"wor*", false)); + expect (s5.matchesWildcard (String (L"wor*"), false)); expect (s5.matchesWildcard ("wOr*", true)); - expect (s5.matchesWildcard (L"*word3", true)); + expect (s5.matchesWildcard (String (L"*word3"), true)); expect (s5.matchesWildcard ("*word?", true)); - expect (s5.matchesWildcard (L"Word*3", true)); - expect (! s5.matchesWildcard (L"*34", true)); + expect (s5.matchesWildcard (String (L"Word*3"), true)); + expect (! s5.matchesWildcard (String (L"*34"), true)); expect (String ("xx**y").matchesWildcard ("*y", true)); expect (String ("xx**y").matchesWildcard ("x*y", true)); expect (String ("xx**y").matchesWildcard ("xx*y", true)); @@ -2282,23 +2324,23 @@ public: expectEquals (s5.fromFirstOccurrenceOf (String::empty, true, false), s5); expectEquals (s5.fromFirstOccurrenceOf ("xword2", true, false), s5.substring (100)); - expectEquals (s5.fromFirstOccurrenceOf (L"word2", true, false), s5.substring (5)); + expectEquals (s5.fromFirstOccurrenceOf (String (L"word2"), true, false), s5.substring (5)); expectEquals (s5.fromFirstOccurrenceOf ("Word2", true, true), s5.substring (5)); expectEquals (s5.fromFirstOccurrenceOf ("word2", false, false), s5.getLastCharacters (6)); - expectEquals (s5.fromFirstOccurrenceOf (L"Word2", false, true), s5.getLastCharacters (6)); + expectEquals (s5.fromFirstOccurrenceOf ("Word2", false, true), s5.getLastCharacters (6)); expectEquals (s5.fromLastOccurrenceOf (String::empty, true, false), s5); - expectEquals (s5.fromLastOccurrenceOf (L"wordx", true, false), s5); + expectEquals (s5.fromLastOccurrenceOf ("wordx", true, false), s5); expectEquals (s5.fromLastOccurrenceOf ("word", true, false), s5.getLastCharacters (5)); - expectEquals (s5.fromLastOccurrenceOf (L"worD", true, true), s5.getLastCharacters (5)); + expectEquals (s5.fromLastOccurrenceOf ("worD", true, true), s5.getLastCharacters (5)); expectEquals (s5.fromLastOccurrenceOf ("word", false, false), s5.getLastCharacters (1)); - expectEquals (s5.fromLastOccurrenceOf (L"worD", false, true), s5.getLastCharacters (1)); + expectEquals (s5.fromLastOccurrenceOf ("worD", false, true), s5.getLastCharacters (1)); expect (s5.upToFirstOccurrenceOf (String::empty, true, false).isEmpty()); expectEquals (s5.upToFirstOccurrenceOf ("word4", true, false), s5); - expectEquals (s5.upToFirstOccurrenceOf (L"word2", true, false), s5.substring (0, 10)); + expectEquals (s5.upToFirstOccurrenceOf ("word2", true, false), s5.substring (0, 10)); expectEquals (s5.upToFirstOccurrenceOf ("Word2", true, true), s5.substring (0, 10)); - expectEquals (s5.upToFirstOccurrenceOf (L"word2", false, false), s5.substring (0, 5)); + expectEquals (s5.upToFirstOccurrenceOf ("word2", false, false), s5.substring (0, 5)); expectEquals (s5.upToFirstOccurrenceOf ("Word2", false, true), s5.substring (0, 5)); expectEquals (s5.upToLastOccurrenceOf (String::empty, true, false), s5); @@ -2309,15 +2351,15 @@ public: expectEquals (s5.upToLastOccurrenceOf ("word", false, false), s5.dropLastCharacters (5)); expectEquals (s5.upToLastOccurrenceOf ("Word", false, true), s5.dropLastCharacters (5)); - expectEquals (s5.replace ("word", L"xyz", false), String ("xyz xyz2 xyz3")); - expect (s5.replace (L"Word", "xyz", true) == "xyz xyz2 xyz3"); + expectEquals (s5.replace ("word", "xyz", false), String ("xyz xyz2 xyz3")); + expect (s5.replace ("Word", "xyz", true) == "xyz xyz2 xyz3"); expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz"); expect (s5.replace ("Word", "", true) == " 2 3"); - expectEquals (s5.replace ("Word2", L"xyz", true), String ("word xyz word3")); + expectEquals (s5.replace ("Word2", "xyz", true), String ("word xyz word3")); expect (s5.replaceCharacter (L'w', 'x') != s5); expectEquals (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w'), s5); expect (s5.replaceCharacters ("wo", "xy") != s5); - expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo"), s5); + expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", "wo"), s5); expectEquals (s5.retainCharacters ("1wordxya"), String ("wordwordword")); expect (s5.retainCharacters (String::empty).isEmpty()); expect (s5.removeCharacters ("1wordxya") == " 2 3"); @@ -2347,9 +2389,9 @@ public: { beginTest ("UTF conversions"); - TestUTFConversion ::test (*this); - TestUTFConversion ::test (*this); - TestUTFConversion ::test (*this); + TestUTFConversion ::test (*this, r); + TestUTFConversion ::test (*this, r); + TestUTFConversion ::test (*this, r); } { @@ -2381,6 +2423,28 @@ public: expectEquals (toks.size(), 3); expectEquals (toks.joinIntoString ("-"), String ("x-'y,z'-")); } + + { + beginTest ("var"); + + var v1 = 0; + var v2 = 0.1; + var v3 = "0.1"; + var v4 = (int64) 0; + var v5 = 0.0; + expect (! v2.equals (v1)); + expect (! v1.equals (v2)); + expect (v2.equals (v3)); + expect (v3.equals (v2)); + expect (! v3.equals (v1)); + expect (! v1.equals (v3)); + expect (v1.equals (v4)); + expect (v4.equals (v1)); + expect (v5.equals (v4)); + expect (v4.equals (v5)); + expect (! v2.equals (v4)); + expect (! v4.equals (v2)); + } } }; diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.h b/JuceLibraryCode/modules/juce_core/text/juce_String.h index 032fb4e..1827d54 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.h @@ -1,53 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_STRING_JUCEHEADER__ -#define __JUCE_STRING_JUCEHEADER__ +#ifndef JUCE_STRING_H_INCLUDED +#define JUCE_STRING_H_INCLUDED -#include "juce_CharacterFunctions.h" - -#ifndef JUCE_STRING_UTF_TYPE - #define JUCE_STRING_UTF_TYPE 8 -#endif - -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514 4996) -#endif - -#include "../memory/juce_Atomic.h" -#include "juce_CharPointer_UTF8.h" -#include "juce_CharPointer_UTF16.h" -#include "juce_CharPointer_UTF32.h" -#include "juce_CharPointer_ASCII.h" - -#if JUCE_MSVC - #pragma warning (pop) -#endif - -class OutputStream; //============================================================================== /** @@ -103,7 +84,7 @@ public: because there's no other way to represent unicode strings in a way that isn't dependent on the compiler, source code editor and platform. - This will use up the the first maxChars characters of the string (or less if the string + This will use up to the first maxChars characters of the string (or less if the string is actually shorter). */ String (const char* text, size_t maxChars); @@ -120,37 +101,40 @@ public: //============================================================================== /** Creates a string from a UTF-8 character string */ - String (const CharPointer_UTF8& text); + String (const CharPointer_UTF8 text); /** Creates a string from a UTF-8 character string */ - String (const CharPointer_UTF8& text, size_t maxChars); + String (const CharPointer_UTF8 text, size_t maxChars); /** Creates a string from a UTF-8 character string */ - String (const CharPointer_UTF8& start, const CharPointer_UTF8& end); + String (const CharPointer_UTF8 start, const CharPointer_UTF8 end); //============================================================================== /** Creates a string from a UTF-16 character string */ - String (const CharPointer_UTF16& text); + String (const CharPointer_UTF16 text); /** Creates a string from a UTF-16 character string */ - String (const CharPointer_UTF16& text, size_t maxChars); + String (const CharPointer_UTF16 text, size_t maxChars); /** Creates a string from a UTF-16 character string */ - String (const CharPointer_UTF16& start, const CharPointer_UTF16& end); + String (const CharPointer_UTF16 start, const CharPointer_UTF16 end); //============================================================================== /** Creates a string from a UTF-32 character string */ - String (const CharPointer_UTF32& text); + String (const CharPointer_UTF32 text); /** Creates a string from a UTF-32 character string */ - String (const CharPointer_UTF32& text, size_t maxChars); + String (const CharPointer_UTF32 text, size_t maxChars); /** Creates a string from a UTF-32 character string */ - String (const CharPointer_UTF32& start, const CharPointer_UTF32& end); + String (const CharPointer_UTF32 start, const CharPointer_UTF32 end); //============================================================================== /** Creates a string from an ASCII character string */ - String (const CharPointer_ASCII& text); + String (const CharPointer_ASCII text); + + /** Creates a string from a UTF-8 encoded std::string. */ + String (const std::string&); //============================================================================== /** Creates a string from a single character. */ @@ -196,6 +180,9 @@ public: /** Generates a probably-unique 64-bit hashcode from this string. */ int64 hashCode64() const noexcept; + /** Generates a probably-unique hashcode from this string. */ + std::size_t hash() const noexcept; + /** Returns the number of characters in the string. */ int length() const noexcept; @@ -217,6 +204,8 @@ public: String& operator+= (const wchar_t* textToAppend); /** 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+= (int64 numberToAppend); /** Appends a character at the end of this string. */ String& operator+= (char characterToAppend); /** Appends a character at the end of this string. */ @@ -235,42 +224,54 @@ public: /** Appends a string to 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 + @param startOfTextToAppend the start of the string to add. This must not be a nullptr + @param endOfTextToAppend the end of the string to add. This must not be a nullptr + */ + void appendCharPointer (const CharPointerType startOfTextToAppend, + const CharPointerType endOfTextToAppend); + + /** Appends a string to the end of this one. + + @param startOfTextToAppend the start of the string to add. This must not be a nullptr + @param endOfTextToAppend the end of the string to add. This must not be a nullptr */ template - void appendCharPointer (const CharPointer& textToAppend, size_t maxCharsToTake) + void appendCharPointer (const CharPointer startOfTextToAppend, + const CharPointer endOfTextToAppend) { - if (textToAppend.getAddress() != nullptr) + jassert (startOfTextToAppend.getAddress() != nullptr && endOfTextToAppend.getAddress() != nullptr); + + size_t extraBytesNeeded = 0, numChars = 1; + + for (CharPointer t (startOfTextToAppend); t != endOfTextToAppend && ! t.isEmpty(); ++numChars) + extraBytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); + + if (extraBytesNeeded > 0) { - size_t extraBytesNeeded = 0; - size_t numChars = 0; + const size_t byteOffsetOfNull = getByteOffsetOfEnd(); - for (CharPointer t (textToAppend); numChars < maxCharsToTake && ! t.isEmpty();) - { - extraBytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); - ++numChars; - } - - if (numChars > 0) - { - const size_t byteOffsetOfNull = getByteOffsetOfEnd(); - - preallocateBytes (byteOffsetOfNull + extraBytesNeeded); - CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)).writeWithCharLimit (textToAppend, (int) (numChars + 1)); - } + preallocateBytes (byteOffsetOfNull + extraBytesNeeded); + CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)) + .writeWithCharLimit (startOfTextToAppend, (int) numChars); } } /** Appends a string to the end of this one. */ + void appendCharPointer (const CharPointerType textToAppend); + + /** Appends a string to 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 + */ template - void appendCharPointer (const CharPointer& textToAppend) + void appendCharPointer (const CharPointer textToAppend, size_t maxCharsToTake) { if (textToAppend.getAddress() != nullptr) { - size_t extraBytesNeeded = 0; + size_t extraBytesNeeded = 0, numChars = 1; - for (CharPointer t (textToAppend); ! t.isEmpty();) + for (CharPointer t (textToAppend); numChars <= maxCharsToTake && ! t.isEmpty(); ++numChars) extraBytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); if (extraBytesNeeded > 0) @@ -278,11 +279,19 @@ public: const size_t byteOffsetOfNull = getByteOffsetOfEnd(); preallocateBytes (byteOffsetOfNull + extraBytesNeeded); - CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)).writeAll (textToAppend); + CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)) + .writeWithCharLimit (textToAppend, (int) numChars); } } } + /** Appends a string to the end of this one. */ + template + void appendCharPointer (const CharPointer textToAppend) + { + appendCharPointer (textToAppend, std::numeric_limits::max()); + } + //============================================================================== // Comparison methods.. @@ -298,9 +307,15 @@ public: */ inline bool isNotEmpty() const noexcept { return text[0] != 0; } + /** Resets this string to be empty. */ + void clear() noexcept; + /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const String& other) const noexcept; + /** Case-insensitive comparison with another string. */ + bool equalsIgnoreCase (StringRef other) const noexcept; + /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const wchar_t* other) const noexcept; @@ -345,7 +360,7 @@ public: If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ - bool startsWith (const String& text) const noexcept; + bool startsWith (StringRef text) const noexcept; /** Tests whether the string begins with a particular character. If the character is 0, this will always return false. @@ -357,13 +372,13 @@ public: If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ - bool startsWithIgnoreCase (const String& text) const noexcept; + bool startsWithIgnoreCase (StringRef text) const noexcept; /** Tests whether the string ends with another string. If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ - bool endsWith (const String& text) const noexcept; + bool endsWith (StringRef text) const noexcept; /** Tests whether the string ends with a particular character. If the character is 0, this will always return false. @@ -375,13 +390,13 @@ public: If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ - bool endsWithIgnoreCase (const String& text) const noexcept; + bool endsWithIgnoreCase (StringRef text) const noexcept; /** Tests whether the string contains another substring. If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ - bool contains (const String& text) const noexcept; + bool contains (StringRef text) const noexcept; /** Tests whether the string contains a particular character. Uses a case-sensitive comparison. @@ -391,41 +406,41 @@ public: /** Tests whether the string contains another substring. Uses a case-insensitive comparison. */ - bool containsIgnoreCase (const String& text) const noexcept; + bool containsIgnoreCase (StringRef text) const noexcept; - /** Tests whether the string contains another substring as a distict word. + /** Tests whether the string contains another substring as a distinct word. @returns true if the string contains this word, surrounded by non-alphanumeric characters @see indexOfWholeWord, containsWholeWordIgnoreCase */ - bool containsWholeWord (const String& wordToLookFor) const noexcept; + bool containsWholeWord (StringRef wordToLookFor) const noexcept; - /** Tests whether the string contains another substring as a distict word. + /** Tests whether the string contains another substring as a distinct word. @returns true if the string contains this word, surrounded by non-alphanumeric characters @see indexOfWholeWordIgnoreCase, containsWholeWord */ - bool containsWholeWordIgnoreCase (const String& wordToLookFor) const noexcept; + bool containsWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept; - /** Finds an instance of another substring if it exists as a distict word. + /** Finds an instance of another substring if it exists as a distinct 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 noexcept; + int indexOfWholeWord (StringRef wordToLookFor) const noexcept; - /** Finds an instance of another substring if it exists as a distict word. + /** Finds an instance of another substring if it exists as a distinct 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 noexcept; + int indexOfWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept; /** Looks for any of a set of characters in the string. Uses a case-sensitive comparison. @@ -433,7 +448,7 @@ public: @returns true if the string contains any of the characters from the string that is passed in. */ - bool containsAnyOf (const String& charactersItMightContain) const noexcept; + bool containsAnyOf (StringRef charactersItMightContain) const noexcept; /** Looks for a set of characters in the string. Uses a case-sensitive comparison. @@ -442,7 +457,7 @@ public: the parameter string. If this string is empty, the return value will always be true. */ - bool containsOnly (const String& charactersItMightContain) const noexcept; + bool containsOnly (StringRef charactersItMightContain) const noexcept; /** Returns true if this string contains any non-whitespace characters. @@ -460,7 +475,7 @@ public: 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 noexcept; + bool matchesWildcard (StringRef wildcard, bool ignoreCase) const noexcept; //============================================================================== // Substring location methods.. @@ -493,7 +508,7 @@ public: @see indexOfChar, lastIndexOfAnyOf */ - int indexOfAnyOf (const String& charactersToLookFor, + int indexOfAnyOf (StringRef charactersToLookFor, int startIndex = 0, bool ignoreCase = false) const noexcept; @@ -502,7 +517,7 @@ public: @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return 0. */ - int indexOf (const String& textToLookFor) const noexcept; + int indexOf (StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-sensitive comparison. @@ -511,14 +526,14 @@ public: @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ - int indexOf (int startIndex, const String& textToLookFor) const noexcept; + int indexOf (int startIndex, StringRef textToLookFor) const noexcept; /** 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. If textToLookFor is an empty string, this will always return 0. */ - int indexOfIgnoreCase (const String& textToLookFor) const noexcept; + int indexOfIgnoreCase (StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-insensitive comparison. @@ -527,7 +542,7 @@ public: @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ - int indexOfIgnoreCase (int startIndex, const String& textToLookFor) const noexcept; + int indexOfIgnoreCase (int startIndex, StringRef textToLookFor) const noexcept; /** Searches for a character inside this string (working backwards from the end of the string). Uses a case-sensitive comparison. @@ -540,14 +555,14 @@ public: @returns the index of the start of the last occurrence of the substring within this string, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ - int lastIndexOf (const String& textToLookFor) const noexcept; + int lastIndexOf (StringRef textToLookFor) const noexcept; /** 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. If textToLookFor is an empty string, this will always return -1. */ - int lastIndexOfIgnoreCase (const String& textToLookFor) const noexcept; + int lastIndexOfIgnoreCase (StringRef textToLookFor) const noexcept; /** Returns the index of the last character in this string that matches one of the characters passed-in to this method. @@ -561,7 +576,7 @@ public: @see lastIndexOf, indexOfAnyOf */ - int lastIndexOfAnyOf (const String& charactersToLookFor, + int lastIndexOfAnyOf (StringRef charactersToLookFor, bool ignoreCase = false) const noexcept; @@ -579,7 +594,7 @@ public: then to use that to iterate the string. @see getCharPointer */ - const juce_wchar operator[] (int index) const noexcept; + juce_wchar operator[] (int index) const noexcept; /** Returns the final character of the string. If the string is empty this will return 0. @@ -645,9 +660,9 @@ public: @see upToFirstOccurrenceOf, fromLastOccurrenceOf */ - String fromFirstOccurrenceOf (const String& substringToStartFrom, + String fromFirstOccurrenceOf (StringRef substringToStartFrom, bool includeSubStringInResult, - bool ignoreCase) const; + bool ignoreCase) const; /** Returns a section of the string starting from the last occurrence of a given substring. @@ -657,7 +672,7 @@ public: @see fromFirstOccurrenceOf, upToLastOccurrenceOf */ - String fromLastOccurrenceOf (const String& substringToFind, + String fromLastOccurrenceOf (StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const; @@ -674,7 +689,7 @@ public: @see upToLastOccurrenceOf, fromFirstOccurrenceOf */ - String upToFirstOccurrenceOf (const String& substringToEndWith, + String upToFirstOccurrenceOf (StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const; @@ -685,7 +700,7 @@ public: @see upToFirstOccurrenceOf, fromFirstOccurrenceOf */ - String upToLastOccurrenceOf (const String& substringToFind, + String upToLastOccurrenceOf (StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const; @@ -705,7 +720,7 @@ public: @param charactersToTrim the set of characters to remove. @see trim, trimStart, trimCharactersAtEnd */ - String trimCharactersAtStart (const String& charactersToTrim) const; + String trimCharactersAtStart (StringRef 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 @@ -713,7 +728,7 @@ public: @param charactersToTrim the set of characters to remove. @see trim, trimEnd, trimCharactersAtStart */ - String trimCharactersAtEnd (const String& charactersToTrim) const; + String trimCharactersAtEnd (StringRef charactersToTrim) const; //============================================================================== /** Returns an upper-case version of this string. */ @@ -740,7 +755,7 @@ public: */ String replaceSection (int startIndex, int numCharactersToReplace, - const String& stringToInsert) const; + StringRef stringToInsert) const; /** Replaces all occurrences of a substring with another string. @@ -749,8 +764,8 @@ public: Note that this is a const method, and won't alter the string itself. */ - String replace (const String& stringToReplace, - const String& stringToInsertInstead, + String replace (StringRef stringToReplace, + StringRef stringToInsertInstead, bool ignoreCase = false) const; /** Returns a string with all occurrences of a character replaced with a different one. */ @@ -767,8 +782,8 @@ public: Note that this is a const method, and won't affect the string itself. */ - String replaceCharacters (const String& charactersToReplace, - const String& charactersToInsertInstead) const; + String replaceCharacters (StringRef charactersToReplace, + StringRef charactersToInsertInstead) const; /** Returns a version of this string that only retains a fixed set of characters. @@ -779,7 +794,7 @@ public: Note that this is a const method, and won't alter the string itself. */ - String retainCharacters (const String& charactersToRetain) const; + String retainCharacters (StringRef charactersToRetain) const; /** Returns a version of this string with a set of characters removed. @@ -790,14 +805,14 @@ public: Note that this is a const method, and won't alter the string itself. */ - String removeCharacters (const String& charactersToRemove) const; + String removeCharacters (StringRef 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. */ - String initialSectionContainingOnly (const String& permittedCharacters) const; + String initialSectionContainingOnly (StringRef permittedCharacters) const; /** Returns a section from the start of the string that only contains a certain set of characters. @@ -805,7 +820,7 @@ public: first character that occurs in the string passed in. (If none of the specified characters are found in the string, the return value will just be the original string). */ - String initialSectionNotContaining (const String& charactersToStopAt) const; + String initialSectionNotContaining (StringRef charactersToStopAt) const; //============================================================================== /** Checks whether the string might be in quotation marks. @@ -848,7 +863,7 @@ public: @param stringToRepeat the string to repeat @param numberOfTimesToRepeat how many times to repeat it */ - static String repeatedString (const String& stringToRepeat, + static String repeatedString (StringRef stringToRepeat, int numberOfTimesToRepeat); /** Returns a copy of this string with the specified character repeatedly added to its @@ -955,7 +970,6 @@ public: int getIntValue() const noexcept; /** 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 noexcept; @@ -1034,7 +1048,7 @@ public: that is returned must not be stored anywhere, as it can be deleted whenever the string changes. */ - inline const CharPointerType& getCharPointer() const noexcept { return text; } + inline CharPointerType getCharPointer() const noexcept { return text; } /** Returns a pointer to a UTF-8 version of this string. @@ -1045,11 +1059,24 @@ public: To find out how many bytes you need to store this string as UTF-8, you can call CharPointer_UTF8::getBytesRequiredFor (myString.getCharPointer()) - @see getCharPointer, toUTF16, toUTF32 + @see toRawUTF8, getCharPointer, toUTF16, toUTF32 */ CharPointer_UTF8 toUTF8() const; - /** Returns a pointer to a UTF-32 version of this string. + /** 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. + + To find out how many bytes you need to store this string as UTF-8, you can call + CharPointer_UTF8::getBytesRequiredFor (myString.getCharPointer()) + + @see getCharPointer, toUTF8, toUTF16, toUTF32 + */ + const char* toRawUTF8() const; + + /** Returns a pointer to a UTF-16 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 @@ -1086,6 +1113,9 @@ public: */ const wchar_t* toWideCharPointer() const; + /** */ + std::string toStdString() const; + //============================================================================== /** Creates a String from a UTF-8 encoded buffer. If the size is < 0, it'll keep reading until it hits a zero. @@ -1096,7 +1126,7 @@ public: The number returned does NOT include the trailing zero. @see toUTF8, copyToUTF8 */ - int getNumBytesAsUTF8() const noexcept; + size_t getNumBytesAsUTF8() const noexcept; //============================================================================== /** Copies the string to a buffer as UTF-8 characters. @@ -1114,7 +1144,7 @@ public: end, and will return the number of bytes that were actually used. @see CharPointer_UTF8::writeWithDestByteLimit */ - int copyToUTF8 (CharPointer_UTF8::CharType* destBuffer, int maxBufferSizeBytes) const noexcept; + size_t copyToUTF8 (CharPointer_UTF8::CharType* destBuffer, size_t maxBufferSizeBytes) const noexcept; /** Copies the string to a buffer as UTF-16 characters. @@ -1131,9 +1161,9 @@ public: end, and will return the number of bytes that were actually used. @see CharPointer_UTF16::writeWithDestByteLimit */ - int copyToUTF16 (CharPointer_UTF16::CharType* destBuffer, int maxBufferSizeBytes) const noexcept; + size_t copyToUTF16 (CharPointer_UTF16::CharType* destBuffer, size_t maxBufferSizeBytes) const noexcept; - /** Copies the string to a buffer as UTF-16 characters. + /** Copies the string to a buffer as UTF-32 characters. Returns the number of bytes copied to the buffer, including the terminating null character. @@ -1148,7 +1178,7 @@ public: end, and will return the number of bytes that were actually used. @see CharPointer_UTF32::writeWithDestByteLimit */ - int copyToUTF32 (CharPointer_UTF32::CharType* destBuffer, int maxBufferSizeBytes) const noexcept; + size_t copyToUTF32 (CharPointer_UTF32::CharType* destBuffer, size_t maxBufferSizeBytes) const noexcept; //============================================================================== /** Increases the string's internally allocated storage. @@ -1188,6 +1218,11 @@ public: String convertToPrecomposedUnicode() const; #endif + /** Returns the number of String objects which are currently sharing the same internal + data as this one. + */ + int getReferenceCount() const noexcept; + private: //============================================================================== CharPointerType text; @@ -1195,14 +1230,13 @@ private: //============================================================================== struct PreallocationBytes { - explicit PreallocationBytes (size_t); + explicit PreallocationBytes (size_t) noexcept; size_t numBytes; }; explicit String (const PreallocationBytes&); // This constructor preallocates a certain amount of memory - void appendFixedLength (const char* text, int numExtraChars); size_t getByteOffsetOfEnd() const noexcept; - JUCE_DEPRECATED (String (const String& stringToCopy, size_t charsToAllocate)); + JUCE_DEPRECATED (String (const String&, size_t)); // This private cast operator should prevent strings being accidentally cast // to bools (this is possible because the compiler can add an implicit cast @@ -1263,6 +1297,10 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, long number); /** Appends a decimal number at the end of a string. */ +JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, int64 number); +/** Appends a decimal number at the end of a string. */ +JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, uint64 number); +/** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, float number); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, double number); @@ -1275,11 +1313,12 @@ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const char* strin /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const wchar_t* string2) noexcept; /** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF8& string2) noexcept; +JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF8 string2) noexcept; /** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF16& string2) noexcept; +JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF16 string2) noexcept; /** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF32& string2) noexcept; +JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF32 string2) noexcept; + /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ @@ -1287,11 +1326,12 @@ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const char* strin /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const wchar_t* string2) noexcept; /** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF8& string2) noexcept; +JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF8 string2) noexcept; /** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF16& string2) noexcept; +JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF16 string2) noexcept; /** Case-sensitive comparison of two strings. */ -JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF32& string2) noexcept; +JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF32 string2) noexcept; + /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator> (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ @@ -1308,7 +1348,7 @@ JUCE_API bool JUCE_CALLTYPE operator<= (const String& string1, const String& str template std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostream & stream, const String& stringToWrite) { - return stream << stringToWrite.toUTF8().getAddress(); + return stream << stringToWrite.toRawUTF8(); } /** This operator allows you to write a juce String directly to std output streams. @@ -1323,5 +1363,8 @@ std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostre /** Writes a string to an OutputStream as UTF8. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& stringToWrite); +/** Writes a string to an OutputStream as UTF8. */ +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef stringToWrite); -#endif // __JUCE_STRING_JUCEHEADER__ + +#endif // JUCE_STRING_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp index 81407e6..e26d38d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp @@ -1,29 +1,31 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -//============================================================================== StringArray::StringArray() noexcept { } @@ -45,42 +47,29 @@ StringArray::StringArray (const String& firstValue) strings.add (firstValue); } -namespace StringArrayHelpers +StringArray::StringArray (const String* initialStrings, int numberOfStrings) { - template - void addArray (Array& dest, const CharType* const* strings) - { - if (strings != nullptr) - while (*strings != nullptr) - dest.add (*strings++); - } - - template - void addArray (Array& dest, const CharType* const* const strings, const int numberOfStrings) - { - for (int i = 0; i < numberOfStrings; ++i) - dest.add (strings [i]); - } + strings.addArray (initialStrings, numberOfStrings); } -StringArray::StringArray (const char* const* const initialStrings) +StringArray::StringArray (const char* const* initialStrings) { - StringArrayHelpers::addArray (strings, initialStrings); + strings.addNullTerminatedArray (initialStrings); } -StringArray::StringArray (const char* const* const initialStrings, const int numberOfStrings) +StringArray::StringArray (const char* const* initialStrings, int numberOfStrings) { - StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings); + strings.addArray (initialStrings, numberOfStrings); } -StringArray::StringArray (const wchar_t* const* const initialStrings) +StringArray::StringArray (const wchar_t* const* initialStrings) { - StringArrayHelpers::addArray (strings, initialStrings); + strings.addNullTerminatedArray (initialStrings); } -StringArray::StringArray (const wchar_t* const* const initialStrings, const int numberOfStrings) +StringArray::StringArray (const wchar_t* const* initialStrings, int numberOfStrings) { - StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings); + strings.addArray (initialStrings, numberOfStrings); } StringArray& StringArray::operator= (const StringArray& other) @@ -103,14 +92,7 @@ StringArray::~StringArray() bool StringArray::operator== (const StringArray& other) const noexcept { - if (other.size() != size()) - return false; - - for (int i = size(); --i >= 0;) - if (other.strings.getReference(i) != strings.getReference(i)) - return false; - - return true; + return strings == other.strings; } bool StringArray::operator!= (const StringArray& other) const noexcept @@ -118,11 +100,21 @@ bool StringArray::operator!= (const StringArray& other) const noexcept return ! operator== (other); } +void StringArray::swapWith (StringArray& other) noexcept +{ + strings.swapWith (other.strings); +} + void StringArray::clear() { strings.clear(); } +void StringArray::clearQuick() +{ + strings.clearQuick(); +} + const String& StringArray::operator[] (const int index) const noexcept { if (isPositiveAndBelow (index, strings.size())) @@ -133,7 +125,6 @@ const String& StringArray::operator[] (const int index) const noexcept String& StringArray::getReference (const int index) noexcept { - jassert (isPositiveAndBelow (index, strings.size())); return strings.getReference (index); } @@ -142,6 +133,13 @@ void StringArray::add (const String& newString) strings.add (newString); } +#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS +void StringArray::add (String&& stringToAdd) +{ + strings.add (static_cast (stringToAdd)); +} +#endif + void StringArray::insert (const int index, const String& newString) { strings.insert (index, newString); @@ -173,25 +171,12 @@ void StringArray::set (const int index, const String& newString) strings.set (index, newString); } -bool StringArray::contains (const String& stringToLookFor, const bool ignoreCase) const +bool StringArray::contains (StringRef stringToLookFor, const bool ignoreCase) const { - if (ignoreCase) - { - for (int i = size(); --i >= 0;) - if (strings.getReference(i).equalsIgnoreCase (stringToLookFor)) - return true; - } - else - { - for (int i = size(); --i >= 0;) - if (stringToLookFor == strings.getReference(i)) - return true; - } - - return false; + return indexOf (stringToLookFor, ignoreCase) >= 0; } -int StringArray::indexOf (const String& stringToLookFor, const bool ignoreCase, int i) const +int StringArray::indexOf (StringRef stringToLookFor, const bool ignoreCase, int i) const { if (i < 0) i = 0; @@ -200,23 +185,15 @@ int StringArray::indexOf (const String& stringToLookFor, const bool ignoreCase, if (ignoreCase) { - while (i < numElements) - { + for (; i < numElements; ++i) if (strings.getReference(i).equalsIgnoreCase (stringToLookFor)) return i; - - ++i; - } } else { - while (i < numElements) - { + for (; i < numElements; ++i) if (stringToLookFor == strings.getReference (i)) return i; - - ++i; - } } return -1; @@ -228,8 +205,7 @@ void StringArray::remove (const int index) strings.remove (index); } -void StringArray::removeString (const String& stringToRemove, - const bool ignoreCase) +void StringArray::removeString (StringRef stringToRemove, const bool ignoreCase) { if (ignoreCase) { @@ -308,7 +284,7 @@ void StringArray::move (const int currentIndex, int newIndex) noexcept //============================================================================== -String StringArray::joinIntoString (const String& separator, int start, int numberToJoin) const +String StringArray::joinIntoString (StringRef separator, int start, int numberToJoin) const { const int last = (numberToJoin < 0) ? size() : jmin (size(), start + numberToJoin); @@ -317,13 +293,13 @@ String StringArray::joinIntoString (const String& separator, int start, int numb start = 0; if (start >= last) - return String::empty; + return String(); if (start == last - 1) return strings.getReference (start); - const size_t separatorBytes = separator.getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); - size_t bytesNeeded = separatorBytes * (last - start - 1); + const size_t separatorBytes = separator.text.sizeInBytes() - sizeof (String::CharPointerType::CharType); + size_t bytesNeeded = separatorBytes * (size_t) (last - start - 1); for (int i = start; i < last; ++i) bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); @@ -341,7 +317,7 @@ String StringArray::joinIntoString (const String& separator, int start, int numb dest.writeAll (s.getCharPointer()); if (++start < last && separatorBytes > 0) - dest.writeAll (separator.getCharPointer()); + dest.writeAll (separator.text); } dest.writeNull(); @@ -349,24 +325,23 @@ String StringArray::joinIntoString (const String& separator, int start, int numb return result; } -int StringArray::addTokens (const String& text, const bool preserveQuotedStrings) +int StringArray::addTokens (StringRef text, const bool preserveQuotedStrings) { return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : ""); } -int StringArray::addTokens (const String& text, const String& breakCharacters, const String& quoteCharacters) +int StringArray::addTokens (StringRef text, StringRef breakCharacters, StringRef quoteCharacters) { int num = 0; - String::CharPointerType t (text.getCharPointer()); - if (! t.isEmpty()) + if (text.isNotEmpty()) { - for (;;) + for (String::CharPointerType t (text.text);;) { String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t, - breakCharacters.getCharPointer(), - quoteCharacters.getCharPointer())); - add (String (t, tokenEnd)); + breakCharacters.text, + quoteCharacters.text)); + strings.add (String (t, tokenEnd)); ++num; if (tokenEnd.isEmpty()) @@ -379,48 +354,58 @@ int StringArray::addTokens (const String& text, const String& breakCharacters, c return num; } -int StringArray::addLines (const String& sourceText) +int StringArray::addLines (StringRef sourceText) { int numLines = 0; - String::CharPointerType text (sourceText.getCharPointer()); + String::CharPointerType text (sourceText.text); bool finished = text.isEmpty(); while (! finished) { - String::CharPointerType startOfLine (text); - size_t numChars = 0; - - for (;;) + for (String::CharPointerType startOfLine (text);;) { - const juce_wchar c = text.getAndAdvance(); + const String::CharPointerType endOfLine (text); - if (c == 0) + switch (text.getAndAdvance()) { - finished = true; - break; + case 0: finished = true; break; + case '\n': break; + case '\r': if (*text == '\n') ++text; break; + default: continue; } - if (c == '\n') - break; - - if (c == '\r') - { - if (*text == '\n') - ++text; - - break; - } - - ++numChars; + strings.add (String (startOfLine, endOfLine)); + ++numLines; + break; } - - add (String (startOfLine, numChars)); - ++numLines; } return numLines; } +StringArray StringArray::fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings) +{ + StringArray s; + s.addTokens (stringToTokenise, preserveQuotedStrings); + return s; +} + +StringArray StringArray::fromTokens (StringRef stringToTokenise, + StringRef breakCharacters, + StringRef quoteCharacters) +{ + StringArray s; + s.addTokens (stringToTokenise, breakCharacters, quoteCharacters); + return s; +} + +StringArray StringArray::fromLines (StringRef stringToBreakUp) +{ + StringArray s; + s.addLines (stringToBreakUp); + return s; +} + //============================================================================== void StringArray::removeDuplicates (const bool ignoreCase) { @@ -428,9 +413,7 @@ void StringArray::removeDuplicates (const bool ignoreCase) { const String s (strings.getReference(i)); - int nextIndex = i + 1; - - for (;;) + for (int nextIndex = i + 1;;) { nextIndex = indexOf (s, ignoreCase, nextIndex); @@ -481,6 +464,11 @@ void StringArray::appendNumbersToDuplicates (const bool ignoreCase, } } +void StringArray::ensureStorageAllocated (int minNumElements) +{ + strings.ensureStorageAllocated (minNumElements); +} + void StringArray::minimiseStorageOverheads() { strings.minimiseStorageOverheads(); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h index 6deb6c1..251a44d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_STRINGARRAY_JUCEHEADER__ -#define __JUCE_STRINGARRAY_JUCEHEADER__ - -#include "juce_String.h" -#include "../containers/juce_Array.h" +#ifndef JUCE_STRINGARRAY_H_INCLUDED +#define JUCE_STRINGARRAY_H_INCLUDED //============================================================================== @@ -44,15 +44,21 @@ public: StringArray() noexcept; /** Creates a copy of another string array */ - StringArray (const StringArray& other); + StringArray (const StringArray&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - StringArray (StringArray&& other) noexcept; + StringArray (StringArray&&) noexcept; #endif /** Creates an array containing a single string. */ explicit StringArray (const String& firstValue); + /** Creates an array from a raw array of strings. + @param strings an array of strings to add + @param numberOfStrings how many items there are in the array + */ + StringArray (const String* 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 @@ -84,24 +90,27 @@ public: ~StringArray(); /** Copies the contents of another string array into this one */ - StringArray& operator= (const StringArray& other); + StringArray& operator= (const StringArray&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - StringArray& operator= (StringArray&& other) noexcept; + StringArray& operator= (StringArray&&) noexcept; #endif + /** Swaps the contents of this and another StringArray. */ + void swapWith (StringArray&) noexcept; + //============================================================================== /** 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 noexcept; + bool operator== (const StringArray&) const noexcept; /** 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 noexcept; + bool operator!= (const StringArray&) const noexcept; //============================================================================== /** Returns the number of strings in the array */ @@ -122,13 +131,29 @@ public: */ String& getReference (int index) noexcept; + /** Returns a pointer to the first String in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline String* begin() const noexcept + { + return strings.begin(); + } + + /** Returns a pointer to the String which follows the last element in the array. + This method is provided for compatibility with standard C++ iteration mechanisms. + */ + inline String* end() const noexcept + { + return strings.end(); + } + /** 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 contains (StringRef stringToLookFor, bool ignoreCase = false) const; /** Searches for a string in the array. @@ -141,7 +166,7 @@ public: @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, + int indexOf (StringRef stringToLookFor, bool ignoreCase = false, int startIndex = 0) const; @@ -149,6 +174,11 @@ public: /** Appends a string at the end of the array. */ void add (const String& stringToAdd); + #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS + /** Appends a string at the end of the array. */ + void add (String&& stringToAdd); + #endif + /** Inserts a string into the array. This will insert a string into the array at the given index, moving @@ -159,7 +189,6 @@ public: 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); @@ -186,11 +215,10 @@ public: 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 + @see fromTokens */ - int addTokens (const String& stringToTokenise, - bool preserveQuotedStrings); + int addTokens (StringRef stringToTokenise, bool preserveQuotedStrings); /** Breaks up a string into tokens and adds them to this array. @@ -204,10 +232,11 @@ public: which are treated as quotes. Any text occurring between quotes is not broken up into tokens. @returns the number of tokens added + @see fromTokens */ - int addTokens (const String& stringToTokenise, - const String& breakCharacters, - const String& quoteCharacters); + int addTokens (StringRef stringToTokenise, + StringRef breakCharacters, + StringRef quoteCharacters); /** Breaks up a string into lines and adds them to this array. @@ -215,24 +244,61 @@ public: to the array. Line-break characters are omitted from the strings that are added to the array. */ - int addLines (const String& stringToBreakUp); + int addLines (StringRef stringToBreakUp); + + /** Returns an array containing the tokens in a given string. + + This will tokenise the given string using whitespace characters as the + token delimiters, and return these tokens as an array. + @see addTokens + */ + static StringArray fromTokens (StringRef stringToTokenise, + bool preserveQuotedStrings); + + /** Returns an array containing the tokens in a given string. + + This will tokenise the given string using whitespace characters as the + token delimiters, and return these tokens as an 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. + @see addTokens + */ + static StringArray fromTokens (StringRef stringToTokenise, + StringRef breakCharacters, + StringRef quoteCharacters); + + /** Returns an array containing the lines in a given string. + + This breaks a string down into lines separated by \\n or \\r\\n, and returns an + array containing these lines. Line-break characters are omitted from the strings that + are added to the array. + */ + static StringArray fromLines (StringRef stringToBreakUp); //============================================================================== /** Removes all elements from the array. */ void clear(); - /** Removes a string from the array. + /** Removes all elements from the array without freeing the array's allocated storage. + @see clear + */ + void clearQuick(); + /** 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, + void removeString (StringRef stringToRemove, bool ignoreCase = false); /** Removes a range of elements from the array. @@ -258,7 +324,6 @@ public: void removeDuplicates (bool ignoreCase); /** Removes empty strings from the array. - @param removeWhitespaceStrings if true, strings that only contain whitespace characters will also be removed */ @@ -316,7 +381,7 @@ public: @param numberOfElements how many elements to join together. If this is less than zero, all available elements will be used. */ - String joinIntoString (const String& separatorString, + String joinIntoString (StringRef separatorString, int startIndex = 0, int numberOfElements = -1) const; @@ -328,6 +393,14 @@ public: void sort (bool ignoreCase); //============================================================================== + /** 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 (int minNumElements); + /** Reduces the amount of storage being used by the array. Arrays typically allocate slightly more storage than they need, and after @@ -336,13 +409,14 @@ public: */ void minimiseStorageOverheads(); + /** This is the array holding the actual strings. This is public to allow direct access + to array methods that may not already be provided by the StringArray class. + */ + Array strings; private: - //============================================================================== - Array strings; - - JUCE_LEAK_DETECTOR (StringArray); + JUCE_LEAK_DETECTOR (StringArray) }; -#endif // __JUCE_STRINGARRAY_JUCEHEADER__ +#endif // JUCE_STRINGARRAY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp index 2a8f520..3275d2d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -60,12 +63,12 @@ bool StringPairArray::operator!= (const StringPairArray& other) const return ! operator== (other); } -const String& StringPairArray::operator[] (const String& key) const +const String& StringPairArray::operator[] (StringRef key) const { return values [keys.indexOf (key, ignoreCase)]; } -String StringPairArray::getValue (const String& key, const String& defaultReturnValue) const +String StringPairArray::getValue (StringRef key, const String& defaultReturnValue) const { const int i = keys.indexOf (key, ignoreCase); @@ -75,6 +78,11 @@ String StringPairArray::getValue (const String& key, const String& defaultReturn return defaultReturnValue; } +bool StringPairArray::containsKey (StringRef key) const noexcept +{ + return keys.contains (key); +} + void StringPairArray::set (const String& key, const String& value) { const int i = keys.indexOf (key, ignoreCase); @@ -102,7 +110,7 @@ void StringPairArray::clear() values.clear(); } -void StringPairArray::remove (const String& key) +void StringPairArray::remove (StringRef key) { remove (keys.indexOf (key, ignoreCase)); } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h index 383cdd8..e1c774d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_STRINGPAIRARRAY_JUCEHEADER__ -#define __JUCE_STRINGPAIRARRAY_JUCEHEADER__ - -#include "juce_StringArray.h" +#ifndef JUCE_STRINGPAIRARRAY_H_INCLUDED +#define JUCE_STRINGPAIRARRAY_H_INCLUDED //============================================================================== @@ -76,16 +77,16 @@ public: @see getValue */ - const String& operator[] (const String& key) const; + const String& operator[] (StringRef 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[] */ - String getValue (const String& key, const String& defaultReturnValue) const; + String getValue (StringRef, const String& defaultReturnValue) const; + /** Returns true if the given key exists. */ + bool containsKey (StringRef key) const noexcept; /** Returns a list of all keys in the array. */ const StringArray& getAllKeys() const noexcept { return keys; } @@ -99,14 +100,12 @@ public: //============================================================================== /** 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); @@ -116,13 +115,11 @@ public: 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); + void remove (StringRef 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); @@ -153,8 +150,8 @@ private: StringArray keys, values; bool ignoreCase; - JUCE_LEAK_DETECTOR (StringPairArray); + JUCE_LEAK_DETECTOR (StringPairArray) }; -#endif // __JUCE_STRINGPAIRARRAY_JUCEHEADER__ +#endif // JUCE_STRINGPAIRARRAY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp index d8571ca..039a30b 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp @@ -1,111 +1,166 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -StringPool::StringPool() noexcept {} -StringPool::~StringPool() {} +static const int minNumberOfStringsForGarbageCollection = 300; +static const uint32 garbageCollectionInterval = 30000; -namespace StringPoolHelpers + +StringPool::StringPool() noexcept : lastGarbageCollectionTime (0) {} +StringPool::~StringPool() {} + +struct StartEndString { - template - String::CharPointerType getPooledStringFromArray (Array& strings, - StringType newString, - const CriticalSection& lock) + StartEndString (String::CharPointerType s, String::CharPointerType e) noexcept : start (s), end (e) {} + operator String() const { return String (start, end); } + + String::CharPointerType start, end; +}; + +static int compareStrings (const String& s1, const String& s2) noexcept { return s1.compare (s2); } +static int compareStrings (CharPointer_UTF8 s1, const String& s2) noexcept { return s1.compare (s2.getCharPointer()); } + +static int compareStrings (const StartEndString& string1, const String& string2) noexcept +{ + String::CharPointerType s1 (string1.start), s2 (string2.getCharPointer()); + + for (;;) { - const ScopedLock sl (lock); - int start = 0; - int end = strings.size(); + const int c1 = s1 < string1.end ? (int) s1.getAndAdvance() : 0; + const int c2 = (int) s2.getAndAdvance(); + const int diff = c1 - c2; - for (;;) - { - if (start >= end) - { - jassert (start <= end); - strings.insert (start, newString); - return strings.getReference (start).getCharPointer(); - } - else - { - const String& startString = strings.getReference (start); - - if (startString == newString) - return startString.getCharPointer(); - - const int halfway = (start + end) >> 1; - - if (halfway == start) - { - if (startString.compare (newString) < 0) - ++start; - - strings.insert (start, newString); - return strings.getReference (start).getCharPointer(); - } - - const int comp = strings.getReference (halfway).compare (newString); - - if (comp == 0) - return strings.getReference (halfway).getCharPointer(); - else if (comp < 0) - start = halfway; - else - end = halfway; - } - } + if (diff != 0) return diff < 0 ? -1 : 1; + if (c1 == 0) break; } + + return 0; } -String::CharPointerType StringPool::getPooledString (const String& s) +template +static String addPooledString (Array& strings, const NewStringType& newString) { - if (s.isEmpty()) - return String::empty.getCharPointer(); + int start = 0; + int end = strings.size(); - return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); + while (start < end) + { + const String& startString = strings.getReference (start); + const int startComp = compareStrings (newString, startString); + + if (startComp == 0) + return startString; + + const int halfway = (start + end) / 2; + + if (halfway == start) + { + if (startComp > 0) + ++start; + + break; + } + + const String& halfwayString = strings.getReference (halfway); + const int halfwayComp = compareStrings (newString, halfwayString); + + if (halfwayComp == 0) + return halfwayString; + + if (halfwayComp > 0) + start = halfway; + else + end = halfway; + } + + strings.insert (start, newString); + return strings.getReference (start); } -String::CharPointerType StringPool::getPooledString (const char* const s) +String StringPool::getPooledString (const char* const newString) { - if (s == nullptr || *s == 0) - return String::empty.getCharPointer(); + if (newString == nullptr || *newString == 0) + return String(); - return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); + const ScopedLock sl (lock); + garbageCollectIfNeeded(); + return addPooledString (strings, CharPointer_UTF8 (newString)); } -String::CharPointerType StringPool::getPooledString (const wchar_t* const s) +String StringPool::getPooledString (String::CharPointerType start, String::CharPointerType end) { - if (s == nullptr || *s == 0) - return String::empty.getCharPointer(); + if (start.isEmpty() || start == end) + return String(); - return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); + const ScopedLock sl (lock); + garbageCollectIfNeeded(); + return addPooledString (strings, StartEndString (start, end)); } -int StringPool::size() const noexcept +String StringPool::getPooledString (StringRef newString) { - return strings.size(); + if (newString.isEmpty()) + return String(); + + const ScopedLock sl (lock); + garbageCollectIfNeeded(); + return addPooledString (strings, newString.text); } -String::CharPointerType StringPool::operator[] (const int index) const noexcept +String StringPool::getPooledString (const String& newString) { - return strings [index].getCharPointer(); + if (newString.isEmpty()) + return String(); + + const ScopedLock sl (lock); + garbageCollectIfNeeded(); + return addPooledString (strings, newString); +} + +void StringPool::garbageCollectIfNeeded() +{ + if (strings.size() > minNumberOfStringsForGarbageCollection + && Time::getApproximateMillisecondCounter() > lastGarbageCollectionTime + garbageCollectionInterval) + garbageCollect(); +} + +void StringPool::garbageCollect() +{ + const ScopedLock sl (lock); + + for (int i = strings.size(); --i >= 0;) + if (strings.getReference(i).getReferenceCount() == 1) + strings.remove (i); + + lastGarbageCollectionTime = Time::getApproximateMillisecondCounter(); +} + +StringPool& StringPool::getGlobalPool() noexcept +{ + static StringPool pool; + return pool; } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h index 9e3aa80..4de2186 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_STRINGPOOL_JUCEHEADER__ -#define __JUCE_STRINGPOOL_JUCEHEADER__ - -#include "juce_String.h" -#include "../containers/juce_Array.h" +#ifndef JUCE_STRINGPOOL_H_INCLUDED +#define JUCE_STRINGPOOL_H_INCLUDED //============================================================================== @@ -52,41 +52,43 @@ public: ~StringPool(); //============================================================================== - /** Returns a pointer to a copy of the string that is passed in. - - The pool will always return the same pointer when asked for a string that matches it. - The pool will own all the pointers that it returns, deleting them when the pool itself - is deleted. + /** Returns a pointer to a shared copy of the string that is passed in. + The pool will always return the same String object when asked for a string that matches it. */ - String::CharPointerType getPooledString (const String& original); + String getPooledString (const String& original); /** Returns a pointer to a copy of the string that is passed in. - - The pool will always return the same pointer when asked for a string that matches it. - The pool will own all the pointers that it returns, deleting them when the pool itself - is deleted. + The pool will always return the same String object when asked for a string that matches it. */ - String::CharPointerType getPooledString (const char* original); + String getPooledString (const char* original); + + /** Returns a pointer to a shared copy of the string that is passed in. + The pool will always return the same String object when asked for a string that matches it. + */ + String getPooledString (StringRef original); /** Returns a pointer to a copy of the string that is passed in. - - The pool will always return the same pointer when asked for a string that matches it. - The pool will own all the pointers that it returns, deleting them when the pool itself - is deleted. + The pool will always return the same String object when asked for a string that matches it. */ - String::CharPointerType getPooledString (const wchar_t* original); + String getPooledString (String::CharPointerType start, String::CharPointerType end); //============================================================================== - /** Returns the number of strings in the pool. */ - int size() const noexcept; + /** Scans the pool, and removes any strings that are unreferenced. + You don't generally need to call this - it'll be called automatically when the pool grows + large enough to warrant it. + */ + void garbageCollect(); - /** Returns one of the strings in the pool, by index. */ - String::CharPointerType operator[] (int index) const noexcept; + /** Returns a shared global pool which is used for things like Identifiers, XML parsing. */ + static StringPool& getGlobalPool() noexcept; private: - Array strings; + Array strings; CriticalSection lock; + uint32 lastGarbageCollectionTime; + + void garbageCollectIfNeeded(); }; -#endif // __JUCE_STRINGPOOL_JUCEHEADER__ +#endif // JUCE_STRINGPOOL_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h b/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h new file mode 100644 index 0000000..434bf4a --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringRef.h @@ -0,0 +1,138 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_STRINGREF_H_INCLUDED +#define JUCE_STRINGREF_H_INCLUDED + +//============================================================================== +/** + A simple class for holding temporary references to a string literal or String. + + Unlike a real String object, the StringRef does not allocate any memory or + take ownership of the strings you give to it - it simply holds a reference to + a string that has been allocated elsewhere. + The main purpose of the class is to be used instead of a const String& as the type + of function arguments where the caller may pass either a string literal or a String + object. This means that when the called uses a string literal, there's no need + for an temporary String object to be allocated, and this cuts down overheads + substantially. + + Because the class is simply a wrapper around a pointer, you should always pass + it by value, not by reference. + + @code + void myStringFunction1 (const String&); + void myStringFunction2 (StringRef); + + myStringFunction1 ("abc"); // Implicitly allocates a temporary String object. + myStringFunction2 ("abc"); // Much faster, as no local allocations are needed. + @endcode + + For examples of it in use, see the XmlElement or StringArray classes. + + Bear in mind that there are still many cases where it's better to use an argument + which is a const String&. For example if the function stores the string or needs + to internally create a String from the argument, then it's better for the original + argument to already be a String. + + @see String +*/ +class JUCE_API StringRef +{ +public: + /** Creates a StringRef from a raw string literal. + The StringRef object does NOT take ownership or copy this data, so you must + ensure that the data does not change during the lifetime of the StringRef. + Note that this pointer not be null! + */ + StringRef (const char* stringLiteral) noexcept; + + /** Creates a StringRef from a raw char pointer. + The StringRef object does NOT take ownership or copy this data, so you must + ensure that the data does not change during the lifetime of the StringRef. + */ + StringRef (String::CharPointerType stringLiteral) noexcept; + + /** Creates a StringRef from a String. + The StringRef object does NOT take ownership or copy the data from the String, + so you must ensure that the String is not modified or deleted during the lifetime + of the StringRef. + */ + StringRef (const String& string) noexcept; + + /** Creates a StringRef pointer to an empty string. */ + StringRef() noexcept; + + //============================================================================== + /** Returns a raw pointer to the underlying string data. */ + operator const String::CharPointerType::CharType*() const noexcept { return text.getAddress(); } + /** Returns a pointer to the underlying string data as a char pointer object. */ + operator String::CharPointerType() const noexcept { return text; } + + /** Returns true if the string is empty. */ + bool isEmpty() const noexcept { return text.isEmpty(); } + /** Returns true if the string is not empty. */ + bool isNotEmpty() const noexcept { return ! text.isEmpty(); } + /** Returns the number of characters in the string. */ + int length() const noexcept { return (int) text.length(); } + + /** Retrieves a character by index. */ + juce_wchar operator[] (int index) const noexcept { return text[index]; } + + /** Compares this StringRef with a String. */ + bool operator== (const String& s) const noexcept { return text.compare (s.getCharPointer()) == 0; } + /** Compares this StringRef with a String. */ + bool operator!= (const String& s) const noexcept { return text.compare (s.getCharPointer()) != 0; } + + /** Case-sensitive comparison of two StringRefs. */ + bool operator== (StringRef s) const noexcept { return text.compare (s.text) == 0; } + /** Case-sensitive comparison of two StringRefs. */ + bool operator!= (StringRef s) const noexcept { return text.compare (s.text) != 0; } + + //============================================================================== + /** The text that is referenced. */ + String::CharPointerType text; + + #if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) + // Sorry, non-UTF8 people, you're unable to take advantage of StringRef, because + // you've chosen a character encoding that doesn't match C++ string literals. + String stringCopy; + #endif +}; + +//============================================================================== +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2) noexcept; +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept; + +#if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) + inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); } +#endif + +#endif // JUCE_STRINGREF_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp new file mode 100644 index 0000000..6b4c807 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.cpp @@ -0,0 +1,247 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +struct TextDiffHelpers +{ + enum { minLengthToMatch = 3 }; + + struct StringRegion + { + StringRegion (const String& s) noexcept + : text (s.getCharPointer()), start (0), length (s.length()) {} + + StringRegion (const String::CharPointerType t, int s, int len) noexcept + : text (t), start (s), length (len) {} + + String::CharPointerType text; + int start, length; + }; + + static void addInsertion (TextDiff& td, const String::CharPointerType text, int index, int length) + { + TextDiff::Change c; + c.insertedText = String (text, (size_t) length); + c.start = index; + c.length = length; + td.changes.add (c); + } + + static void addDeletion (TextDiff& td, int index, int length) + { + TextDiff::Change c; + c.start = index; + c.length = length; + td.changes.add (c); + } + + static void diffSkippingCommonStart (TextDiff& td, const StringRegion& a, const StringRegion& b) + { + String::CharPointerType sa (a.text); + String::CharPointerType sb (b.text); + const int maxLen = jmax (a.length, b.length); + + for (int i = 0; i < maxLen; ++i, ++sa, ++sb) + { + if (*sa != *sb) + { + diffRecursively (td, StringRegion (sa, a.start + i, a.length - i), + StringRegion (sb, b.start + i, b.length - i)); + break; + } + } + } + + static void diffRecursively (TextDiff& td, const StringRegion& a, const StringRegion& b) + { + int indexA, indexB; + const int len = findLongestCommonSubstring (a.text, a.length, + b.text, b.length, + indexA, indexB); + + if (len >= minLengthToMatch) + { + if (indexA > 0 && indexB > 0) + diffSkippingCommonStart (td, StringRegion (a.text, a.start, indexA), + StringRegion (b.text, b.start, indexB)); + else if (indexA > 0) + addDeletion (td, b.start, indexA); + else if (indexB > 0) + addInsertion (td, b.text, b.start, indexB); + + diffRecursively (td, StringRegion (a.text + indexA + len, a.start + indexA + len, a.length - indexA - len), + StringRegion (b.text + indexB + len, b.start + indexB + len, b.length - indexB - len)); + } + else + { + if (a.length > 0) addDeletion (td, b.start, a.length); + if (b.length > 0) addInsertion (td, b.text, b.start, b.length); + } + } + + static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, + const String::CharPointerType b, const int lenB, + int& indexInA, int& indexInB) + { + if (lenA == 0 || lenB == 0) + return 0; + + HeapBlock lines; + lines.calloc (2 + 2 * (size_t) lenB); + + int* l0 = lines; + int* l1 = l0 + lenB + 1; + + int loopsWithoutImprovement = 0; + int bestLength = 0; + indexInA = indexInB = 0; + + for (int i = 0; i < lenA; ++i) + { + const juce_wchar ca = a.getAndAdvance(); + String::CharPointerType b2 (b); + + for (int j = 0; j < lenB; ++j) + { + if (ca != b2.getAndAdvance()) + { + l1[j + 1] = 0; + } + else + { + const int len = l0[j] + 1; + l1[j + 1] = len; + + if (len > bestLength) + { + loopsWithoutImprovement = 0; + bestLength = len; + indexInA = i; + indexInB = j; + } + } + } + + if (++loopsWithoutImprovement > 100) + break; + + std::swap (l0, l1); + } + + indexInA -= bestLength - 1; + indexInB -= bestLength - 1; + return bestLength; + } +}; + +TextDiff::TextDiff (const String& original, const String& target) +{ + TextDiffHelpers::diffSkippingCommonStart (*this, original, target); +} + +String TextDiff::appliedTo (String text) const +{ + for (int i = 0; i < changes.size(); ++i) + text = changes.getReference(i).appliedTo (text); + + return text; +} + +bool TextDiff::Change::isDeletion() const noexcept +{ + return insertedText.isEmpty(); +} + +String TextDiff::Change::appliedTo (const String& text) const noexcept +{ + return text.substring (0, start) + (isDeletion() ? text.substring (start + length) + : (insertedText + text.substring (start))); +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class DiffTests : public UnitTest +{ +public: + DiffTests() : UnitTest ("TextDiff class") {} + + static String createString (Random& r) + { + juce_wchar buffer[50] = { 0 }; + + for (int i = r.nextInt (49); --i >= 0;) + { + if (r.nextInt (10) == 0) + { + do + { + buffer[i] = (juce_wchar) (1 + r.nextInt (0x10ffff - 1)); + } + while (! CharPointer_UTF16::canRepresent (buffer[i])); + } + else + buffer[i] = (juce_wchar) ('a' + r.nextInt (3)); + } + + return CharPointer_UTF32 (buffer); + } + + void testDiff (const String& a, const String& b) + { + TextDiff diff (a, b); + const String result (diff.appliedTo (a)); + expectEquals (result, b); + } + + void runTest() + { + beginTest ("TextDiff"); + + Random r = getRandom(); + + testDiff (String::empty, String::empty); + testDiff ("x", String::empty); + testDiff (String::empty, "x"); + testDiff ("x", "x"); + testDiff ("x", "y"); + testDiff ("xxx", "x"); + testDiff ("x", "xxx"); + + for (int i = 5000; --i >= 0;) + { + String s (createString (r)); + testDiff (s, createString (r)); + testDiff (s + createString (r), s + createString (r)); + } + } +}; + +static DiffTests diffTests; + +#endif diff --git a/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h new file mode 100644 index 0000000..d420c3d --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/text/juce_TextDiff.h @@ -0,0 +1,80 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_TEXTDIFF_H_INCLUDED +#define JUCE_TEXTDIFF_H_INCLUDED + + +/** + Calculates and applies a sequence of changes to convert one text string into + another. + + Once created, the TextDiff object contains an array of change objects, where + each change can be either an insertion or a deletion. When applied in order + to the original string, these changes will convert it to the target string. +*/ +class JUCE_API TextDiff +{ +public: + /** Creates a set of diffs for converting the original string into the target. */ + TextDiff (const String& original, + const String& target); + + /** Applies this sequence of changes to the original string, producing the + target string that was specified when generating them. + + Obviously it only makes sense to call this function with the string that + was originally passed to the constructor. Any other input will produce an + undefined result. + */ + String appliedTo (String text) const; + + /** Describes a change, which can be either an insertion or deletion. */ + struct Change + { + String insertedText; /**< If this change is a deletion, this string will be empty; otherwise, + it'll be the text that should be inserted at the index specified by start. */ + int start; /**< Specifies the character index in a string at which text should be inserted or deleted. */ + int length; /**< If this change is a deletion, this specifies the number of characters to delete. For an + insertion, this is the length of the new text being inserted. */ + + /** Returns true if this change is a deletion, or false for an insertion. */ + bool isDeletion() const noexcept; + + /** Returns the result of applying this change to a string. */ + String appliedTo (const String& original) const noexcept; + }; + + /** The list of changes required to perform the transformation. + Applying each of these, in order, to the original string will produce the target. + */ + Array changes; +}; + + +#endif // JUCE_TEXTDIFF_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp index 723e878..4566b13 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -26,9 +29,29 @@ ChildProcess::ChildProcess() {} ChildProcess::~ChildProcess() {} +bool ChildProcess::isRunning() const +{ + return activeProcess != nullptr && activeProcess->isRunning(); +} + +int ChildProcess::readProcessOutput (void* dest, int numBytes) +{ + return activeProcess != nullptr ? activeProcess->read (dest, numBytes) : 0; +} + +bool ChildProcess::kill() +{ + return activeProcess == nullptr || activeProcess->killProcess(); +} + +uint32 ChildProcess::getExitCode() const +{ + return activeProcess != nullptr ? activeProcess->getExitCode() : 0; +} + bool ChildProcess::waitForProcessToFinish (const int timeoutMs) const { - const int64 timeoutTime = Time::getMillisecondCounter() + timeoutMs; + const uint32 timeoutTime = Time::getMillisecondCounter() + (uint32) timeoutMs; do { @@ -52,7 +75,7 @@ String ChildProcess::readAllProcessOutput() if (num <= 0) break; - result.write (buffer, num); + result.write (buffer, (size_t) num); } return result.toString(); diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h index 454a6c6..0adcb57 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ChildProcess.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CHILDPROCESS_JUCEHEADER__ -#define __JUCE_CHILDPROCESS_JUCEHEADER__ +#ifndef JUCE_CHILDPROCESS_H_INCLUDED +#define JUCE_CHILDPROCESS_H_INCLUDED //============================================================================== @@ -48,14 +51,34 @@ public: */ ~ChildProcess(); + /** These flags are used by the start() methods. */ + enum StreamFlags + { + wantStdOut = 1, + wantStdErr = 2 + }; + /** Attempts to launch a child process command. The command should be the name of the executable file, followed by any arguments that are required. If the process has already been launched, this will launch it again. If a problem occurs, the method will return false. + The streamFlags is a combinations of values to indicate which of the child's output + streams should be read and returned by readProcessOutput(). */ - bool start (const String& command); + bool start (const String& command, int streamFlags = wantStdOut | wantStdErr); + + /** Attempts to launch a child process command. + + The first argument should be the name of the executable file, followed by any other + arguments that are needed. + If the process has already been launched, this will launch it again. If a problem + occurs, the method will return false. + The streamFlags is a combinations of values to indicate which of the child's output + streams should be read and returned by readProcessOutput(). + */ + bool start (const StringArray& arguments, int streamFlags = wantStdOut | wantStdErr); /** Returns true if the child process is alive. */ bool isRunning() const; @@ -74,6 +97,9 @@ public: /** Blocks until the process is no longer running. */ bool waitForProcessToFinish (int timeoutMs) const; + /** If the process has finished, this returns its exit code. */ + uint32 getExitCode() const; + /** Attempts to kill the child process. Returns true if it succeeded. Trying to read from the process after calling this may result in undefined behaviour. @@ -83,11 +109,11 @@ public: private: //============================================================================== class ActiveProcess; - friend class ScopedPointer; + friend struct ContainerDeletePolicy; ScopedPointer activeProcess; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess) }; -#endif // __JUCE_CHILDPROCESS_JUCEHEADER__ +#endif // JUCE_CHILDPROCESS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h b/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h index 23b7f84..60e61dd 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_CriticalSection.h @@ -1,42 +1,47 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_CRITICALSECTION_JUCEHEADER__ -#define __JUCE_CRITICALSECTION_JUCEHEADER__ - -#include "juce_ScopedLock.h" +#ifndef JUCE_CRITICALSECTION_H_INCLUDED +#define JUCE_CRITICALSECTION_H_INCLUDED //============================================================================== /** - A mutex class. + A re-entrant mutex. - A CriticalSection acts as a re-entrant mutex lock. The best way to lock and unlock + A CriticalSection acts as a re-entrant mutex object. The best way to lock and unlock one of these is by using RAII in the form of a local ScopedLock object - have a look through the codebase for many examples of how to do this. + In almost all cases you'll want to declare your CriticalSection as a member variable. + Occasionally you may want to declare one as a static variable, but in that case the usual + C++ static object order-of-construction warnings should be heeded. + @see ScopedLock, ScopedTryLock, ScopedUnlock, SpinLock, ReadWriteLock, Thread, InterProcessLock */ class JUCE_API CriticalSection @@ -102,19 +107,19 @@ public: private: //============================================================================== #if JUCE_WINDOWS - // To avoid including windows.h in the public JUCE headers, we'll just allocate a - // block of memory here that's big enough to be used internally as a windows critical - // section structure. + // To avoid including windows.h in the public JUCE headers, we'll just allocate + // a block of memory here that's big enough to be used internally as a windows + // CRITICAL_SECTION structure. #if JUCE_64BIT - uint8 internal [44]; + uint8 lock[44]; #else - uint8 internal [24]; + uint8 lock[24]; #endif #else - mutable pthread_mutex_t internal; + mutable pthread_mutex_t lock; #endif - JUCE_DECLARE_NON_COPYABLE (CriticalSection); + JUCE_DECLARE_NON_COPYABLE (CriticalSection) }; @@ -149,28 +154,34 @@ public: typedef ScopedLockType ScopedUnlockType; private: - JUCE_DECLARE_NON_COPYABLE (DummyCriticalSection); + JUCE_DECLARE_NON_COPYABLE (DummyCriticalSection) }; //============================================================================== /** Automatically locks and unlocks a CriticalSection object. - Use one of these as a local variable to provide RAII-based locking of a CriticalSection. + You can use a ScopedLock as a local variable to provide RAII-based locking of a CriticalSection. e.g. @code - CriticalSection myCriticalSection; - - for (;;) + struct MyObject { - const ScopedLock myScopedLock (myCriticalSection); - // myCriticalSection is now locked + CriticalSection objectLock; - ...do some stuff... + // assuming that this example function will be called by multiple threads + void foo() + { + const ScopedLock myScopedLock (objectLock); - // myCriticalSection gets unlocked here. - } + // objectLock is now locked.. + + ...do some thread-safe work here... + + // ..and objectLock gets unlocked here, as myScopedLock goes out of + // scope at the end of the block + } + }; @endcode @see CriticalSection, ScopedUnlock @@ -188,29 +199,29 @@ typedef CriticalSection::ScopedLockType ScopedLock; e.g. @code - CriticalSection myCriticalSection; - - for (;;) + struct MyObject { - const ScopedLock myScopedLock (myCriticalSection); - // myCriticalSection is now locked + CriticalSection objectLock; - ... do some stuff with it locked .. - - while (xyz) + void foo() { - ... do some stuff with it locked .. + { + const ScopedLock myScopedLock (objectLock); - const ScopedUnlock unlocker (myCriticalSection); + // objectLock is now locked.. - // myCriticalSection is now unlocked for the remainder of this block, - // and re-locked at the end. + { + ScopedUnlock myUnlocker (objectLock); - ...do some stuff with it unlocked ... + // ..and now unlocked.. + } + + // ..and now locked again.. + } + + // ..and finally unlocked. } - - // myCriticalSection gets unlocked here. - } + }; @endcode @see CriticalSection, ScopedLock @@ -224,26 +235,27 @@ typedef CriticalSection::ScopedUnlockType ScopedUnlock; Use one of these as a local variable to control access to a CriticalSection. e.g. @code - CriticalSection myCriticalSection; - for (;;) + struct MyObject { - const ScopedTryLock myScopedTryLock (myCriticalSection); + CriticalSection objectLock; - // 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()) + void foo() { - ...do some stuff... - } - else - { - ..our attempt at locking failed because another thread had already locked it.. - } + const ScopedTryLock myScopedTryLock (objectLock); - // myCriticalSection gets unlocked here (if it was locked) - } + // Unlike using a ScopedLock, this may fail to actually get the lock, so you + // must call the isLocked() method before making any assumptions.. + if (myScopedTryLock.isLocked()) + { + ...safely do some work... + } + else + { + // If we get here, then our attempt at locking failed because another thread had already locked it.. + } + } + }; @endcode @see CriticalSection::tryEnter, ScopedLock, ScopedUnlock, ScopedReadLock @@ -251,4 +263,4 @@ typedef CriticalSection::ScopedUnlockType ScopedUnlock; typedef CriticalSection::ScopedTryLockType ScopedTryLock; -#endif // __JUCE_CRITICALSECTION_JUCEHEADER__ +#endif // JUCE_CRITICALSECTION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h b/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h index 1b81f49..df6625b 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_DynamicLibrary.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_DYNAMICLIBRARY_JUCEHEADER__ -#define __JUCE_DYNAMICLIBRARY_JUCEHEADER__ +#ifndef JUCE_DYNAMICLIBRARY_H_INCLUDED +#define JUCE_DYNAMICLIBRARY_H_INCLUDED /** Handles the opening and closing of DLLs. @@ -75,8 +78,8 @@ public: private: void* handle; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DynamicLibrary); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DynamicLibrary) }; -#endif // __JUCE_DYNAMICLIBRARY_JUCEHEADER__ +#endif // JUCE_DYNAMICLIBRARY_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp new file mode 100644 index 0000000..3475b74 --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.cpp @@ -0,0 +1,36 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +HighResolutionTimer::HighResolutionTimer() { pimpl = new Pimpl (*this); } +HighResolutionTimer::~HighResolutionTimer() { stopTimer(); } + +void HighResolutionTimer::startTimer (int periodMs) { pimpl->start (jmax (1, periodMs)); } +void HighResolutionTimer::stopTimer() { pimpl->stop(); } + +bool HighResolutionTimer::isTimerRunning() const noexcept { return pimpl->periodMs != 0; } +int HighResolutionTimer::getTimerInterval() const noexcept { return pimpl->periodMs; } diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h new file mode 100644 index 0000000..21022ad --- /dev/null +++ b/JuceLibraryCode/modules/juce_core/threads/juce_HighResolutionTimer.h @@ -0,0 +1,109 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_HIGHRESOLUTIONTIMER_H_INCLUDED +#define JUCE_HIGHRESOLUTIONTIMER_H_INCLUDED + +/** + A high-resolution periodic timer. + + This provides accurately-timed regular callbacks. Unlike the normal Timer + class, this one uses a dedicated thread, not the message thread, so is + far more stable and precise. + + You should only use this class in situations where you really need accuracy, + because unlike the normal Timer class, which is very lightweight and cheap + to start/stop, the HighResolutionTimer will use far more resources, and + starting/stopping it may involve launching and killing threads. + + @see Timer +*/ +class JUCE_API HighResolutionTimer +{ +protected: + /** Creates a HighResolutionTimer. + When created, the timer is stopped, so use startTimer() to get it going. + */ + HighResolutionTimer(); + +public: + /** Destructor. */ + virtual ~HighResolutionTimer(); + + //============================================================================== + /** The user-defined callback routine that actually gets called periodically. + + This will be called on a dedicated timer thread, so make sure your + implementation is thread-safe! + + It's perfectly ok to call startTimer() or stopTimer() from within this + callback to change the subsequent intervals. + */ + virtual void hiResTimerCallback() = 0; + + //============================================================================== + /** Starts the timer and sets the length of interval required. + + If the timer is already started, this will reset its counter, 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); + + /** Stops the timer. + + This method may block while it waits for pending callbacks to complete. Once it + returns, no more callbacks will be made. If it is called from the timer's own thread, + it will cancel the timer after the current callback returns. + */ + void stopTimer(); + + /** Checks if the timer has been started. + @returns true if the timer is running. + */ + bool isTimerRunning() const noexcept; + + /** 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 noexcept; + +private: + struct Pimpl; + friend struct Pimpl; + friend struct ContainerDeletePolicy; + ScopedPointer pimpl; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HighResolutionTimer) +}; + + +#endif // JUCE_HIGHRESOLUTIONTIMER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h index 33e8f2f..dc903b0 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_InterProcessLock.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_INTERPROCESSLOCK_JUCEHEADER__ -#define __JUCE_INTERPROCESSLOCK_JUCEHEADER__ - -#include "../text/juce_String.h" -#include "../memory/juce_ScopedPointer.h" +#ifndef JUCE_INTERPROCESSLOCK_H_INCLUDED +#define JUCE_INTERPROCESSLOCK_H_INCLUDED //============================================================================== @@ -41,13 +41,11 @@ class JUCE_API InterProcessLock public: //============================================================================== /** Creates a lock object. - - @param name a name that processes will use to identify this 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(); @@ -55,17 +53,15 @@ public: //============================================================================== /** 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 + @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. - */ + /** Releases the lock if it's currently held by this process. */ void exit(); //============================================================================== @@ -94,7 +90,7 @@ public: 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(); } + explicit ScopedLockType (InterProcessLock& l) : ipLock (l) { lockWasSuccessful = l.enter(); } /** Destructor. @@ -103,30 +99,30 @@ public: Make sure this object is created and deleted by the same thread, otherwise there are no guarantees what will happen! */ - inline ~ScopedLockType() { lock_.exit(); } + inline ~ScopedLockType() { ipLock.exit(); } /** Returns true if the InterProcessLock was successfully locked. */ - bool isLocked() const noexcept { return lockWasSuccessful; } + bool isLocked() const noexcept { return lockWasSuccessful; } private: //============================================================================== - InterProcessLock& lock_; + InterProcessLock& ipLock; bool lockWasSuccessful; - JUCE_DECLARE_NON_COPYABLE (ScopedLockType); + JUCE_DECLARE_NON_COPYABLE (ScopedLockType) }; private: //============================================================================== class Pimpl; - friend class ScopedPointer ; - ScopedPointer pimpl; + friend struct ContainerDeletePolicy; + ScopedPointer pimpl; CriticalSection lock; String name; - JUCE_DECLARE_NON_COPYABLE (InterProcessLock); + JUCE_DECLARE_NON_COPYABLE (InterProcessLock) }; -#endif // __JUCE_INTERPROCESSLOCK_JUCEHEADER__ +#endif // JUCE_INTERPROCESSLOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Process.h b/JuceLibraryCode/modules/juce_core/threads/juce_Process.h index 522d2a6..f3efd66 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Process.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Process.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_PROCESS_JUCEHEADER__ -#define __JUCE_PROCESS_JUCEHEADER__ - -#include "../text/juce_String.h" +#ifndef JUCE_PROCESS_H_INCLUDED +#define JUCE_PROCESS_H_INCLUDED //============================================================================== @@ -35,7 +36,7 @@ This contains methods for controlling the current application at the process-level. - @see Thread, JUCEApplication + @see Thread, JUCEApplicationBase */ class JUCE_API Process { @@ -54,7 +55,7 @@ public: @param priority the process priority, where 0=low, 1=normal, 2=high, 3=realtime */ - static void setPriority (const ProcessPriority priority); + static void JUCE_CALLTYPE setPriority (const ProcessPriority priority); /** Kills the current process immediately. @@ -62,15 +63,23 @@ public: immediately - it's intended only for use only when something goes horribly wrong. - @see JUCEApplication::quit + @see JUCEApplicationBase::quit */ - static void terminate(); + static void JUCE_CALLTYPE terminate(); //============================================================================== /** Returns true if this application process is the one that the user is currently using. */ - static bool isForegroundProcess(); + static bool JUCE_CALLTYPE isForegroundProcess(); + + /** Attempts to make the current process the active one. + (This is not possible on some platforms). + */ + static void JUCE_CALLTYPE makeForegroundProcess(); + + /** Hides the application (on an OS that supports this, e.g. OSX) */ + static void JUCE_CALLTYPE hide(); //============================================================================== /** Raises the current process's privilege level. @@ -78,30 +87,29 @@ public: Does nothing if this isn't supported by the current OS, or if process privilege level is fixed. */ - static void raisePrivilege(); + static void JUCE_CALLTYPE 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(); + static void JUCE_CALLTYPE lowerPrivilege(); //============================================================================== - /** Returns true if this process is being hosted by a debugger. - */ + /** Returns true if this process is being hosted by a debugger. */ static bool JUCE_CALLTYPE isRunningUnderDebugger(); //============================================================================== /** Tries to launch the OS's default reader application for a given file or URL. */ - static bool openDocument (const String& documentURL, const String& parameters); + static bool JUCE_CALLTYPE openDocument (const String& documentURL, const String& parameters); /** Tries to launch the OS's default email application to let the user create a message. */ - static bool openEmailWithAttachments (const String& targetEmailAddress, - const String& emailSubject, - const String& bodyText, - const StringArray& filesToAttach); + static bool JUCE_CALLTYPE openEmailWithAttachments (const String& targetEmailAddress, + const String& emailSubject, + const String& bodyText, + const StringArray& filesToAttach); #if JUCE_WINDOWS || DOXYGEN //============================================================================== @@ -128,17 +136,18 @@ public: @see getCurrentModuleInstanceHandle() */ static void JUCE_CALLTYPE setCurrentModuleInstanceHandle (void* newHandle) noexcept; + #endif - /** WINDOWS ONLY - Gets the command-line params as a string. - This is needed to avoid unicode problems with the argc type params. - */ - static String JUCE_CALLTYPE getCurrentCommandLineParams(); + #if JUCE_MAC || DOXYGEN + //============================================================================== + /** OSX ONLY - Shows or hides the OSX dock icon for this app. */ + static void setDockIconVisible (bool isVisible); #endif private: Process(); - JUCE_DECLARE_NON_COPYABLE (Process); + JUCE_DECLARE_NON_COPYABLE (Process) }; -#endif // __JUCE_PROCESS_JUCEHEADER__ +#endif // JUCE_PROCESS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp index a263e2a..a0821b4 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -39,39 +42,37 @@ ReadWriteLock::~ReadWriteLock() noexcept //============================================================================== void ReadWriteLock::enterRead() const noexcept +{ + while (! tryEnterRead()) + waitEvent.wait (100); +} + +bool ReadWriteLock::tryEnterRead() const noexcept { const Thread::ThreadID threadId = Thread::getCurrentThreadId(); + const SpinLock::ScopedLockType sl (accessLock); - for (;;) + for (int i = 0; i < readerThreads.size(); ++i) { - jassert (readerThreads.size() % 2 == 0); + ThreadRecursionCount& trc = readerThreads.getReference(i); - int i; - for (i = 0; i < readerThreads.size(); i += 2) - if (readerThreads.getUnchecked(i) == threadId) - break; - - if (i < readerThreads.size() - || numWriters + numWaitingWriters == 0 - || (threadId == writerThreadId && numWriters > 0)) + if (trc.threadID == threadId) { - if (i < readerThreads.size()) - { - readerThreads.set (i + 1, (Thread::ThreadID) (1 + (pointer_sized_int) readerThreads.getUnchecked (i + 1))); - } - else - { - readerThreads.add (threadId); - readerThreads.add ((Thread::ThreadID) 1); - } - - return; + trc.count++; + return true; } - - const SpinLock::ScopedUnlockType ul (accessLock); - waitEvent.wait (100); } + + if (numWriters + numWaitingWriters == 0 + || (threadId == writerThreadId && numWriters > 0)) + { + ThreadRecursionCount trc = { threadId, 1 }; + readerThreads.add (trc); + return true; + } + + return false; } void ReadWriteLock::exitRead() const noexcept @@ -79,21 +80,17 @@ void ReadWriteLock::exitRead() const noexcept const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); - for (int i = 0; i < readerThreads.size(); i += 2) + for (int i = 0; i < readerThreads.size(); ++i) { - if (readerThreads.getUnchecked(i) == threadId) - { - const pointer_sized_int newCount = ((pointer_sized_int) readerThreads.getUnchecked (i + 1)) - 1; + ThreadRecursionCount& trc = readerThreads.getReference(i); - if (newCount == 0) + if (trc.threadID == threadId) + { + if (--(trc.count) == 0) { - readerThreads.removeRange (i, 2); + readerThreads.remove (i); waitEvent.signal(); } - else - { - readerThreads.set (i + 1, (Thread::ThreadID) newCount); - } return; } @@ -108,18 +105,8 @@ void ReadWriteLock::enterWrite() const noexcept const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); - for (;;) + while (! tryEnterWriteInternal (threadId)) { - if (readerThreads.size() + numWriters == 0 - || threadId == writerThreadId - || (readerThreads.size() == 2 - && readerThreads.getUnchecked(0) == threadId)) - { - writerThreadId = threadId; - ++numWriters; - break; - } - ++numWaitingWriters; accessLock.exit(); waitEvent.wait (100); @@ -130,13 +117,15 @@ void ReadWriteLock::enterWrite() const noexcept bool ReadWriteLock::tryEnterWrite() const noexcept { - const Thread::ThreadID threadId = Thread::getCurrentThreadId(); const SpinLock::ScopedLockType sl (accessLock); + return tryEnterWriteInternal (Thread::getCurrentThreadId()); +} +bool ReadWriteLock::tryEnterWriteInternal (Thread::ThreadID threadId) const noexcept +{ if (readerThreads.size() + numWriters == 0 || threadId == writerThreadId - || (readerThreads.size() == 2 - && readerThreads.getUnchecked(0) == threadId)) + || (readerThreads.size() == 1 && readerThreads.getReference(0).threadID == threadId)) { writerThreadId = threadId; ++numWriters; diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h index 7b729a7..c41d258 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ReadWriteLock.h @@ -1,36 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_READWRITELOCK_JUCEHEADER__ -#define __JUCE_READWRITELOCK_JUCEHEADER__ - -#include "juce_CriticalSection.h" -#include "juce_SpinLock.h" -#include "juce_WaitableEvent.h" -#include "juce_Thread.h" -#include "../containers/juce_Array.h" +#ifndef JUCE_READWRITELOCK_H_INCLUDED +#define JUCE_READWRITELOCK_H_INCLUDED //============================================================================== @@ -62,23 +59,30 @@ public: ReadWriteLock() noexcept; /** Destructor. - - If the object is deleted whilst locked, any subsequent behaviour - is unpredictable. + If the object is deleted whilst locked, any subsequent behaviour is undefined. */ ~ReadWriteLock() noexcept; //============================================================================== /** 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. + Multiple threads can simultaneously 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 noexcept; + /** Tries to lock this object for reading. + + Multiple threads can simultaneously lock the object for reading, but if another + thread has it locked for writing, then this will fail and return false. + + @returns true if the lock is successfully gained. + @see exitRead, ScopedReadLock + */ + bool tryEnterRead() const noexcept; + /** Releases the read-lock. If the caller thread hasn't got the lock, this can have unpredictable results. @@ -106,6 +110,7 @@ public: This is like enterWrite(), but doesn't block - it returns true if it manages to obtain the lock. + @returns true if the lock is successfully gained. @see enterWrite */ bool tryEnterWrite() const noexcept; @@ -129,10 +134,19 @@ private: WaitableEvent waitEvent; mutable int numWaitingWriters, numWriters; mutable Thread::ThreadID writerThreadId; - mutable Array readerThreads; - JUCE_DECLARE_NON_COPYABLE (ReadWriteLock); + struct ThreadRecursionCount + { + Thread::ThreadID threadID; + int count; + }; + + mutable Array readerThreads; + + bool tryEnterWriteInternal (Thread::ThreadID) const noexcept; + + JUCE_DECLARE_NON_COPYABLE (ReadWriteLock) }; -#endif // __JUCE_READWRITELOCK_JUCEHEADER__ +#endif // JUCE_READWRITELOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h index 83cc9dc..442551a 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedLock.h @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SCOPEDLOCK_JUCEHEADER__ -#define __JUCE_SCOPEDLOCK_JUCEHEADER__ +#ifndef JUCE_SCOPEDLOCK_H_INCLUDED +#define JUCE_SCOPEDLOCK_H_INCLUDED //============================================================================== @@ -79,7 +82,7 @@ private: //============================================================================== const LockType& lock_; - JUCE_DECLARE_NON_COPYABLE (GenericScopedLock); + JUCE_DECLARE_NON_COPYABLE (GenericScopedLock) }; @@ -152,7 +155,7 @@ private: //============================================================================== const LockType& lock_; - JUCE_DECLARE_NON_COPYABLE (GenericScopedUnlock); + JUCE_DECLARE_NON_COPYABLE (GenericScopedUnlock) }; @@ -227,8 +230,8 @@ private: const LockType& lock_; const bool lockWasSuccessful; - JUCE_DECLARE_NON_COPYABLE (GenericScopedTryLock); + JUCE_DECLARE_NON_COPYABLE (GenericScopedTryLock) }; -#endif // __JUCE_SCOPEDLOCK_JUCEHEADER__ +#endif // JUCE_SCOPEDLOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h index ff68f36..107e838 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedReadLock.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SCOPEDREADLOCK_JUCEHEADER__ -#define __JUCE_SCOPEDREADLOCK_JUCEHEADER__ - -#include "juce_ReadWriteLock.h" +#ifndef JUCE_SCOPEDREADLOCK_H_INCLUDED +#define JUCE_SCOPEDREADLOCK_H_INCLUDED //============================================================================== @@ -82,8 +83,8 @@ private: //============================================================================== const ReadWriteLock& lock_; - JUCE_DECLARE_NON_COPYABLE (ScopedReadLock); + JUCE_DECLARE_NON_COPYABLE (ScopedReadLock) }; -#endif // __JUCE_SCOPEDREADLOCK_JUCEHEADER__ +#endif // JUCE_SCOPEDREADLOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h index b52a6b5..44e3198 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ScopedWriteLock.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ -#define __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ - -#include "juce_ReadWriteLock.h" +#ifndef JUCE_SCOPEDWRITELOCK_H_INCLUDED +#define JUCE_SCOPEDWRITELOCK_H_INCLUDED //============================================================================== @@ -82,8 +83,8 @@ private: //============================================================================== const ReadWriteLock& lock_; - JUCE_DECLARE_NON_COPYABLE (ScopedWriteLock); + JUCE_DECLARE_NON_COPYABLE (ScopedWriteLock) }; -#endif // __JUCE_SCOPEDWRITELOCK_JUCEHEADER__ +#endif // JUCE_SCOPEDWRITELOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h b/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h index 3dac911..664cc3c 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_SpinLock.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_SPINLOCK_JUCEHEADER__ -#define __JUCE_SPINLOCK_JUCEHEADER__ - -#include "juce_ScopedLock.h" +#ifndef JUCE_SPINLOCK_H_INCLUDED +#define JUCE_SPINLOCK_H_INCLUDED //============================================================================== @@ -83,8 +84,8 @@ private: //============================================================================== mutable Atomic lock; - JUCE_DECLARE_NON_COPYABLE (SpinLock); + JUCE_DECLARE_NON_COPYABLE (SpinLock) }; -#endif // __JUCE_SPINLOCK_JUCEHEADER__ +#endif // JUCE_SPINLOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp index 5df2ee5..3d1ff0a 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -44,7 +47,7 @@ Thread::~Thread() */ jassert (! isThreadRunning()); - stopThread (100); + stopThread (-1); } //============================================================================== @@ -57,15 +60,20 @@ struct CurrentThreadHolder : public ReferenceCountedObject typedef ReferenceCountedObjectPtr Ptr; ThreadLocalValue value; - JUCE_DECLARE_NON_COPYABLE (CurrentThreadHolder); + JUCE_DECLARE_NON_COPYABLE (CurrentThreadHolder) }; static char currentThreadHolderLock [sizeof (SpinLock)]; // (statically initialised to zeros). +static SpinLock* castToSpinLockWithoutAliasingWarning (void* s) +{ + return static_cast (s); +} + static CurrentThreadHolder::Ptr getCurrentThreadHolder() { static CurrentThreadHolder::Ptr currentThreadHolder; - SpinLock::ScopedLockType lock (*reinterpret_cast (currentThreadHolderLock)); + SpinLock::ScopedLockType lock (*castToSpinLockWithoutAliasingWarning (currentThreadHolderLock)); if (currentThreadHolder == nullptr) currentThreadHolder = new CurrentThreadHolder(); @@ -140,7 +148,7 @@ bool Thread::isThreadRunning() const return threadHandle != nullptr; } -Thread* Thread::getCurrentThread() +Thread* JUCE_CALLTYPE Thread::getCurrentThread() { return getCurrentThreadHolder()->value.get(); } @@ -156,21 +164,20 @@ bool Thread::waitForThreadToExit (const int timeOutMilliseconds) const // Doh! So how exactly do you expect this thread to wait for itself to stop?? jassert (getThreadId() != getCurrentThreadId() || getCurrentThreadId() == 0); - const int sleepMsPerIteration = 5; - int count = timeOutMilliseconds / sleepMsPerIteration; + const uint32 timeoutEnd = Time::getMillisecondCounter() + (uint32) timeOutMilliseconds; while (isThreadRunning()) { - if (timeOutMilliseconds >= 0 && --count < 0) + if (timeOutMilliseconds >= 0 && Time::getMillisecondCounter() > timeoutEnd) return false; - sleep (sleepMsPerIteration); + sleep (2); } return true; } -void Thread::stopThread (const int timeOutMilliseconds) +bool Thread::stopThread (const int timeOutMilliseconds) { // agh! You can't stop the thread that's calling this method! How on earth // would that work?? @@ -197,16 +204,24 @@ void Thread::stopThread (const int timeOutMilliseconds) threadHandle = nullptr; threadId = 0; + return false; } } + + return true; } //============================================================================== bool Thread::setPriority (const int newPriority) { + // NB: deadlock possible if you try to set the thread prio from the thread itself, + // so using setCurrentThreadPriority instead in that case. + if (getCurrentThreadId() == getThreadId()) + return setCurrentThreadPriority (newPriority); + const ScopedLock sl (startStopLock); - if (setThreadPriority (threadHandle, newPriority)) + if ((! isThreadRunning()) || setThreadPriority (threadHandle, newPriority)) { threadPriority = newPriority; return true; @@ -269,7 +284,7 @@ public: expect (ByteOrder::swap ((uint16) 0x1122) == 0x2211); expect (ByteOrder::swap ((uint32) 0x11223344) == 0x44332211); - expect (ByteOrder::swap ((uint64) literal64bit (0x1122334455667788)) == literal64bit (0x8877665544332211)); + expect (ByteOrder::swap ((uint64) 0x1122334455667788ULL) == 0x8877665544332211LL); beginTest ("Atomic int"); AtomicTester ::testInteger (*this); diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h index d464556..aef96b7 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_Thread.h @@ -1,33 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_THREAD_JUCEHEADER__ -#define __JUCE_THREAD_JUCEHEADER__ - -#include "juce_WaitableEvent.h" -#include "juce_CriticalSection.h" +#ifndef JUCE_THREAD_H_INCLUDED +#define JUCE_THREAD_H_INCLUDED //============================================================================== @@ -58,10 +58,10 @@ public: /** 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). + You must never attempt to delete a Thread object while it's still running - + always call stopThread() and make sure your thread has stopped before deleting + the object. Failing to do so will throw an assertion, and put you firmly into + undefined behaviour territory. */ virtual ~Thread(); @@ -81,8 +81,8 @@ public: /** Starts the thread running. - This will start the thread's run() method. - (if it's already started, startThread() won't do anything). + This will cause the thread's run() method to be called by a new thread. + If this thread is already running, startThread() won't do anything. @see stopThread */ @@ -113,9 +113,11 @@ public: @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. + @returns true if the thread was cleanly stopped before the timeout, or false + if it had to be killed by force. @see signalThreadShouldExit, threadShouldExit, waitForThreadToExit, isThreadRunning */ - void stopThread (int timeOutMilliseconds); + bool stopThread (int timeOutMilliseconds); //============================================================================== /** Returns true if the thread is currently active */ @@ -183,12 +185,10 @@ public: 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); + static void JUCE_CALLTYPE setCurrentThreadAffinityMask (uint32 affinityMask); //============================================================================== // this can be called from any thread that needs to pause.. @@ -230,14 +230,14 @@ public: @returns a unique identifier that identifies the calling thread. @see getThreadId */ - static ThreadID getCurrentThreadId(); + static ThreadID JUCE_CALLTYPE 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(); + static Thread* JUCE_CALLTYPE getCurrentThread(); /** Returns the ID of this thread. @@ -259,7 +259,7 @@ public: /** Changes the name of the caller thread. Different OSes may place different length or content limits on this name. */ - static void setCurrentThreadName (const String& newThreadName); + static void JUCE_CALLTYPE setCurrentThreadName (const String& newThreadName); private: @@ -283,7 +283,7 @@ private: void threadEntryPoint(); static bool setThreadPriority (void*, int); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Thread) }; -#endif // __JUCE_THREAD_JUCEHEADER__ +#endif // JUCE_THREAD_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h index 7009d9e..bd5c808 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadLocalValue.h @@ -1,34 +1,37 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_THREADLOCALVALUE_JUCEHEADER__ -#define __JUCE_THREADLOCALVALUE_JUCEHEADER__ +#ifndef JUCE_THREADLOCALVALUE_H_INCLUDED +#define JUCE_THREADLOCALVALUE_H_INCLUDED // (NB: on win32, native thread-locals aren't possible in a dynamically loaded DLL in XP). -#if ! ((JUCE_MSVC && (defined (_WIN64) || ! defined (JucePlugin_PluginCode))) \ - || (JUCE_MAC && defined (__clang__) && defined (MAC_OS_X_VERSION_10_7) \ +#if ! ((JUCE_MSVC && (JUCE_64BIT || ! defined (JucePlugin_PluginCode))) \ + || (JUCE_MAC && JUCE_CLANG && defined (MAC_OS_X_VERSION_10_7) \ && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7)) #define JUCE_NO_COMPILER_THREAD_LOCAL 1 #endif @@ -44,8 +47,7 @@ Typically, you'll probably want to create a static instance of a ThreadLocalValue object, or hold one within a singleton. - The templated class for your value could be a primitive type, or any class that - has a default constructor and copy operator. + The templated class for your value must be a primitive type, or a simple POD struct. When a thread no longer needs to use its value, it can call releaseCurrentThreadStorage() to allow the storage to be re-used by another thread. If a thread exits without calling @@ -174,23 +176,23 @@ private: #if JUCE_NO_COMPILER_THREAD_LOCAL struct ObjectHolder { - ObjectHolder (const Thread::ThreadID& threadId_) - : threadId (threadId_), object() + ObjectHolder (const Thread::ThreadID& tid) + : threadId (tid), next (nullptr), object() {} Thread::ThreadID threadId; ObjectHolder* next; Type object; - JUCE_DECLARE_NON_COPYABLE (ObjectHolder); + JUCE_DECLARE_NON_COPYABLE (ObjectHolder) }; mutable Atomic first; SpinLock lock; #endif - JUCE_DECLARE_NON_COPYABLE (ThreadLocalValue); + JUCE_DECLARE_NON_COPYABLE (ThreadLocalValue) }; -#endif // __JUCE_THREADLOCALVALUE_JUCEHEADER__ +#endif // JUCE_THREADLOCALVALUE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp index 2c9bf4c..e6b0b9f 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.cpp @@ -1,34 +1,56 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ +class ThreadPool::ThreadPoolThread : public Thread +{ +public: + ThreadPoolThread (ThreadPool& p) + : Thread ("Pool"), currentJob (nullptr), pool (p) + { + } + + void run() override + { + while (! threadShouldExit()) + if (! pool.runNextJob (*this)) + wait (500); + } + + ThreadPoolJob* volatile currentJob; + ThreadPool& pool; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread) +}; + +//============================================================================== ThreadPoolJob::ThreadPoolJob (const String& name) - : jobName (name), - pool (nullptr), - shouldStop (false), - isActive (false), - shouldBeDeleted (false) + : jobName (name), pool (nullptr), + shouldStop (false), isActive (false), shouldBeDeleted (false) { } @@ -54,30 +76,13 @@ void ThreadPoolJob::signalJobShouldExit() shouldStop = true; } -//============================================================================== -class ThreadPool::ThreadPoolThread : public Thread +ThreadPoolJob* ThreadPoolJob::getCurrentThreadPoolJob() { -public: - ThreadPoolThread (ThreadPool& pool_) - : Thread ("Pool"), - pool (pool_) - { - } + if (ThreadPool::ThreadPoolThread* t = dynamic_cast (Thread::getCurrentThread())) + return t->currentJob; - void run() - { - while (! threadShouldExit()) - { - if (! pool.runNextJob()) - wait (500); - } - } - -private: - ThreadPool& pool; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolThread); -}; + return nullptr; +} //============================================================================== ThreadPool::ThreadPool (const int numThreads) @@ -161,8 +166,7 @@ bool ThreadPool::isJobRunning (const ThreadPoolJob* const job) const return jobs.contains (const_cast (job)) && job->isActive; } -bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, - const int timeOutMs) const +bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, const int timeOutMs) const { if (job != nullptr) { @@ -170,7 +174,7 @@ bool ThreadPool::waitForJobToFinish (const ThreadPoolJob* const job, while (contains (job)) { - if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs) + if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs) return false; jobFinishedSignal.wait (2); @@ -202,7 +206,7 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job, } else { - jobs.removeValue (job); + jobs.removeFirstMatchingValue (job); addToDeleteList (deletionList, job); } } @@ -212,7 +216,7 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job, } bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeOutMs, - ThreadPool::JobSelector* selectedJobsToRemove) + ThreadPool::JobSelector* const selectedJobsToRemove) { Array jobsToWaitFor; @@ -260,7 +264,7 @@ bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, const int timeO if (jobsToWaitFor.size() == 0) break; - if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + timeOutMs) + if (timeOutMs >= 0 && Time::getMillisecondCounter() >= start + (uint32) timeOutMs) return false; jobFinishedSignal.wait (20); @@ -325,46 +329,49 @@ ThreadPoolJob* ThreadPool::pickNextJobToRun() return nullptr; } -bool ThreadPool::runNextJob() +bool ThreadPool::runNextJob (ThreadPoolThread& thread) { - ThreadPoolJob* const job = pickNextJobToRun(); - - if (job == nullptr) - return false; - - ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished; - - JUCE_TRY + if (ThreadPoolJob* const job = pickNextJobToRun()) { - result = job->runJob(); - } - JUCE_CATCH_ALL_ASSERT + ThreadPoolJob::JobStatus result = ThreadPoolJob::jobHasFinished; + thread.currentJob = job; - OwnedArray deletionList; - - { - const ScopedLock sl (lock); - - if (jobs.contains (job)) + JUCE_TRY { - job->isActive = false; + result = job->runJob(); + } + JUCE_CATCH_ALL_ASSERT - if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop) - { - jobs.removeValue (job); - addToDeleteList (deletionList, job); + thread.currentJob = nullptr; - jobFinishedSignal.signal(); - } - else + OwnedArray deletionList; + + { + const ScopedLock sl (lock); + + if (jobs.contains (job)) { - // move the job to the end of the queue if it wants another go - jobs.move (jobs.indexOf (job), -1); + job->isActive = false; + + if (result != ThreadPoolJob::jobNeedsRunningAgain || job->shouldStop) + { + jobs.removeFirstMatchingValue (job); + addToDeleteList (deletionList, job); + + jobFinishedSignal.signal(); + } + else + { + // move the job to the end of the queue if it wants another go + jobs.move (jobs.indexOf (job), -1); + } } } + + return true; } - return true; + return false; } void ThreadPool::addToDeleteList (OwnedArray& deletionList, ThreadPoolJob* const job) const diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h index 1e6744f..3d5f762 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h @@ -1,35 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_THREADPOOL_JUCEHEADER__ -#define __JUCE_THREADPOOL_JUCEHEADER__ +#ifndef JUCE_THREADPOOL_H_INCLUDED +#define JUCE_THREADPOOL_H_INCLUDED -#include "juce_Thread.h" -#include "../text/juce_StringArray.h" -#include "../containers/juce_Array.h" -#include "../containers/juce_OwnedArray.h" class ThreadPool; class ThreadPoolThread; @@ -120,6 +119,12 @@ public: */ void signalJobShouldExit(); + //============================================================================== + /** If the calling thread is being invoked inside a runJob() method, this will + return the ThreadPoolJob that it belongs to. + */ + static ThreadPoolJob* getCurrentThreadPoolJob(); + //============================================================================== private: friend class ThreadPool; @@ -128,7 +133,7 @@ private: ThreadPool* pool; bool shouldStop, isActive, shouldBeDeleted; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolJob); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPoolJob) }; @@ -136,7 +141,7 @@ private: /** 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 + When a ThreadPoolJob object is added to the ThreadPool's list, its runJob() method will be called by the next pooled thread that becomes free. @see ThreadPoolJob, Thread @@ -211,7 +216,7 @@ public: 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 + left in the pool and this will return false. It returns true if the job is successfully stopped and removed. @param job the job to remove @@ -248,7 +253,7 @@ public: /** 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. + around in the list, and this method may return nullptr if the index is currently out-of-range. */ ThreadPoolJob* getJob (int index) const; @@ -291,14 +296,15 @@ private: Array jobs; class ThreadPoolThread; + friend class ThreadPoolJob; friend class ThreadPoolThread; - friend class OwnedArray ; - OwnedArray threads; + friend struct ContainerDeletePolicy; + OwnedArray threads; CriticalSection lock; WaitableEvent jobFinishedSignal; - bool runNextJob(); + bool runNextJob (ThreadPoolThread&); ThreadPoolJob* pickNextJobToRun(); void addToDeleteList (OwnedArray&, ThreadPoolJob*) const; void createThreads (int numThreads); @@ -308,8 +314,8 @@ private: // whether the jobs should be deleted - see the new method for details. void removeAllJobs (bool, int, bool); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPool); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ThreadPool) }; -#endif // __JUCE_THREADPOOL_JUCEHEADER__ +#endif // JUCE_THREADPOOL_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp index c9ca624..f055a15 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp +++ b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.cpp @@ -1,30 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -TimeSliceThread::TimeSliceThread (const String& threadName) - : Thread (threadName), +TimeSliceThread::TimeSliceThread (const String& name) + : Thread (name), clientBeingCalled (nullptr) { } @@ -59,11 +62,11 @@ void TimeSliceThread::removeTimeSliceClient (TimeSliceClient* const client) const ScopedLock sl2 (callbackLock); const ScopedLock sl3 (listLock); - clients.removeValue (client); + clients.removeFirstMatchingValue (client); } else { - clients.removeValue (client); + clients.removeFirstMatchingValue (client); } } @@ -125,8 +128,7 @@ void TimeSliceThread::run() index = clients.size() > 0 ? ((index + 1) % clients.size()) : 0; - TimeSliceClient* const firstClient = getNextClient (index); - if (firstClient != nullptr) + if (TimeSliceClient* const firstClient = getNextClient (index)) nextClientTime = firstClient->nextCallTime; } @@ -154,9 +156,9 @@ void TimeSliceThread::run() const ScopedLock sl2 (listLock); if (msUntilNextCall >= 0) - clientBeingCalled->nextCallTime += RelativeTime::milliseconds (msUntilNextCall); + clientBeingCalled->nextCallTime = now + RelativeTime::milliseconds (msUntilNextCall); else - clients.removeValue (clientBeingCalled); + clients.removeFirstMatchingValue (clientBeingCalled); clientBeingCalled = nullptr; } diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h index ef99d0a..8c30567 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_TimeSliceThread.h @@ -1,34 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_TIMESLICETHREAD_JUCEHEADER__ -#define __JUCE_TIMESLICETHREAD_JUCEHEADER__ +#ifndef JUCE_TIMESLICETHREAD_H_INCLUDED +#define JUCE_TIMESLICETHREAD_H_INCLUDED -#include "juce_Thread.h" -#include "../containers/juce_Array.h" -#include "../time/juce_Time.h" class TimeSliceThread; @@ -131,7 +131,7 @@ public: //============================================================================== #ifndef DOXYGEN - void run(); + void run() override; #endif //============================================================================== @@ -142,8 +142,8 @@ private: TimeSliceClient* getNextClient (int index) const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimeSliceThread); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimeSliceThread) }; -#endif // __JUCE_TIMESLICETHREAD_JUCEHEADER__ +#endif // JUCE_TIMESLICETHREAD_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h index 11e146f..83f6f06 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_WaitableEvent.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_WAITABLEEVENT_JUCEHEADER__ -#define __JUCE_WAITABLEEVENT_JUCEHEADER__ - -#include "../text/juce_String.h" +#ifndef JUCE_WAITABLEEVENT_H_INCLUDED +#define JUCE_WAITABLEEVENT_H_INCLUDED //============================================================================== @@ -43,11 +44,13 @@ public: //============================================================================== /** Creates a WaitableEvent object. + The object is initially in an unsignalled state. + @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) noexcept; + explicit WaitableEvent (bool manualReset = false) noexcept; /** Destructor. @@ -93,7 +96,6 @@ public: //============================================================================== /** Resets the event to an unsignalled state. - If it's not already signalled, this does nothing. */ void reset() const noexcept; @@ -101,10 +103,16 @@ public: private: //============================================================================== - void* internal; + #if JUCE_WINDOWS + void* handle; + #else + mutable pthread_cond_t condition; + mutable pthread_mutex_t mutex; + mutable bool triggered, manualReset; + #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaitableEvent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaitableEvent) }; -#endif // __JUCE_WAITABLEEVENT_JUCEHEADER__ +#endif // JUCE_WAITABLEEVENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp index 80ac658..b22d5e4 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp @@ -1,92 +1,132 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -PerformanceCounter::PerformanceCounter (const String& name_, - const int runsPerPrintout, - const File& loggingFile) - : name (name_), - numRuns (0), - runsPerPrint (runsPerPrintout), - totalTime (0), - outputFile (loggingFile) +static void appendToFile (const File& f, const String& s) { - if (outputFile != File::nonexistent) + if (f.getFullPathName().isNotEmpty()) { - String s ("**** Counter for \""); - s << name_ << "\" started at: " - << Time::getCurrentTime().toString (true, true) - << newLine; + FileOutputStream out (f); - outputFile.appendText (s, false, false); + if (! out.failedToOpen()) + out << s << newLine; } } +PerformanceCounter::PerformanceCounter (const String& name, int runsPerPrintout, const File& loggingFile) + : runsPerPrint (runsPerPrintout), startTime (0), outputFile (loggingFile) +{ + stats.name = name; + appendToFile (outputFile, "**** Counter for \"" + name + "\" started at: " + Time::getCurrentTime().toString (true, true)); +} + PerformanceCounter::~PerformanceCounter() { printStatistics(); } -void PerformanceCounter::start() +PerformanceCounter::Statistics::Statistics() noexcept + : averageSeconds(), maximumSeconds(), minimumSeconds(), totalSeconds(), numRuns() { - started = Time::getHighResolutionTicks(); } -void PerformanceCounter::stop() +void PerformanceCounter::Statistics::clear() noexcept { - const int64 now = Time::getHighResolutionTicks(); + averageSeconds = maximumSeconds = minimumSeconds = totalSeconds = 0; + numRuns = 0; +} - totalTime += 1000.0 * Time::highResolutionTicksToSeconds (now - started); +void PerformanceCounter::Statistics::addResult (double elapsed) noexcept +{ + if (numRuns == 0) + { + maximumSeconds = elapsed; + minimumSeconds = elapsed; + } + else + { + maximumSeconds = jmax (maximumSeconds, elapsed); + minimumSeconds = jmin (minimumSeconds, elapsed); + } - if (++numRuns == runsPerPrint) - printStatistics(); + ++numRuns; + totalSeconds += elapsed; +} + +static String timeToString (double secs) +{ + return String ((int64) (secs * (secs < 0.01 ? 1000000.0 : 1000.0) + 0.5)) + + (secs < 0.01 ? " microsecs" : " millisecs"); +} + +String PerformanceCounter::Statistics::toString() const +{ + MemoryOutputStream s; + + s << "Performance count for \"" << name << "\" over " << numRuns << " run(s)" << newLine + << "Average = " << timeToString (averageSeconds) + << ", minimum = " << timeToString (minimumSeconds) + << ", maximum = " << timeToString (maximumSeconds) + << ", total = " << timeToString (totalSeconds); + + return s.toString(); +} + +void PerformanceCounter::start() noexcept +{ + startTime = Time::getHighResolutionTicks(); +} + +bool PerformanceCounter::stop() +{ + stats.addResult (Time::highResolutionTicksToSeconds (Time::getHighResolutionTicks() - startTime)); + + if (stats.numRuns < runsPerPrint) + return false; + + printStatistics(); + return true; } void PerformanceCounter::printStatistics() { - if (numRuns > 0) - { - String s ("Performance count for \""); - s << name << "\" - average over " << numRuns << " run(s) = "; + const String desc (getStatisticsAndReset().toString()); - const int micros = (int) (totalTime * (1000.0 / numRuns)); - - if (micros > 10000) - s << (micros/1000) << " millisecs"; - else - s << micros << " microsecs"; - - s << ", total = " << String (totalTime / 1000, 5) << " seconds"; - - Logger::outputDebugString (s); - - s << newLine; - - if (outputFile != File::nonexistent) - outputFile.appendText (s, false, false); - - numRuns = 0; - totalTime = 0; - } + Logger::outputDebugString (desc); + appendToFile (outputFile, desc); +} + +PerformanceCounter::Statistics PerformanceCounter::getStatisticsAndReset() +{ + Statistics s (stats); + stats.clear(); + + if (s.numRuns > 0) + s.averageSeconds = s.totalSeconds / s.numRuns; + + return s; } diff --git a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h index 3e02229..aac50d2 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ -#define __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ - -#include "../files/juce_File.h" +#ifndef JUCE_PERFORMANCECOUNTER_H_INCLUDED +#define JUCE_PERFORMANCECOUNTER_H_INCLUDED //============================================================================== @@ -64,17 +65,16 @@ public: */ PerformanceCounter (const String& counterName, int runsPerPrintout = 100, - const File& loggingFile = File::nonexistent); + const File& loggingFile = File()); /** Destructor. */ ~PerformanceCounter(); //============================================================================== /** Starts timing. - @see stop */ - void start(); + void start() noexcept; /** Stops timing and prints out the results. @@ -83,7 +83,7 @@ public: @see start */ - void stop(); + bool stop(); /** Dumps the current metrics to the debugger output and to a file. @@ -93,13 +93,35 @@ public: */ void printStatistics(); + /** Holds the current statistics. */ + struct Statistics + { + Statistics() noexcept; + + void clear() noexcept; + String toString() const; + + void addResult (double elapsed) noexcept; + + String name; + double averageSeconds; + double maximumSeconds; + double minimumSeconds; + double totalSeconds; + int64 numRuns; + }; + + /** Returns a copy of the current stats, and resets the internal counter. */ + Statistics getStatisticsAndReset(); + private: //============================================================================== - String name; - int numRuns, runsPerPrint; - double totalTime; - int64 started; + Statistics stats; + int64 runsPerPrint, startTime; File outputFile; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PerformanceCounter) }; -#endif // __JUCE_PERFORMANCECOUNTER_JUCEHEADER__ + +#endif // JUCE_PERFORMANCECOUNTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp index 378ad4e..6fca4fe 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.cpp @@ -1,83 +1,100 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -RelativeTime::RelativeTime (const double seconds_) noexcept - : seconds (seconds_) -{ -} - -RelativeTime::RelativeTime (const RelativeTime& other) noexcept - : seconds (other.seconds) -{ -} - -RelativeTime::~RelativeTime() noexcept -{ -} +RelativeTime::RelativeTime (const double secs) noexcept : numSeconds (secs) {} +RelativeTime::RelativeTime (const RelativeTime& other) noexcept : numSeconds (other.numSeconds) {} +RelativeTime::~RelativeTime() noexcept {} //============================================================================== -const RelativeTime RelativeTime::milliseconds (const int milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } -const RelativeTime RelativeTime::milliseconds (const int64 milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } -const RelativeTime RelativeTime::minutes (const double numberOfMinutes) noexcept { return RelativeTime (numberOfMinutes * 60.0); } -const RelativeTime RelativeTime::hours (const double numberOfHours) noexcept { return RelativeTime (numberOfHours * (60.0 * 60.0)); } -const RelativeTime RelativeTime::days (const double numberOfDays) noexcept { return RelativeTime (numberOfDays * (60.0 * 60.0 * 24.0)); } -const RelativeTime RelativeTime::weeks (const double numberOfWeeks) noexcept { return RelativeTime (numberOfWeeks * (60.0 * 60.0 * 24.0 * 7.0)); } +RelativeTime RelativeTime::milliseconds (const int milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } +RelativeTime RelativeTime::milliseconds (const int64 milliseconds) noexcept { return RelativeTime (milliseconds * 0.001); } +RelativeTime RelativeTime::seconds (double s) noexcept { return RelativeTime (s); } +RelativeTime RelativeTime::minutes (const double numberOfMinutes) noexcept { return RelativeTime (numberOfMinutes * 60.0); } +RelativeTime RelativeTime::hours (const double numberOfHours) noexcept { return RelativeTime (numberOfHours * (60.0 * 60.0)); } +RelativeTime RelativeTime::days (const double numberOfDays) noexcept { return RelativeTime (numberOfDays * (60.0 * 60.0 * 24.0)); } +RelativeTime RelativeTime::weeks (const double numberOfWeeks) noexcept { return RelativeTime (numberOfWeeks * (60.0 * 60.0 * 24.0 * 7.0)); } //============================================================================== -int64 RelativeTime::inMilliseconds() const noexcept { return (int64) (seconds * 1000.0); } -double RelativeTime::inMinutes() const noexcept { return seconds / 60.0; } -double RelativeTime::inHours() const noexcept { return seconds / (60.0 * 60.0); } -double RelativeTime::inDays() const noexcept { return seconds / (60.0 * 60.0 * 24.0); } -double RelativeTime::inWeeks() const noexcept { return seconds / (60.0 * 60.0 * 24.0 * 7.0); } +int64 RelativeTime::inMilliseconds() const noexcept { return (int64) (numSeconds * 1000.0); } +double RelativeTime::inMinutes() const noexcept { return numSeconds / 60.0; } +double RelativeTime::inHours() const noexcept { return numSeconds / (60.0 * 60.0); } +double RelativeTime::inDays() const noexcept { return numSeconds / (60.0 * 60.0 * 24.0); } +double RelativeTime::inWeeks() const noexcept { return numSeconds / (60.0 * 60.0 * 24.0 * 7.0); } //============================================================================== +RelativeTime& RelativeTime::operator= (const RelativeTime& other) noexcept { numSeconds = other.numSeconds; return *this; } + +RelativeTime RelativeTime::operator+= (RelativeTime t) noexcept { numSeconds += t.numSeconds; return *this; } +RelativeTime RelativeTime::operator-= (RelativeTime t) noexcept { numSeconds -= t.numSeconds; return *this; } +RelativeTime RelativeTime::operator+= (const double secs) noexcept { numSeconds += secs; return *this; } +RelativeTime RelativeTime::operator-= (const double secs) noexcept { numSeconds -= secs; return *this; } + +RelativeTime operator+ (RelativeTime t1, RelativeTime t2) noexcept { return t1 += t2; } +RelativeTime operator- (RelativeTime t1, RelativeTime t2) noexcept { return t1 -= t2; } + +bool operator== (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() == t2.inSeconds(); } +bool operator!= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() != t2.inSeconds(); } +bool operator> (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() > t2.inSeconds(); } +bool operator< (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() < t2.inSeconds(); } +bool operator>= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() >= t2.inSeconds(); } +bool operator<= (RelativeTime t1, RelativeTime t2) noexcept { return t1.inSeconds() <= t2.inSeconds(); } + +//============================================================================== +static void translateTimeField (String& result, int n, const char* singular, const char* plural) +{ + result << TRANS (n == 1 ? singular : plural) + .replace (n == 1 ? "1" : "2", String (n)) + << ' '; +} + String RelativeTime::getDescription (const String& returnValueForZeroTime) const { - if (seconds < 0.001 && seconds > -0.001) + if (numSeconds < 0.001 && numSeconds > -0.001) return returnValueForZeroTime; String result; result.preallocateBytes (32); - if (seconds < 0) + if (numSeconds < 0) result << '-'; int fieldsShown = 0; int n = std::abs ((int) inWeeks()); if (n > 0) { - result << n << (n == 1 ? TRANS(" week ") - : TRANS(" weeks ")); + translateTimeField (result, n, NEEDS_TRANS("1 week"), NEEDS_TRANS("2 weeks")); ++fieldsShown; } n = std::abs ((int) inDays()) % 7; if (n > 0) { - result << n << (n == 1 ? TRANS(" day ") - : TRANS(" days ")); + translateTimeField (result, n, NEEDS_TRANS("1 day"), NEEDS_TRANS("2 days")); ++fieldsShown; } @@ -86,8 +103,7 @@ String RelativeTime::getDescription (const String& returnValueForZeroTime) const n = std::abs ((int) inHours()) % 24; if (n > 0) { - result << n << (n == 1 ? TRANS(" hr ") - : TRANS(" hrs ")); + translateTimeField (result, n, NEEDS_TRANS("1 hr"), NEEDS_TRANS("2 hrs")); ++fieldsShown; } @@ -96,8 +112,7 @@ String RelativeTime::getDescription (const String& returnValueForZeroTime) const n = std::abs ((int) inMinutes()) % 60; if (n > 0) { - result << n << (n == 1 ? TRANS(" min ") - : TRANS(" mins ")); + translateTimeField (result, n, NEEDS_TRANS("1 min"), NEEDS_TRANS("2 mins")); ++fieldsShown; } @@ -106,8 +121,7 @@ String RelativeTime::getDescription (const String& returnValueForZeroTime) const n = std::abs ((int) inSeconds()) % 60; if (n > 0) { - result << n << (n == 1 ? TRANS(" sec ") - : TRANS(" secs ")); + translateTimeField (result, n, NEEDS_TRANS("1 sec"), NEEDS_TRANS("2 secs")); ++fieldsShown; } @@ -115,7 +129,7 @@ String RelativeTime::getDescription (const String& returnValueForZeroTime) const { n = std::abs ((int) inMilliseconds()) % 1000; if (n > 0) - result << n << TRANS(" ms"); + result << n << ' ' << TRANS ("ms"); } } } @@ -123,45 +137,3 @@ String RelativeTime::getDescription (const String& returnValueForZeroTime) const return result.trimEnd(); } - -//============================================================================== -RelativeTime& RelativeTime::operator= (const RelativeTime& other) noexcept -{ - seconds = other.seconds; - return *this; -} - -//============================================================================== -const RelativeTime& RelativeTime::operator+= (const RelativeTime& timeToAdd) noexcept -{ - seconds += timeToAdd.seconds; - return *this; -} - -const RelativeTime& RelativeTime::operator-= (const RelativeTime& timeToSubtract) noexcept -{ - seconds -= timeToSubtract.seconds; - return *this; -} - -const RelativeTime& RelativeTime::operator+= (const double secondsToAdd) noexcept -{ - seconds += secondsToAdd; - return *this; -} - -const RelativeTime& RelativeTime::operator-= (const double secondsToSubtract) noexcept -{ - seconds -= secondsToSubtract; - return *this; -} - -bool operator== (const RelativeTime& t1, const RelativeTime& t2) noexcept { return t1.inSeconds() == t2.inSeconds(); } -bool operator!= (const RelativeTime& t1, const RelativeTime& t2) noexcept { return t1.inSeconds() != t2.inSeconds(); } -bool operator> (const RelativeTime& t1, const RelativeTime& t2) noexcept { return t1.inSeconds() > t2.inSeconds(); } -bool operator< (const RelativeTime& t1, const RelativeTime& t2) noexcept { return t1.inSeconds() < t2.inSeconds(); } -bool operator>= (const RelativeTime& t1, const RelativeTime& t2) noexcept { return t1.inSeconds() >= t2.inSeconds(); } -bool operator<= (const RelativeTime& t1, const RelativeTime& t2) noexcept { return t1.inSeconds() <= t2.inSeconds(); } - -RelativeTime operator+ (const RelativeTime& t1, const RelativeTime& t2) noexcept { RelativeTime t (t1); return t += t2; } -RelativeTime operator- (const RelativeTime& t1, const RelativeTime& t2) noexcept { RelativeTime t (t1); return t -= t2; } diff --git a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h index 21d86bf..7e39d21 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_RelativeTime.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_RELATIVETIME_JUCEHEADER__ -#define __JUCE_RELATIVETIME_JUCEHEADER__ - -#include "../text/juce_String.h" +#ifndef JUCE_RELATIVETIME_H_INCLUDED +#define JUCE_RELATIVETIME_H_INCLUDED //============================================================================== @@ -59,34 +60,39 @@ public: //============================================================================== /** Creates a new RelativeTime object representing a number of milliseconds. - @see minutes, hours, days, weeks + @see seconds, minutes, hours, days, weeks */ - static const RelativeTime milliseconds (int milliseconds) noexcept; + static RelativeTime milliseconds (int milliseconds) noexcept; /** Creates a new RelativeTime object representing a number of milliseconds. - @see minutes, hours, days, weeks + @see seconds, minutes, hours, days, weeks */ - static const RelativeTime milliseconds (int64 milliseconds) noexcept; + static RelativeTime milliseconds (int64 milliseconds) noexcept; + + /** Creates a new RelativeTime object representing a number of seconds. + @see milliseconds, minutes, hours, days, weeks + */ + static RelativeTime seconds (double seconds) noexcept; /** Creates a new RelativeTime object representing a number of minutes. @see milliseconds, hours, days, weeks */ - static const RelativeTime minutes (double numberOfMinutes) noexcept; + static RelativeTime minutes (double numberOfMinutes) noexcept; /** Creates a new RelativeTime object representing a number of hours. @see milliseconds, minutes, days, weeks */ - static const RelativeTime hours (double numberOfHours) noexcept; + static RelativeTime hours (double numberOfHours) noexcept; /** Creates a new RelativeTime object representing a number of days. @see milliseconds, minutes, hours, weeks */ - static const RelativeTime days (double numberOfDays) noexcept; + static RelativeTime days (double numberOfDays) noexcept; /** Creates a new RelativeTime object representing a number of weeks. @see milliseconds, minutes, hours, days */ - static const RelativeTime weeks (double numberOfWeeks) noexcept; + static RelativeTime weeks (double numberOfWeeks) noexcept; //============================================================================== /** Returns the number of milliseconds this time represents. @@ -97,7 +103,7 @@ public: /** Returns the number of seconds this time represents. @see inMilliseconds, inMinutes, inHours, inDays, inWeeks */ - double inSeconds() const noexcept { return seconds; } + double inSeconds() const noexcept { return numSeconds; } /** Returns the number of minutes this time represents. @see inMilliseconds, inSeconds, inHours, inDays, inWeeks @@ -139,40 +145,40 @@ public: //============================================================================== /** Adds another RelativeTime to this one. */ - const RelativeTime& operator+= (const RelativeTime& timeToAdd) noexcept; + RelativeTime operator+= (RelativeTime timeToAdd) noexcept; /** Subtracts another RelativeTime from this one. */ - const RelativeTime& operator-= (const RelativeTime& timeToSubtract) noexcept; + RelativeTime operator-= (RelativeTime timeToSubtract) noexcept; /** Adds a number of seconds to this time. */ - const RelativeTime& operator+= (double secondsToAdd) noexcept; + RelativeTime operator+= (double secondsToAdd) noexcept; /** Subtracts a number of seconds from this time. */ - const RelativeTime& operator-= (double secondsToSubtract) noexcept; + RelativeTime operator-= (double secondsToSubtract) noexcept; private: //============================================================================== - double seconds; + double numSeconds; }; //============================================================================== /** Compares two RelativeTimes. */ -bool operator== (const RelativeTime& t1, const RelativeTime& t2) noexcept; +bool operator== (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ -bool operator!= (const RelativeTime& t1, const RelativeTime& t2) noexcept; +bool operator!= (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ -bool operator> (const RelativeTime& t1, const RelativeTime& t2) noexcept; +bool operator> (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ -bool operator< (const RelativeTime& t1, const RelativeTime& t2) noexcept; +bool operator< (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ -bool operator>= (const RelativeTime& t1, const RelativeTime& t2) noexcept; +bool operator>= (RelativeTime t1, RelativeTime t2) noexcept; /** Compares two RelativeTimes. */ -bool operator<= (const RelativeTime& t1, const RelativeTime& t2) noexcept; +bool operator<= (RelativeTime t1, RelativeTime t2) noexcept; //============================================================================== /** Adds two RelativeTimes together. */ -RelativeTime operator+ (const RelativeTime& t1, const RelativeTime& t2) noexcept; +RelativeTime operator+ (RelativeTime t1, RelativeTime t2) noexcept; /** Subtracts two RelativeTimes. */ -RelativeTime operator- (const RelativeTime& t1, const RelativeTime& t2) noexcept; +RelativeTime operator- (RelativeTime t1, RelativeTime t2) noexcept; -#endif // __JUCE_RELATIVETIME_JUCEHEADER__ +#endif // JUCE_RELATIVETIME_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp b/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp index 408d38d..959275b 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_Time.cpp @@ -1,42 +1,31 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4514) -#endif - -#if JUCE_MSVC - #pragma warning (pop) - - #ifdef _INC_TIME_INL - #define USE_NEW_SECURE_TIME_FNS - #endif -#endif - -//============================================================================== namespace TimeHelpers { static struct tm millisToLocal (const int64 millis) noexcept @@ -44,13 +33,13 @@ namespace TimeHelpers struct tm result; const int64 seconds = millis / 1000; - if (seconds < literal64bit (86400) || seconds >= literal64bit (2145916800)) + if (seconds < 86400LL || seconds >= 2145916800LL) { // use extended maths for dates beyond 1970 to 2037.. const int timeZoneAdjustment = 31536000 - (int) (Time (1971, 0, 1, 0, 0).toMilliseconds() / 1000); - const int64 jdm = seconds + timeZoneAdjustment + literal64bit (210866803200); + const int64 jdm = seconds + timeZoneAdjustment + 210866803200LL; - const int days = (int) (jdm / literal64bit (86400)); + const int days = (int) (jdm / 86400LL); const int a = 32044 + days; const int b = (4 * a + 3) / 146097; const int c = a - (b * 146097) / 4; @@ -64,7 +53,7 @@ namespace TimeHelpers result.tm_wday = (days + 1) % 7; result.tm_yday = -1; - int t = (int) (jdm % literal64bit (86400)); + int t = (int) (jdm % 86400LL); result.tm_hour = t / 3600; t %= 3600; result.tm_min = t / 60; @@ -76,7 +65,7 @@ namespace TimeHelpers time_t now = static_cast (seconds); #if JUCE_WINDOWS - #ifdef USE_NEW_SECURE_TIME_FNS + #ifdef _INC_TIME_INL if (now >= 0 && now <= 0x793406fff) localtime_s (&result, &now); else @@ -99,26 +88,32 @@ namespace TimeHelpers : (value - ((value / modulo) + 1) * modulo)); } - static int doFTime (CharPointer_UTF32 dest, const size_t maxChars, - const String& format, const struct tm* const tm) noexcept + static inline String formatString (const String& format, const struct tm* const tm) { #if JUCE_ANDROID - HeapBlock tempDest; - tempDest.calloc (maxChars + 2); - const int result = (int) strftime (tempDest, maxChars, format.toUTF8(), tm); - if (result > 0) - dest.writeAll (CharPointer_UTF8 (tempDest.getData())); - return result; + typedef CharPointer_UTF8 StringType; #elif JUCE_WINDOWS - HeapBlock tempDest; - tempDest.calloc (maxChars + 2); - const int result = (int) wcsftime (tempDest, maxChars, format.toWideCharPointer(), tm); - if (result > 0) - dest.writeAll (CharPointer_UTF16 (tempDest.getData())); - return result; + typedef CharPointer_UTF16 StringType; #else - return (int) wcsftime (dest.getAddress(), maxChars, format.toUTF32(), tm); + typedef CharPointer_UTF32 StringType; #endif + + for (size_t bufferSize = 256; ; bufferSize += 256) + { + HeapBlock buffer (bufferSize); + + #if JUCE_ANDROID + const size_t numChars = strftime (buffer, bufferSize - 1, format.toUTF8(), tm); + #elif JUCE_WINDOWS + const size_t numChars = wcsftime (buffer, bufferSize - 1, format.toWideCharPointer(), tm); + #else + const size_t numChars = wcsftime (buffer, bufferSize - 1, format.toUTF32(), tm); + #endif + + if (numChars > 0 || format.isEmpty()) + return String (StringType (buffer), + StringType (buffer) + (int) numChars); + } } static uint32 lastMSCounterValue = 0; @@ -162,7 +157,7 @@ Time::Time (const int year, + (y * 365) + (y / 4) - (y / 100) + (y / 400) - 32045; - const int64 s = ((int64) jd) * literal64bit (86400) - literal64bit (210866803200); + const int64 s = ((int64) jd) * 86400LL - 210866803200LL; millisSinceEpoch = 1000 * (s + (hours * 3600 + minutes * 60 + seconds - timeZoneAdjustment)) + milliseconds; @@ -200,39 +195,24 @@ Time& Time::operator= (const Time& other) noexcept //============================================================================== int64 Time::currentTimeMillis() noexcept { - static uint32 lastCounterResult = 0xffffffff; - static int64 correction = 0; + #if JUCE_WINDOWS + struct _timeb t; + #ifdef _INC_TIME_INL + _ftime_s (&t); + #else + _ftime (&t); + #endif + return ((int64) t.time) * 1000 + t.millitm; + #else + struct timeval tv; + gettimeofday (&tv, nullptr); + return ((int64) tv.tv_sec) * 1000 + tv.tv_usec / 1000; + #endif +} - const uint32 now = getMillisecondCounter(); - - // check the counter hasn't wrapped (also triggered the first time this function is called) - if (now < lastCounterResult) - { - // double-check it's actually wrapped, in case multi-cpu machines have timers that drift a bit. - if (lastCounterResult == 0xffffffff || now < lastCounterResult - 10) - { - // get the time once using normal library calls, and store the difference needed to - // turn the millisecond counter into a real time. - #if JUCE_WINDOWS - struct _timeb t; - #ifdef USE_NEW_SECURE_TIME_FNS - _ftime_s (&t); - #else - _ftime (&t); - #endif - correction = (((int64) t.time) * 1000 + t.millitm) - now; - #else - struct timeval tv; - struct timezone tz; - gettimeofday (&tv, &tz); - correction = (((int64) tv.tv_sec) * 1000 + tv.tv_usec / 1000) - now; - #endif - } - } - - lastCounterResult = now; - - return correction + now; +Time JUCE_CALLTYPE Time::getCurrentTime() noexcept +{ + return Time (currentTimeMillis()); } //============================================================================== @@ -302,13 +282,6 @@ int64 Time::secondsToHighResolutionTicks (const double seconds) noexcept return (int64) (seconds * (double) getHighResolutionTicksPerSecond()); } - -//============================================================================== -Time JUCE_CALLTYPE Time::getCurrentTime() noexcept -{ - return Time (currentTimeMillis()); -} - //============================================================================== String Time::toString (const bool includeDate, const bool includeTime, @@ -349,18 +322,8 @@ String Time::toString (const bool includeDate, String Time::formatted (const String& format) const { - size_t bufferSize = 128; - HeapBlock buffer (128); - struct tm t (TimeHelpers::millisToLocal (millisSinceEpoch)); - - while (TimeHelpers::doFTime (CharPointer_UTF32 (buffer.getData()), bufferSize, format, &t) <= 0) - { - bufferSize += 128; - buffer.malloc (bufferSize); - } - - return CharPointer_UTF32 (buffer.getData()); + return TimeHelpers::formatString (format, &t); } //============================================================================== @@ -378,12 +341,10 @@ int Time::getHoursInAmPmFormat() const noexcept { const int hours = getHours(); - if (hours == 0) - return 12; - else if (hours <= 12) - return hours; - else - return hours - 12; + if (hours == 0) return 12; + if (hours <= 12) return hours; + + return hours - 12; } bool Time::isAfternoon() const noexcept @@ -403,7 +364,7 @@ String Time::getTimeZone() const noexcept #if JUCE_WINDOWS _tzset(); - #ifdef USE_NEW_SECURE_TIME_FNS + #ifdef _INC_TIME_INL for (int i = 0; i < 2; ++i) { char name[128] = { 0 }; @@ -448,8 +409,8 @@ String Time::getWeekdayName (const bool threeLetterVersion) const String Time::getMonthName (int monthNumber, const bool threeLetterVersion) { - const char* const shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - const char* const longMonthNames[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + static const char* const shortMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static const char* const longMonthNames[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; monthNumber %= 12; @@ -459,8 +420,8 @@ String Time::getMonthName (int monthNumber, const bool threeLetterVersion) String Time::getWeekdayName (int day, const bool threeLetterVersion) { - const char* const shortDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - const char* const longDayNames[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + static const char* const shortDayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char* const longDayNames[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; day %= 7; @@ -469,17 +430,17 @@ String Time::getWeekdayName (int day, const bool threeLetterVersion) } //============================================================================== -Time& Time::operator+= (const RelativeTime& delta) { millisSinceEpoch += delta.inMilliseconds(); return *this; } -Time& Time::operator-= (const RelativeTime& delta) { millisSinceEpoch -= delta.inMilliseconds(); return *this; } +Time& Time::operator+= (RelativeTime delta) { millisSinceEpoch += delta.inMilliseconds(); return *this; } +Time& Time::operator-= (RelativeTime delta) { millisSinceEpoch -= delta.inMilliseconds(); return *this; } -Time operator+ (const Time& time, const RelativeTime& delta) { Time t (time); return t += delta; } -Time operator- (const Time& time, const RelativeTime& delta) { Time t (time); return t -= delta; } -Time operator+ (const RelativeTime& delta, const Time& time) { Time t (time); return t += delta; } -const RelativeTime operator- (const Time& time1, const Time& time2) { return RelativeTime::milliseconds (time1.toMilliseconds() - time2.toMilliseconds()); } +Time operator+ (Time time, RelativeTime delta) { Time t (time); return t += delta; } +Time operator- (Time time, RelativeTime delta) { Time t (time); return t -= delta; } +Time operator+ (RelativeTime delta, Time time) { Time t (time); return t += delta; } +const RelativeTime operator- (Time time1, Time time2) { return RelativeTime::milliseconds (time1.toMilliseconds() - time2.toMilliseconds()); } -bool operator== (const Time& time1, const Time& time2) { return time1.toMilliseconds() == time2.toMilliseconds(); } -bool operator!= (const Time& time1, const Time& time2) { return time1.toMilliseconds() != time2.toMilliseconds(); } -bool operator< (const Time& time1, const Time& time2) { return time1.toMilliseconds() < time2.toMilliseconds(); } -bool operator> (const Time& time1, const Time& time2) { return time1.toMilliseconds() > time2.toMilliseconds(); } -bool operator<= (const Time& time1, const Time& time2) { return time1.toMilliseconds() <= time2.toMilliseconds(); } -bool operator>= (const Time& time1, const Time& time2) { return time1.toMilliseconds() >= time2.toMilliseconds(); } +bool operator== (Time time1, Time time2) { return time1.toMilliseconds() == time2.toMilliseconds(); } +bool operator!= (Time time1, Time time2) { return time1.toMilliseconds() != time2.toMilliseconds(); } +bool operator< (Time time1, Time time2) { return time1.toMilliseconds() < time2.toMilliseconds(); } +bool operator> (Time time1, Time time2) { return time1.toMilliseconds() > time2.toMilliseconds(); } +bool operator<= (Time time1, Time time2) { return time1.toMilliseconds() <= time2.toMilliseconds(); } +bool operator>= (Time time1, Time time2) { return time1.toMilliseconds() >= time2.toMilliseconds(); } diff --git a/JuceLibraryCode/modules/juce_core/time/juce_Time.h b/JuceLibraryCode/modules/juce_core/time/juce_Time.h index a56c421..f72fb2c 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_Time.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_Time.h @@ -1,32 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_TIME_JUCEHEADER__ -#define __JUCE_TIME_JUCEHEADER__ - -#include "juce_RelativeTime.h" +#ifndef JUCE_TIME_H_INCLUDED +#define JUCE_TIME_H_INCLUDED //============================================================================== @@ -252,9 +253,9 @@ public: //============================================================================== /** Adds a RelativeTime to this time. */ - Time& operator+= (const RelativeTime& delta); + Time& operator+= (RelativeTime delta); /** Subtracts a RelativeTime from this time. */ - Time& operator-= (const RelativeTime& delta); + Time& operator-= (RelativeTime delta); //============================================================================== /** Tries to set the computer's clock. @@ -377,27 +378,27 @@ private: //============================================================================== /** Adds a RelativeTime to a Time. */ -JUCE_API Time operator+ (const Time& time, const RelativeTime& delta); +JUCE_API Time operator+ (Time time, RelativeTime delta); /** Adds a RelativeTime to a Time. */ -JUCE_API Time operator+ (const RelativeTime& delta, const Time& time); +JUCE_API Time operator+ (RelativeTime delta, Time time); /** Subtracts a RelativeTime from a Time. */ -JUCE_API Time operator- (const Time& time, const RelativeTime& delta); +JUCE_API Time operator- (Time time, RelativeTime delta); /** Returns the relative time difference between two times. */ -JUCE_API const RelativeTime operator- (const Time& time1, const Time& time2); +JUCE_API const RelativeTime operator- (Time time1, Time time2); /** Compares two Time objects. */ -JUCE_API bool operator== (const Time& time1, const Time& time2); +JUCE_API bool operator== (Time time1, Time time2); /** Compares two Time objects. */ -JUCE_API bool operator!= (const Time& time1, const Time& time2); +JUCE_API bool operator!= (Time time1, Time time2); /** Compares two Time objects. */ -JUCE_API bool operator< (const Time& time1, const Time& time2); +JUCE_API bool operator< (Time time1, Time time2); /** Compares two Time objects. */ -JUCE_API bool operator<= (const Time& time1, const Time& time2); +JUCE_API bool operator<= (Time time1, Time time2); /** Compares two Time objects. */ -JUCE_API bool operator> (const Time& time1, const Time& time2); +JUCE_API bool operator> (Time time1, Time time2); /** Compares two Time objects. */ -JUCE_API bool operator>= (const Time& time1, const Time& time2); +JUCE_API bool operator>= (Time time1, Time time2); -#endif // __JUCE_TIME_JUCEHEADER__ +#endif // JUCE_TIME_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp index d44467d..f5a60c1 100644 --- a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp +++ b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.cpp @@ -1,37 +1,40 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -UnitTest::UnitTest (const String& name_) - : name (name_), runner (nullptr) +UnitTest::UnitTest (const String& nm) + : name (nm), runner (nullptr) { getAllTests().add (this); } UnitTest::~UnitTest() { - getAllTests().removeValue (this); + getAllTests().removeFirstMatchingValue (this); } Array& UnitTest::getAllTests() @@ -43,10 +46,10 @@ Array& UnitTest::getAllTests() void UnitTest::initialise() {} void UnitTest::shutdown() {} -void UnitTest::performTest (UnitTestRunner* const runner_) +void UnitTest::performTest (UnitTestRunner* const newRunner) { - jassert (runner_ != nullptr); - runner = runner_; + jassert (newRunner != nullptr); + runner = newRunner; initialise(); runTest(); @@ -55,22 +58,39 @@ void UnitTest::performTest (UnitTestRunner* const runner_) void UnitTest::logMessage (const String& message) { + // This method's only valid while the test is being run! + jassert (runner != nullptr); + runner->logMessage (message); } void UnitTest::beginTest (const String& testName) { + // This method's only valid while the test is being run! + jassert (runner != nullptr); + runner->beginNewTest (this, testName); } void UnitTest::expect (const bool result, const String& failureMessage) { + // This method's only valid while the test is being run! + jassert (runner != nullptr); + if (result) runner->addPass(); else runner->addFail (failureMessage); } +Random UnitTest::getRandom() const +{ + // This method's only valid while the test is being run! + jassert (runner != nullptr); + + return runner->randomForTest; +} + //============================================================================== UnitTestRunner::UnitTestRunner() : currentTest (nullptr), @@ -107,11 +127,17 @@ void UnitTestRunner::resultsUpdated() { } -void UnitTestRunner::runTests (const Array& tests) +void UnitTestRunner::runTests (const Array& tests, int64 randomSeed) { results.clear(); resultsUpdated(); + if (randomSeed == 0) + randomSeed = Random().nextInt (0x7ffffff); + + randomForTest = Random (randomSeed); + logMessage ("Random seed: 0x" + String::toHexString (randomSeed)); + for (int i = 0; i < tests.size(); ++i) { if (shouldAbortTests()) @@ -130,9 +156,9 @@ void UnitTestRunner::runTests (const Array& tests) endTest(); } -void UnitTestRunner::runAllTests() +void UnitTestRunner::runAllTests (int64 randomSeed) { - runTests (UnitTest::getAllTests()); + runTests (UnitTest::getAllTests(), randomSeed); } void UnitTestRunner::logMessage (const String& message) @@ -230,5 +256,5 @@ void UnitTestRunner::addFail (const String& failureMessage) resultsUpdated(); - if (assertOnFailure) { jassertfalse } + if (assertOnFailure) { jassertfalse; } } diff --git a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h index 4bced4d..f5908c1 100644 --- a/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h +++ b/JuceLibraryCode/modules/juce_core/unit_tests/juce_UnitTest.h @@ -1,33 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_UNITTEST_JUCEHEADER__ -#define __JUCE_UNITTEST_JUCEHEADER__ +#ifndef JUCE_UNITTEST_H_INCLUDED +#define JUCE_UNITTEST_H_INCLUDED -#include "../text/juce_StringArray.h" -#include "../containers/juce_OwnedArray.h" class UnitTestRunner; @@ -162,12 +163,28 @@ public: */ void logMessage (const String& message); + /** Returns a shared RNG that all unit tests should use. + If a test needs random numbers, it's important that when an error is found, the + exact circumstances can be re-created in order to re-test the problem, by + repeating the test with the same random seed value. + To make this possible, the UnitTestRunner class creates a master seed value + for the run, writes this number to the log, and then this method returns a + Random object based on that seed. All tests should only use this method to + create any Random objects that they need. + + Note that this method will return an identical object each time it's called + for a given run, so if you need several different Random objects, the best + way to do that is to call Random::combineSeed() on the result to permute it + with a constant value. + */ + Random getRandom() const; + private: //============================================================================== const String name; UnitTestRunner* runner; - JUCE_DECLARE_NON_COPYABLE (UnitTest); + JUCE_DECLARE_NON_COPYABLE (UnitTest) }; @@ -197,13 +214,19 @@ public: The tests are performed in order, and the results are logged. To run all the registered UnitTest objects that exist, use runAllTests(). + + If you want to run the tests with a predetermined seed, you can pass that into + the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. */ - void runTests (const Array& tests); + void runTests (const Array& tests, int64 randomSeed = 0); /** Runs all the UnitTest objects that currently exist. This calls runTests() for all the objects listed in UnitTest::getAllTests(). + + If you want to run the tests with a predetermined seed, you can pass that into + the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. */ - void runAllTests(); + void runAllTests (int64 randomSeed = 0); /** Sets a flag to indicate whether an assertion should be triggered if a test fails. This is true by default. @@ -273,6 +296,7 @@ private: String currentSubCategory; OwnedArray results; bool assertOnFailure, logPasses; + Random randomForTest; void beginNewTest (UnitTest* test, const String& subCategory); void endTest(); @@ -280,8 +304,8 @@ private: void addPass(); void addFail (const String& failureMessage); - JUCE_DECLARE_NON_COPYABLE (UnitTestRunner); + JUCE_DECLARE_NON_COPYABLE (UnitTestRunner) }; -#endif // __JUCE_UNITTEST_JUCEHEADER__ +#endif // JUCE_UNITTEST_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp index 645d836..2eab527 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -26,12 +29,18 @@ XmlDocument::XmlDocument (const String& documentText) : originalText (documentText), input (nullptr), + outOfData (false), + errorOccurred (false), + needToLoadDTD (false), ignoreEmptyTextElements (true) { } XmlDocument::XmlDocument (const File& file) : input (nullptr), + outOfData (false), + errorOccurred (false), + needToLoadDTD (false), ignoreEmptyTextElements (true), inputSource (new FileInputSource (file)) { @@ -92,55 +101,54 @@ namespace XmlIdentifierChars DBG (s); }*/ + + static String::CharPointerType findEndOfToken (String::CharPointerType p) + { + while (isIdentifierChar (*p)) + ++p; + + return p; + } } XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentElement) { - String textToParse (originalText); - - if (textToParse.isEmpty() && inputSource != nullptr) + if (originalText.isEmpty() && inputSource != nullptr) { - ScopedPointer in (inputSource->createInputStream()); + ScopedPointer in (inputSource->createInputStream()); if (in != nullptr) { MemoryOutputStream data; data.writeFromInputStream (*in, onlyReadOuterDocumentElement ? 8192 : -1); - textToParse = data.toString(); - if (! onlyReadOuterDocumentElement) - originalText = textToParse; + #if JUCE_STRING_UTF_TYPE == 8 + if (data.getDataSize() > 2) + { + data.writeByte (0); + const char* text = static_cast (data.getData()); + + if (CharPointer_UTF16::isByteOrderMarkBigEndian (text) + || CharPointer_UTF16::isByteOrderMarkLittleEndian (text)) + { + originalText = data.toString(); + } + else + { + if (CharPointer_UTF8::isByteOrderMark (text)) + text += 3; + + // parse the input buffer directly to avoid copying it all to a string.. + return parseDocumentElement (String::CharPointerType (text), onlyReadOuterDocumentElement); + } + } + #else + originalText = data.toString(); + #endif } } - input = textToParse.getCharPointer(); - lastError = String::empty; - errorOccurred = false; - outOfData = false; - needToLoadDTD = true; - - if (textToParse.isEmpty()) - { - lastError = "not enough input"; - } - else - { - skipHeader(); - - if (input.getAddress() != nullptr) - { - ScopedPointer result (readNextElement (! onlyReadOuterDocumentElement)); - - if (! errorOccurred) - return result.release(); - } - else - { - lastError = "incorrect xml header"; - } - } - - return nullptr; + return parseDocumentElement (originalText.getCharPointer(), onlyReadOuterDocumentElement); } const String& XmlDocument::getLastParseError() const noexcept @@ -158,7 +166,7 @@ String XmlDocument::getFileContents (const String& filename) const { if (inputSource != nullptr) { - const ScopedPointer in (inputSource->createInputStreamFor (filename.trim().unquoted())); + const ScopedPointer in (inputSource->createInputStreamFor (filename.trim().unquoted())); if (in != nullptr) return in->readEntireStreamAsString(); @@ -180,33 +188,56 @@ juce_wchar XmlDocument::readNextChar() noexcept return c; } -int XmlDocument::findNextTokenLength() noexcept +XmlElement* XmlDocument::parseDocumentElement (String::CharPointerType textToParse, + const bool onlyReadOuterDocumentElement) { - int len = 0; - juce_wchar c = *input; + input = textToParse; + errorOccurred = false; + outOfData = false; + needToLoadDTD = true; - while (XmlIdentifierChars::isIdentifierChar (c)) - c = input [++len]; + if (textToParse.isEmpty()) + { + lastError = "not enough input"; + } + else if (! parseHeader()) + { + lastError = "malformed header"; + } + else if (! parseDTD()) + { + lastError = "malformed DTD"; + } + else + { + lastError.clear(); - return len; + ScopedPointer result (readNextElement (! onlyReadOuterDocumentElement)); + + if (! errorOccurred) + return result.release(); + } + + return nullptr; } -void XmlDocument::skipHeader() +bool XmlDocument::parseHeader() { - const int headerStart = input.indexOf (CharPointer_UTF8 ("= 0) + if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII ("")); - if (headerEnd < 0) - return; + const String::CharPointerType headerEnd (CharacterFunctions::find (input, CharPointer_ASCII ("?>"))); + + if (headerEnd.isEmpty()) + return false; #if JUCE_DEBUG - const String header (input + headerStart, (size_t) (headerEnd - headerStart)); - const String encoding (header.fromFirstOccurrenceOf ("encoding", false, true) - .fromFirstOccurrenceOf ("=", false, false) - .fromFirstOccurrenceOf ("\"", false, false) - .upToFirstOccurrenceOf ("\"", false, false).trim()); + const String encoding (String (input, headerEnd) + .fromFirstOccurrenceOf ("encoding", false, true) + .fromFirstOccurrenceOf ("=", false, false) + .fromFirstOccurrenceOf ("\"", false, false) + .upToFirstOccurrenceOf ("\"", false, false).trim()); /* If you load an XML document with a non-UTF encoding type, it may have been loaded wrongly.. Since all the files are read via the normal juce file streams, @@ -218,58 +249,59 @@ void XmlDocument::skipHeader() jassert (encoding.isEmpty() || encoding.startsWithIgnoreCase ("utf-")); #endif - input += headerEnd + 2; + input = headerEnd + 2; + skipNextWhiteSpace(); } - skipNextWhiteSpace(); + return true; +} - const int docTypeIndex = input.indexOf (CharPointer_UTF8 (" 0) +bool XmlDocument::parseDTD() +{ + if (CharacterFunctions::compareUpTo (input, CharPointer_ASCII (" 0;) + { + const juce_wchar c = readNextChar(); - if (c == '<') - ++n; - else if (c == '>') - --n; + if (outOfData) + return false; + + if (c == '<') + ++n; + else if (c == '>') + --n; + } + + dtdText = String (dtdStart, input - 1).trim(); } - dtdText = String (docType, (size_t) (input.getAddress() - (docType.getAddress() + 1))).trim(); + return true; } void XmlDocument::skipNextWhiteSpace() { for (;;) { - juce_wchar c = *input; + input = input.findEndOfWhitespace(); - while (CharacterFunctions::isWhitespace (c)) - c = *++input; - - if (c == 0) + if (input.isEmpty()) { outOfData = true; break; } - else if (c == '<') + + if (*input == '<') { if (input[1] == '!' && input[2] == '-' && input[3] == '-') { input += 4; - const int closeComment = input.indexOf (CharPointer_UTF8 ("-->")); + const int closeComment = input.indexOf (CharPointer_ASCII ("-->")); if (closeComment < 0) { @@ -280,10 +312,11 @@ void XmlDocument::skipNextWhiteSpace() input += closeComment + 3; continue; } - else if (input[1] == '?') + + if (input[1] == '?') { input += 2; - const int closeBracket = input.indexOf (CharPointer_UTF8 ("?>")); + const int closeBracket = input.indexOf (CharPointer_ASCII ("?>")); if (closeBracket < 0) { @@ -320,7 +353,6 @@ void XmlDocument::readQuotedString (String& result) else { const String::CharPointerType start (input); - size_t numChars = 0; for (;;) { @@ -328,24 +360,23 @@ void XmlDocument::readQuotedString (String& result) if (character == quote) { - result.appendCharPointer (start, numChars); + result.appendCharPointer (start, input); ++input; return; } else if (character == '&') { - result.appendCharPointer (start, numChars); + result.appendCharPointer (start, input); break; } else if (character == 0) { - outOfData = true; setLastError ("unmatched quotes", false); + outOfData = true; break; } ++input; - ++numChars; } } } @@ -359,28 +390,26 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (outOfData) return nullptr; - const int openBracket = input.indexOf ((juce_wchar) '<'); - - if (openBracket >= 0) + if (*input == '<') { - input += openBracket + 1; - int tagLen = findNextTokenLength(); + ++input; + String::CharPointerType endOfToken (XmlIdentifierChars::findEndOfToken (input)); - if (tagLen == 0) + if (endOfToken == input) { // no tag name - but allow for a gap after the '<' before giving an error skipNextWhiteSpace(); - tagLen = findNextTokenLength(); + endOfToken = XmlIdentifierChars::findEndOfToken (input); - if (tagLen == 0) + if (endOfToken == input) { setLastError ("tag name missing", false); return node; } } - node = new XmlElement (String (input, (size_t) tagLen)); - input += tagLen; + node = new XmlElement (input, endOfToken); + input = endOfToken; LinkedListPointer::Appender attributeAppender (node->attributes); // look for attributes @@ -403,7 +432,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) ++input; if (alsoParseSubElements) - readChildElements (node); + readChildElements (*node); break; } @@ -411,12 +440,12 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) // get an attribute.. if (XmlIdentifierChars::isIdentifierChar (c)) { - const int attNameLen = findNextTokenLength(); + String::CharPointerType attNameEnd (XmlIdentifierChars::findEndOfToken (input)); - if (attNameLen > 0) + if (attNameEnd != input) { const String::CharPointerType attNameStart (input); - input += attNameLen; + input = attNameEnd; skipNextWhiteSpace(); @@ -429,14 +458,19 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (nextChar == '"' || nextChar == '\'') { XmlElement::XmlAttributeNode* const newAtt - = new XmlElement::XmlAttributeNode (String (attNameStart, (size_t) attNameLen), - String::empty); + = new XmlElement::XmlAttributeNode (attNameStart, attNameEnd); readQuotedString (newAtt->value); attributeAppender.append (newAtt); continue; } } + else + { + setLastError ("expected '=' after attribute '" + + String (attNameStart, attNameEnd) + "'", false); + return node; + } } } else @@ -452,9 +486,9 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) return node; } -void XmlDocument::readChildElements (XmlElement* parent) +void XmlDocument::readChildElements (XmlElement& parent) { - LinkedListPointer::Appender childAppender (parent->firstChildElement); + LinkedListPointer::Appender childAppender (parent.firstChildElement); for (;;) { @@ -469,7 +503,9 @@ void XmlDocument::readChildElements (XmlElement* parent) if (*input == '<') { - if (input[1] == '/') + const juce_wchar c1 = input[1]; + + if (c1 == '/') { // our close tag.. const int closeTag = input.indexOf ((juce_wchar) '>'); @@ -479,48 +515,38 @@ void XmlDocument::readChildElements (XmlElement* parent) break; } - else if (input[1] == '!' - && input[2] == '[' - && input[3] == 'C' - && input[4] == 'D' - && input[5] == 'A' - && input[6] == 'T' - && input[7] == 'A' - && input[8] == '[') + + if (c1 == '!' && CharacterFunctions::compareUpTo (input + 2, CharPointer_ASCII ("[CDATA["), 7) == 0) { input += 9; const String::CharPointerType inputStart (input); - size_t len = 0; - for (;;) { - if (*input == 0) + const juce_wchar c0 = *input; + + if (c0 == 0) { setLastError ("unterminated CDATA section", false); outOfData = true; break; } - else if (input[0] == ']' + else if (c0 == ']' && input[1] == ']' && input[2] == '>') { + childAppender.append (XmlElement::createTextElement (String (inputStart, input))); input += 3; break; } ++input; - ++len; } - - childAppender.append (XmlElement::createTextElement (String (inputStart, len))); } else { // this is some other element, so parse and add it.. - XmlElement* const n = readNextElement (true); - - if (n != nullptr) + if (XmlElement* const n = readNextElement (true)) childAppender.append (n); else break; @@ -536,7 +562,25 @@ void XmlDocument::readChildElements (XmlElement* parent) const juce_wchar c = *input; if (c == '<') + { + if (input[1] == '!' && input[2] == '-' && input[3] == '-') + { + input += 4; + const int closeComment = input.indexOf (CharPointer_ASCII ("-->")); + + if (closeComment < 0) + { + setLastError ("unterminated comment", false); + outOfData = true; + return; + } + + input += closeComment + 3; + continue; + } + break; + } if (c == 0) { @@ -579,17 +623,15 @@ void XmlDocument::readChildElements (XmlElement* parent) else { const String::CharPointerType start (input); - size_t len = 0; for (;;) { const juce_wchar nextChar = *input; if (nextChar == '<' || nextChar == '&') - { break; - } - else if (nextChar == 0) + + if (nextChar == 0) { setLastError ("unmatched tags", false); outOfData = true; @@ -597,17 +639,14 @@ void XmlDocument::readChildElements (XmlElement* parent) } ++input; - ++len; } - textElementContent.appendCharPointer (start, len); + textElementContent.appendCharPointer (start, input); } } if ((! ignoreEmptyTextElements) || textElementContent.containsNonWhitespaceChars()) - { childAppender.append (XmlElement::createTextElement (textElementContent)); - } } } } @@ -617,27 +656,27 @@ void XmlDocument::readEntity (String& result) // skip over the ampersand ++input; - if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("amp;"), 4) == 0) + if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("amp;"), 4) == 0) { input += 4; result += '&'; } - else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("quot;"), 5) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("quot;"), 5) == 0) { input += 5; result += '"'; } - else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("apos;"), 5) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("apos;"), 5) == 0) { input += 5; result += '\''; } - else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("lt;"), 3) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("lt;"), 3) == 0) { input += 3; result += '<'; } - else if (input.compareIgnoreCaseUpTo (CharPointer_UTF8 ("gt;"), 3) == 0) + else if (input.compareIgnoreCaseUpTo (CharPointer_ASCII ("gt;"), 3) == 0) { input += 3; result += '>'; @@ -843,8 +882,8 @@ String XmlDocument::getParameterEntity (const String& entity) if (ent.equalsIgnoreCase ("system")) return getFileContents (tokenisedDTD [i + 2].trimCharactersAtEnd (">")); - else - return ent.trim().unquoted(); + + return ent.trim().unquoted(); } } diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h index 9af494d..d2a5b49 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.h @@ -1,36 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_XMLDOCUMENT_JUCEHEADER__ -#define __JUCE_XMLDOCUMENT_JUCEHEADER__ - -#include "juce_XmlElement.h" -#include "../text/juce_StringArray.h" -#include "../files/juce_File.h" -#include "../memory/juce_ScopedPointer.h" -class InputSource; +#ifndef JUCE_XMLDOCUMENT_H_INCLUDED +#define JUCE_XMLDOCUMENT_H_INCLUDED //============================================================================== @@ -90,7 +87,7 @@ public: /** 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 + parse error, it may returns nullptr (and you can find out the error using the getLastParseError() method). See also the parse() methods, which provide a shorthand way to quickly @@ -159,25 +156,26 @@ private: String lastError, dtdText; StringArray tokenisedDTD; bool needToLoadDTD, ignoreEmptyTextElements; - ScopedPointer inputSource; + ScopedPointer inputSource; - void setLastError (const String& desc, bool carryOn); - void skipHeader(); + XmlElement* parseDocumentElement (String::CharPointerType, bool outer); + void setLastError (const String&, bool carryOn); + bool parseHeader(); + bool parseDTD(); void skipNextWhiteSpace(); juce_wchar readNextChar() noexcept; XmlElement* readNextElement (bool alsoParseSubElements); - void readChildElements (XmlElement* parent); - int findNextTokenLength() noexcept; - void readQuotedString (String& result); - void readEntity (String& result); + void readChildElements (XmlElement&); + void readQuotedString (String&); + void readEntity (String&); - String getFileContents (const String& filename) const; - String expandEntity (const String& entity); - String expandExternalEntity (const String& entity); - String getParameterEntity (const String& entity); + String getFileContents (const String&) const; + String expandEntity (const String&); + String expandExternalEntity (const String&); + String getParameterEntity (const String&); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlDocument); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlDocument) }; -#endif // __JUCE_XMLDOCUMENT_JUCEHEADER__ +#endif // JUCE_XMLDOCUMENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp index 9ccaac2..000df15 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -29,9 +32,8 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) n { } -XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, const String& value_) noexcept - : name (name_), - value (value_) +XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept + : name (n), value (v) { #if JUCE_DEBUG // this checks whether the attribute name string contains any illegal characters.. @@ -40,20 +42,51 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const String& name_, const Strin #endif } -inline bool XmlElement::XmlAttributeNode::hasName (const String& nameToMatch) const noexcept +XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd) + : name (nameStart, nameEnd) { - return name.equalsIgnoreCase (nameToMatch); } //============================================================================== -XmlElement::XmlElement (const String& tagName_) noexcept - : tagName (tagName_) +static void sanityCheckTagName (const String& tag) { + (void) tag; + // the tag name mustn't be empty, or it'll look like a text element! - jassert (tagName_.containsNonWhitespaceChars()) + jassert (tag.containsNonWhitespaceChars()) // The tag can't contain spaces or other characters that would create invalid XML! - jassert (! tagName_.containsAnyOf (" <>/&")); + jassert (! tag.containsAnyOf (" <>/&(){}")); +} + +XmlElement::XmlElement (const String& tag) + : tagName (StringPool::getGlobalPool().getPooledString (tag)) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (const char* tag) + : tagName (StringPool::getGlobalPool().getPooledString (tag)) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (StringRef tag) + : tagName (StringPool::getGlobalPool().getPooledString (tag)) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (const Identifier& tag) + : tagName (tag.toString()) +{ + sanityCheckTagName (tagName); +} + +XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd) + : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd)) +{ + sanityCheckTagName (tagName); } XmlElement::XmlElement (int /*dummy*/) noexcept @@ -72,9 +105,7 @@ XmlElement& XmlElement::operator= (const XmlElement& other) { removeAllAttributes(); deleteAllChildElements(); - tagName = other.tagName; - copyChildrenAndAttributesFrom (other); } @@ -83,10 +114,10 @@ XmlElement& XmlElement::operator= (const XmlElement& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS XmlElement::XmlElement (XmlElement&& other) noexcept - : nextListItem (static_cast &&> (other.nextListItem)), - firstChildElement (static_cast &&> (other.firstChildElement)), - attributes (static_cast &&> (other.attributes)), - tagName (static_cast (other.tagName)) + : nextListItem (static_cast&&> (other.nextListItem)), + firstChildElement (static_cast&&> (other.firstChildElement)), + attributes (static_cast&&> (other.attributes)), + tagName (static_cast (other.tagName)) { } @@ -97,10 +128,10 @@ XmlElement& XmlElement::operator= (XmlElement&& other) noexcept removeAllAttributes(); deleteAllChildElements(); - nextListItem = static_cast &&> (other.nextListItem); - firstChildElement = static_cast &&> (other.firstChildElement); - attributes = static_cast &&> (other.attributes); - tagName = static_cast (other.tagName); + nextListItem = static_cast&&> (other.nextListItem); + firstChildElement = static_cast&&> (other.firstChildElement); + attributes = static_cast&&> (other.attributes); + tagName = static_cast (other.tagName); return *this; } @@ -161,8 +192,8 @@ namespace XmlOutputFunctions static bool isLegalXmlChar (const uint32 c) noexcept { - static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, 255, 255, 191, 254, 255, 255, 127 }; - + static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, + 255, 255, 191, 254, 255, 255, 127 }; return c < sizeof (legalChars) * 8 && (legalChars [c >> 3] & (1 << (c & 7))) != 0; } @@ -207,7 +238,7 @@ namespace XmlOutputFunctions } } - static void writeSpaces (OutputStream& out, const int numSpaces) + static void writeSpaces (OutputStream& out, const size_t numSpaces) { out.writeRepeatedByte (' ', numSpaces); } @@ -220,7 +251,7 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, using namespace XmlOutputFunctions; if (indentationLevel >= 0) - writeSpaces (outputStream, indentationLevel); + writeSpaces (outputStream, (size_t) indentationLevel); if (! isTextElement()) { @@ -228,7 +259,7 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, outputStream << tagName; { - const int attIndent = indentationLevel + tagName.length() + 1; + const size_t attIndent = (size_t) (indentationLevel + tagName.length() + 1); int lineLen = 0; for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) @@ -254,7 +285,6 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, { outputStream.writeByte ('>'); - bool lastWasTextNode = false; for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) @@ -278,7 +308,7 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, if (indentationLevel >= 0 && ! lastWasTextNode) { outputStream << newLine; - writeSpaces (outputStream, indentationLevel); + writeSpaces (outputStream, (size_t) indentationLevel); } outputStream.write (" out (tempFile.getFile().createOutputStream()); + FileOutputStream out (tempFile.getFile()); - if (out != nullptr) - { - writeToStream (*out, dtdToUse, false, true, encodingType, lineWrapLength); - out = nullptr; + if (! out.openedOk()) + return false; - return tempFile.overwriteTargetFileWithTemporary(); - } + writeToStream (out, dtdToUse, false, true, encodingType, lineWrapLength); } - return false; + return tempFile.overwriteTargetFileWithTemporary(); } //============================================================================== -bool XmlElement::hasTagName (const String& tagNameWanted) const noexcept +bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept { - #if JUCE_DEBUG - // if debugging, check that the case is actually the same, because - // valid xml is case-sensitive, and although this lets it pass, it's - // better not to.. - if (tagName.equalsIgnoreCase (tagNameWanted)) - { - jassert (tagName == tagNameWanted); - return true; - } - else - { - return false; - } - #else - return tagName.equalsIgnoreCase (tagNameWanted); - #endif + const bool matches = tagName.equalsIgnoreCase (possibleTagName); + + // XML tags should be case-sensitive, so although this method allows a + // case-insensitive match to pass, you should try to avoid this. + jassert ((! matches) || tagName == possibleTagName); + + return matches; } -XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName) const +String XmlElement::getNamespace() const +{ + return tagName.upToFirstOccurrenceOf (":", false, false); +} + +String XmlElement::getTagNameWithoutNamespace() const +{ + return tagName.fromLastOccurrenceOf (":", false, false); +} + +bool XmlElement::hasTagNameIgnoringNamespace (StringRef possibleTagName) const +{ + return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName; +} + +XmlElement* XmlElement::getNextElementWithTagName (StringRef requiredTagName) const { XmlElement* e = nextListItem; @@ -404,98 +437,96 @@ int XmlElement::getNumAttributes() const noexcept const String& XmlElement::getAttributeName (const int index) const noexcept { - const XmlAttributeNode* const att = attributes [index]; - return att != nullptr ? att->name : String::empty; -} - -const String& XmlElement::getAttributeValue (const int index) const noexcept -{ - const XmlAttributeNode* const att = attributes [index]; - return att != nullptr ? att->value : String::empty; -} - -bool XmlElement::hasAttribute (const String& attributeName) const noexcept -{ - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return true; - - return false; -} - -//============================================================================== -const String& XmlElement::getStringAttribute (const String& attributeName) const noexcept -{ - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value; + if (const XmlAttributeNode* const att = attributes [index]) + return att->name.toString(); return String::empty; } -String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const +const String& XmlElement::getAttributeValue (const int index) const noexcept { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value; + if (const XmlAttributeNode* const att = attributes [index]) + return att->value; + + return String::empty; +} + +XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept +{ + for (XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) + if (att->name == attributeName) + return att; + + return nullptr; +} + +bool XmlElement::hasAttribute (StringRef attributeName) const noexcept +{ + return getAttribute (attributeName) != nullptr; +} + +//============================================================================== +const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept +{ + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value; + + return String::empty; +} + +String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const +{ + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value; return defaultReturnValue; } -int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const +int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value.getIntValue(); + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value.getIntValue(); return defaultReturnValue; } -double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const +double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value.getDoubleValue(); + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value.getDoubleValue(); return defaultReturnValue; } -bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const +bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) + if (const XmlAttributeNode* att = getAttribute (attributeName)) { - if (att->hasName (attributeName)) - { - juce_wchar firstChar = att->value[0]; + const juce_wchar firstChar = *(att->value.getCharPointer().findEndOfWhitespace()); - if (CharacterFunctions::isWhitespace (firstChar)) - firstChar = att->value.trimStart() [0]; - - return firstChar == '1' - || firstChar == 't' - || firstChar == 'y' - || firstChar == 'T' - || firstChar == 'Y'; - } + return firstChar == '1' + || firstChar == 't' + || firstChar == 'y' + || firstChar == 'T' + || firstChar == 'Y'; } return defaultReturnValue; } -bool XmlElement::compareAttribute (const String& attributeName, - const String& stringToCompareAgainst, +bool XmlElement::compareAttribute (StringRef attributeName, + StringRef stringToCompareAgainst, const bool ignoreCase) const noexcept { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst) - : att->value == stringToCompareAgainst; + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst) + : att->value == stringToCompareAgainst; return false; } //============================================================================== -void XmlElement::setAttribute (const String& attributeName, const String& value) +void XmlElement::setAttribute (const Identifier& attributeName, const String& value) { if (attributes == nullptr) { @@ -503,49 +534,44 @@ void XmlElement::setAttribute (const String& attributeName, const String& value) } else { - XmlAttributeNode* att = attributes; - - for (;;) + for (XmlAttributeNode* att = attributes; ; att = att->nextListItem) { - if (att->hasName (attributeName)) + if (att->name == attributeName) { att->value = value; break; } - else if (att->nextListItem == nullptr) + + if (att->nextListItem == nullptr) { att->nextListItem = new XmlAttributeNode (attributeName, value); break; } - - att = att->nextListItem; } } } -void XmlElement::setAttribute (const String& attributeName, const int number) +void XmlElement::setAttribute (const Identifier& attributeName, const int number) { setAttribute (attributeName, String (number)); } -void XmlElement::setAttribute (const String& attributeName, const double number) +void XmlElement::setAttribute (const Identifier& attributeName, const double number) { - setAttribute (attributeName, String (number)); + setAttribute (attributeName, String (number, 20)); } -void XmlElement::removeAttribute (const String& attributeName) noexcept +void XmlElement::removeAttribute (const Identifier& attributeName) noexcept { - LinkedListPointer* att = &attributes; - - while (att->get() != nullptr) + for (LinkedListPointer* att = &attributes; + att->get() != nullptr; + att = &(att->get()->nextListItem)) { - if (att->get()->hasName (attributeName)) + if (att->get()->name == attributeName) { delete att->removeNext(); break; } - - att = &(att->get()->nextListItem); } } @@ -565,8 +591,10 @@ XmlElement* XmlElement::getChildElement (const int index) const noexcept return firstChildElement [index].get(); } -XmlElement* XmlElement::getChildByName (const String& childName) const noexcept +XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept { + jassert (! childName.isEmpty()); + for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) if (child->hasTagName (childName)) return child; @@ -574,23 +602,51 @@ XmlElement* XmlElement::getChildByName (const String& childName) const noexcept return nullptr; } +XmlElement* XmlElement::getChildByAttribute (StringRef attributeName, StringRef attributeValue) const noexcept +{ + jassert (! attributeName.isEmpty()); + + for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) + if (child->compareAttribute (attributeName, attributeValue)) + return child; + + return nullptr; +} + void XmlElement::addChildElement (XmlElement* const newNode) noexcept { if (newNode != nullptr) + { + // The element being added must not be a child of another node! + jassert (newNode->nextListItem == nullptr); + firstChildElement.append (newNode); + } } -void XmlElement::insertChildElement (XmlElement* const newNode, - int indexToInsertAt) noexcept +void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept { if (newNode != nullptr) { - removeChildElement (newNode, false); + // The element being added must not be a child of another node! + jassert (newNode->nextListItem == nullptr); + firstChildElement.insertAtIndex (indexToInsertAt, newNode); } } -XmlElement* XmlElement::createNewChildElement (const String& childTagName) +void XmlElement::prependChildElement (XmlElement* newNode) noexcept +{ + if (newNode != nullptr) + { + // The element being added must not be a child of another node! + jassert (newNode->nextListItem == nullptr); + + firstChildElement.insertNext (newNode); + } +} + +XmlElement* XmlElement::createNewChildElement (StringRef childTagName) { XmlElement* const newElement = new XmlElement (childTagName); addChildElement (newElement); @@ -602,9 +658,7 @@ bool XmlElement::replaceChildElement (XmlElement* const currentChildElement, { if (newNode != nullptr) { - LinkedListPointer* const p = firstChildElement.findPointerTo (currentChildElement); - - if (p != nullptr) + if (LinkedListPointer* const p = firstChildElement.findPointerTo (currentChildElement)) { if (currentChildElement != newNode) delete p->replaceNext (newNode); @@ -706,11 +760,9 @@ void XmlElement::deleteAllChildElements() noexcept firstChildElement.deleteAll(); } -void XmlElement::deleteAllChildElementsWithTagName (const String& name) noexcept +void XmlElement::deleteAllChildElementsWithTagName (StringRef name) noexcept { - XmlElement* child = firstChildElement; - - while (child != nullptr) + for (XmlElement* child = firstChildElement; child != nullptr;) { XmlElement* const nextChild = child->nextListItem; @@ -736,9 +788,7 @@ XmlElement* XmlElement::findParentElementOf (const XmlElement* const elementToLo if (elementToLookFor == child) return this; - XmlElement* const found = child->findParentElementOf (elementToLookFor); - - if (found != nullptr) + if (XmlElement* const found = child->findParentElementOf (elementToLookFor)) return found; } @@ -793,20 +843,20 @@ String XmlElement::getAllSubText() const if (isTextElement()) return getText(); + if (getNumChildElements() == 1) + return firstChildElement.get()->getAllSubText(); + MemoryOutputStream mem (1024); for (const XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) mem << child->getAllSubText(); - return mem.toString(); + return mem.toUTF8(); } -String XmlElement::getChildElementAllSubText (const String& childTagName, - const String& defaultReturnValue) const +String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const { - const XmlElement* const child = getChildByName (childTagName); - - if (child != nullptr) + if (const XmlElement* const child = getChildByName (childTagName)) return child->getAllSubText(); return defaultReturnValue; @@ -826,9 +876,7 @@ void XmlElement::addTextElement (const String& text) void XmlElement::deleteAllTextElements() noexcept { - XmlElement* child = firstChildElement; - - while (child != nullptr) + for (XmlElement* child = firstChildElement; child != nullptr;) { XmlElement* const next = child->nextListItem; diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h index 2d79ce6..4ab4d08 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlElement.h @@ -1,35 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_XMLELEMENT_JUCEHEADER__ -#define __JUCE_XMLELEMENT_JUCEHEADER__ - -#include "../text/juce_String.h" -#include "../streams/juce_OutputStream.h" -#include "../files/juce_File.h" -#include "../containers/juce_LinkedListPointer.h" +#ifndef JUCE_XMLELEMENT_H_INCLUDED +#define JUCE_XMLELEMENT_H_INCLUDED //============================================================================== @@ -146,20 +144,32 @@ class JUCE_API XmlElement public: //============================================================================== /** Creates an XmlElement with this tag name. */ - explicit XmlElement (const String& tagName) noexcept; + explicit XmlElement (const String& tagName); + + /** Creates an XmlElement with this tag name. */ + explicit XmlElement (const char* tagName); + + /** Creates an XmlElement with this tag name. */ + explicit XmlElement (const Identifier& tagName); + + /** Creates an XmlElement with this tag name. */ + explicit XmlElement (StringRef tagName); + + /** Creates an XmlElement with this tag name. */ + XmlElement (String::CharPointerType tagNameBegin, String::CharPointerType tagNameEnd); /** Creates a (deep) copy of another element. */ - XmlElement (const XmlElement& other); + XmlElement (const XmlElement&); /** Creates a (deep) copy of another element. */ - XmlElement& operator= (const XmlElement& other); + XmlElement& operator= (const XmlElement&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - XmlElement (XmlElement&& other) noexcept; - XmlElement& operator= (XmlElement&& other) noexcept; + XmlElement (XmlElement&&) noexcept; + XmlElement& operator= (XmlElement&&) noexcept; #endif - /** Deleting an XmlElement will also delete all its child elements. */ + /** Deleting an XmlElement will also delete all of its child elements. */ ~XmlElement() noexcept; //============================================================================== @@ -195,10 +205,10 @@ public: determines how lists of attributes get broken up @see writeToStream, writeToFile */ - String createDocument (const String& dtdToUse, + String createDocument (StringRef dtdToUse, bool allOnOneLine = false, bool includeXmlHeader = true, - const String& encodingType = "UTF-8", + StringRef encodingType = "UTF-8", int lineWrapLength = 60) const; /** Writes the document to a stream as UTF-8. @@ -217,10 +227,10 @@ public: @see writeToFile, createDocument */ void writeToStream (OutputStream& output, - const String& dtdToUse, + StringRef dtdToUse, bool allOnOneLine = false, bool includeXmlHeader = true, - const String& encodingType = "UTF-8", + StringRef encodingType = "UTF-8", int lineWrapLength = 60) const; /** Writes the element to a file as an XML document. @@ -243,27 +253,34 @@ public: @see createDocument */ bool writeToFile (const File& destinationFile, - const String& dtdToUse, - const String& encodingType = "UTF-8", + StringRef dtdToUse, + StringRef encodingType = "UTF-8", int lineWrapLength = 60) const; //============================================================================== /** Returns this element's tag type name. - - E.g. for an element such as \, this would return - "MOOSE". - + E.g. for an element such as \, this would return "MOOSE". @see hasTagName */ - inline const String& getTagName() const noexcept { return tagName; } + const String& getTagName() const noexcept { return tagName; } + + /** Returns the namespace portion of the tag-name, or an empty string if none is specified. */ + String getNamespace() const; + + /** Returns the part of the tag-name that follows any namespace declaration. */ + String getTagNameWithoutNamespace() const; /** 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 noexcept; + bool hasTagName (StringRef possibleTagName) const noexcept; + + /** Tests whether this element has a particular tag name, ignoring any XML namespace prefix. + So a test for e.g. "xyz" will return true for "xyz" and also "foo:xyz", "bar::xyz", etc. + @see getTagName + */ + bool hasTagNameIgnoringNamespace (StringRef possibleTagName) const; //============================================================================== /** Returns the number of XML attributes this element contains. @@ -295,22 +312,19 @@ public: // Attribute-handling methods.. /** Checks whether the element contains an attribute with a certain name. */ - bool hasAttribute (const String& attributeName) const noexcept; + bool hasAttribute (StringRef attributeName) const noexcept; /** Returns the value of a named attribute. - @param attributeName the name of the attribute to look up */ - const String& getStringAttribute (const String& attributeName) const noexcept; + const String& getStringAttribute (StringRef attributeName) const noexcept; /** 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 */ - String getStringAttribute (const String& attributeName, - const String& defaultReturnValue) const; + String getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const; /** Compares the value of a named attribute with a value passed-in. @@ -320,8 +334,8 @@ public: @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 compareAttribute (StringRef attributeName, + StringRef stringToCompareAgainst, bool ignoreCase = false) const noexcept; /** Returns the value of a named attribute as an integer. @@ -334,8 +348,7 @@ public: with this name @see setAttribute */ - int getIntAttribute (const String& attributeName, - int defaultReturnValue = 0) const; + int getIntAttribute (StringRef attributeName, int defaultReturnValue = 0) const; /** Returns the value of a named attribute as floating-point. @@ -347,8 +360,7 @@ public: with this name @see setAttribute */ - double getDoubleAttribute (const String& attributeName, - double defaultReturnValue = 0.0) const; + double getDoubleAttribute (StringRef attributeName, double defaultReturnValue = 0.0) const; /** Returns the value of a named attribute as a boolean. @@ -360,8 +372,7 @@ public: @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; + bool getBoolAttribute (StringRef attributeName, bool defaultReturnValue = false) const; /** Adds a named attribute to the element. @@ -376,8 +387,7 @@ public: @param newValue the value to set it to @see removeAttribute */ - void setAttribute (const String& attributeName, - const String& newValue); + void setAttribute (const Identifier& attributeName, const String& newValue); /** Adds a named attribute to the element, setting it to an integer value. @@ -391,8 +401,7 @@ public: @param attributeName the name of the attribute to set @param newValue the value to set it to */ - void setAttribute (const String& attributeName, - int newValue); + void setAttribute (const Identifier& attributeName, int newValue); /** Adds a named attribute to the element, setting it to a floating-point value. @@ -406,27 +415,23 @@ public: @param attributeName the name of the attribute to set @param newValue the value to set it to */ - void setAttribute (const String& attributeName, - double newValue); + void setAttribute (const Identifier& 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) noexcept; + void removeAttribute (const Identifier& attributeName) noexcept; - /** Removes all attributes from this element. - */ + /** Removes all attributes from this element. */ void removeAllAttributes() noexcept; //============================================================================== // 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 noexcept { return firstChildElement; } @@ -467,10 +472,9 @@ public: @see getNextElement, forEachXmlChildElementWithTagName */ - XmlElement* getNextElementWithTagName (const String& requiredTagName) const; + XmlElement* getNextElementWithTagName (StringRef requiredTagName) const; /** Returns the number of sub-elements in this element. - @see getChildElement */ int getNumChildElements() const noexcept; @@ -480,7 +484,7 @@ public: 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 + @returns the n'th child of this element, or nullptr if the index is out-of-range @see getNextElement, isTextElement, getChildByName */ XmlElement* getChildElement (int index) const noexcept; @@ -488,10 +492,20 @@ public: /** 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 + @returns the first element with this tag name, or nullptr if none is found + @see getNextElement, isTextElement, getChildElement, getChildByAttribute */ - XmlElement* getChildByName (const String& tagNameToLookFor) const noexcept; + XmlElement* getChildByName (StringRef tagNameToLookFor) const noexcept; + + /** Returns the first sub-element which has an attribute that matches the given value. + + @param attributeName the name of the attribute to check + @param attributeValue the target value of the attribute + @returns the first element with this attribute value, or nullptr if none is found + @see getChildByName + */ + XmlElement* getChildByAttribute (StringRef attributeName, + StringRef attributeValue) const noexcept; //============================================================================== /** Appends an element to this element's list of children. @@ -500,6 +514,10 @@ public: 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. + Note that due to the XmlElement using a singly-linked-list, prependChildElement() + is an O(1) operation, but addChildElement() is an O(N) operation - so if + you're adding large number of elements, you may prefer to do so in reverse order! + @see getFirstChildElement, getNextElement, getNumChildElements, getChildElement, removeChildElement */ @@ -511,14 +529,28 @@ public: 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 newChildElement 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, + void insertChildElement (XmlElement* newChildElement, int indexToInsertAt) noexcept; + /** Inserts an element at the beginning of 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. + + Note that due to the XmlElement using a singly-linked-list, prependChildElement() + is an O(1) operation, but addChildElement() is an O(N) operation - so if + you're adding large number of elements, you may prefer to do so in reverse order! + + @see addChildElement, insertChildElement + */ + void prependChildElement (XmlElement* newChildElement) noexcept; + /** Creates a new element with the given name and returns it, after adding it as a child element. @@ -533,7 +565,7 @@ public: XmlElement* newElement = myParentElement->createNewChildElement ("foobar"); @endcode */ - XmlElement* createNewChildElement (const String& tagName); + XmlElement* createNewChildElement (StringRef tagName); /** Replaces one of this element's children with another node. @@ -555,16 +587,14 @@ public: bool shouldDeleteTheChild) noexcept; /** Deletes all the child elements in the element. - @see removeChildElement, deleteAllChildElementsWithTagName */ void deleteAllChildElements() noexcept; /** Deletes all the child elements with a given tag name. - @see removeChildElement */ - void deleteAllChildElementsWithTagName (const String& tagName) noexcept; + void deleteAllChildElementsWithTagName (StringRef tagName) noexcept; /** Returns true if the given element is a child of this one. */ bool containsChildElement (const XmlElement* possibleChild) const noexcept; @@ -671,23 +701,20 @@ public: @see getAllSubText */ - String getChildElementAllSubText (const String& childTagName, + String getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const; /** Appends a section of text to this element. - @see isTextElement, getText, getAllSubText */ void addTextElement (const String& text); /** Removes all the text elements from this element. - @see isTextElement, getText, getAllSubText, addTextElement */ void deleteAllTextElements() noexcept; - /** Creates a text element that can be added to a parent element. - */ + /** Creates a text element that can be added to a parent element. */ static XmlElement* createTextElement (const String& text); //============================================================================== @@ -695,25 +722,26 @@ private: struct XmlAttributeNode { XmlAttributeNode (const XmlAttributeNode&) noexcept; - XmlAttributeNode (const String& name, const String& value) noexcept; + XmlAttributeNode (const Identifier&, const String&) noexcept; + XmlAttributeNode (String::CharPointerType, String::CharPointerType); LinkedListPointer nextListItem; - String name, value; - - bool hasName (const String&) const noexcept; + Identifier name; + String value; private: - XmlAttributeNode& operator= (const XmlAttributeNode&); + XmlAttributeNode& operator= (const XmlAttributeNode&) JUCE_DELETED_FUNCTION; }; friend class XmlDocument; - friend class LinkedListPointer ; - friend class LinkedListPointer ; - friend class LinkedListPointer ::Appender; + friend class LinkedListPointer; + friend class LinkedListPointer; + friend class LinkedListPointer::Appender; + friend class NamedValueSet; - LinkedListPointer nextListItem; - LinkedListPointer firstChildElement; - LinkedListPointer attributes; + LinkedListPointer nextListItem; + LinkedListPointer firstChildElement; + LinkedListPointer attributes; String tagName; XmlElement (int) noexcept; @@ -721,9 +749,15 @@ private: void writeElementAsText (OutputStream&, int indentationLevel, int lineWrapLength) const; void getChildElementsAsArray (XmlElement**) const noexcept; void reorderChildElements (XmlElement**, int) noexcept; + XmlAttributeNode* getAttribute (StringRef) const noexcept; - JUCE_LEAK_DETECTOR (XmlElement); + // Sigh.. L"" or _T("") string literals are problematic in general, and really inappropriate + // for XML tags. Use a UTF-8 encoded literal instead, or if you're really determined to use + // UTF-16, cast it to a String and use the other constructor. + XmlElement (const wchar_t*) JUCE_DELETED_FUNCTION; + + JUCE_LEAK_DETECTOR (XmlElement) }; -#endif // __JUCE_XMLELEMENT_JUCEHEADER__ +#endif // JUCE_XMLELEMENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp index f5ff68b..cb9a577 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -46,26 +49,26 @@ public: zlibNamespace::deflateEnd (&stream); } - bool write (const uint8* data, int dataSize, OutputStream& destStream) + bool write (const uint8* data, size_t dataSize, OutputStream& out) { // When you call flush() on a gzip stream, the stream is closed, and you can // no longer continue to write data to it! jassert (! finished); while (dataSize > 0) - if (! doNextBlock (data, dataSize, destStream, Z_NO_FLUSH)) + if (! doNextBlock (data, dataSize, out, Z_NO_FLUSH)) return false; return true; } - void finish (OutputStream& destStream) + void finish (OutputStream& out) { const uint8* data = nullptr; - int dataSize = 0; + size_t dataSize = 0; while (! finished) - doNextBlock (data, dataSize, destStream, Z_FINISH); + doNextBlock (data, dataSize, out, Z_FINISH); } private: @@ -76,9 +79,10 @@ private: bool isFirstDeflate, streamIsValid, finished; zlibNamespace::Bytef buffer[32768]; - bool doNextBlock (const uint8*& data, int& dataSize, OutputStream& destStream, const int flushMode) + bool doNextBlock (const uint8*& data, size_t& dataSize, OutputStream& out, const int flushMode) { using namespace zlibNamespace; + if (streamIsValid) { stream.next_in = const_cast (data); @@ -98,9 +102,9 @@ private: case Z_OK: { data += dataSize - stream.avail_in; - dataSize = (int) stream.avail_in; - const int bytesDone = ((int) sizeof (buffer)) - (int) stream.avail_out; - return bytesDone <= 0 || destStream.write (buffer, bytesDone); + dataSize = stream.avail_in; + const ssize_t bytesDone = (ssize_t) sizeof (buffer) - (ssize_t) stream.avail_out; + return bytesDone <= 0 || out.write (buffer, (size_t) bytesDone); } default: @@ -111,18 +115,18 @@ private: return false; } - JUCE_DECLARE_NON_COPYABLE (GZIPCompressorHelper); + JUCE_DECLARE_NON_COPYABLE (GZIPCompressorHelper) }; //============================================================================== -GZIPCompressorOutputStream::GZIPCompressorOutputStream (OutputStream* const destStream_, +GZIPCompressorOutputStream::GZIPCompressorOutputStream (OutputStream* const out, const int compressionLevel, const bool deleteDestStream, const int windowBits) - : destStream (destStream_, deleteDestStream), + : destStream (out, deleteDestStream), helper (new GZIPCompressorHelper (compressionLevel, windowBits)) { - jassert (destStream_ != nullptr); + jassert (out != nullptr); } GZIPCompressorOutputStream::~GZIPCompressorOutputStream() @@ -136,9 +140,9 @@ void GZIPCompressorOutputStream::flush() destStream->flush(); } -bool GZIPCompressorOutputStream::write (const void* destBuffer, int howMany) +bool GZIPCompressorOutputStream::write (const void* destBuffer, size_t howMany) { - jassert (destBuffer != nullptr && howMany >= 0); + jassert (destBuffer != nullptr && (ssize_t) howMany >= 0); return helper->write (static_cast (destBuffer), howMany, *destStream); } @@ -165,7 +169,7 @@ public: void runTest() { beginTest ("GZIP"); - Random rng; + Random rng = getRandom(); for (int i = 100; --i >= 0;) { @@ -176,13 +180,13 @@ public: for (int j = rng.nextInt (100); --j >= 0;) { - MemoryBlock data (rng.nextInt (2000) + 1); + MemoryBlock data ((unsigned int) (rng.nextInt (2000) + 1)); for (int k = (int) data.getSize(); --k >= 0;) data[k] = (char) rng.nextInt (255); - original.write (data.getData(), (int) data.getSize()); - zipper .write (data.getData(), (int) data.getSize()); + original << data; + zipper << data; } } diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h index 9ac10b0..3309486 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPCompressorOutputStream.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ -#define __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ - -#include "../streams/juce_OutputStream.h" -#include "../memory/juce_OptionalScopedPointer.h" -#include "../memory/juce_HeapBlock.h" +#ifndef JUCE_GZIPCOMPRESSOROUTPUTSTREAM_H_INCLUDED +#define JUCE_GZIPCOMPRESSOROUTPUTSTREAM_H_INCLUDED //============================================================================== @@ -75,11 +74,11 @@ public: */ void flush(); - int64 getPosition(); - bool setPosition (int64 newPosition); - bool write (const void* destBuffer, int howMany); + int64 getPosition() override; + bool setPosition (int64) override; + bool write (const void*, size_t) override; - /** These are preset values that can be used for the constructor's windowBits paramter. + /** These are preset values that can be used for the constructor's windowBits parameter. For more info about this, see the zlib documentation for its windowBits parameter. */ enum WindowBitsValues @@ -93,10 +92,10 @@ private: OptionalScopedPointer destStream; class GZIPCompressorHelper; - friend class ScopedPointer ; - ScopedPointer helper; + friend struct ContainerDeletePolicy; + ScopedPointer helper; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPCompressorOutputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPCompressorOutputStream) }; -#endif // __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ +#endif // JUCE_GZIPCOMPRESSOROUTPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp index ed4a7bb..71e0850 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -31,6 +34,13 @@ namespace zlibNamespace { #if JUCE_INCLUDE_ZLIB_CODE + #if JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wconversion" + #pragma clang diagnostic ignored "-Wshadow" + #pragma clang diagnostic ignored "-Wdeprecated-register" + #endif + #undef OS_CODE #undef fdopen #define ZLIB_INTERNAL @@ -55,6 +65,16 @@ namespace zlibNamespace #include "zlib/trees.c" #include "zlib/zutil.c" #undef Byte + #undef fdopen + #undef local + #undef Freq + #undef Code + #undef Dad + #undef Len + + #if JUCE_CLANG + #pragma clang diagnostic pop + #endif #else #include JUCE_ZLIB_INCLUDE_PATH #endif @@ -70,7 +90,7 @@ namespace zlibNamespace class GZIPDecompressorInputStream::GZIPDecompressHelper { public: - GZIPDecompressHelper (const bool noWrap) + GZIPDecompressHelper (const bool dontWrap) : finished (true), needsDictionary (false), error (true), @@ -80,7 +100,7 @@ public: { using namespace zlibNamespace; zerostruct (stream); - streamIsValid = (inflateInit2 (&stream, noWrap ? -MAX_WBITS : MAX_WBITS) == Z_OK); + streamIsValid = (inflateInit2 (&stream, dontWrap ? -MAX_WBITS : MAX_WBITS) == Z_OK); finished = error = ! streamIsValid; } @@ -99,7 +119,7 @@ public: dataSize = size; } - int doNextBlock (uint8* const dest, const int destSize) + int doNextBlock (uint8* const dest, const unsigned int destSize) { using namespace zlibNamespace; if (streamIsValid && data != nullptr && ! finished) @@ -146,33 +166,33 @@ private: uint8* data; size_t dataSize; - JUCE_DECLARE_NON_COPYABLE (GZIPDecompressHelper); + JUCE_DECLARE_NON_COPYABLE (GZIPDecompressHelper) }; //============================================================================== -GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* const sourceStream_, +GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream* const source, const bool deleteSourceWhenDestroyed, const bool noWrap_, const int64 uncompressedStreamLength_) - : sourceStream (sourceStream_, deleteSourceWhenDestroyed), + : sourceStream (source, deleteSourceWhenDestroyed), uncompressedStreamLength (uncompressedStreamLength_), noWrap (noWrap_), isEof (false), activeBufferSize (0), - originalSourcePos (sourceStream_->getPosition()), + originalSourcePos (source->getPosition()), currentPos (0), buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), helper (new GZIPDecompressHelper (noWrap_)) { } -GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream& sourceStream_) - : sourceStream (&sourceStream_, false), +GZIPDecompressorInputStream::GZIPDecompressorInputStream (InputStream& source) + : sourceStream (&source, false), uncompressedStreamLength (-1), noWrap (false), isEof (false), activeBufferSize (0), - originalSourcePos (sourceStream_.getPosition()), + originalSourcePos (source.getPosition()), currentPos (0), buffer ((size_t) GZIPDecompressHelper::gzipDecompBufferSize), helper (new GZIPDecompressHelper (false)) @@ -199,7 +219,7 @@ int GZIPDecompressorInputStream::read (void* destBuffer, int howMany) while (! helper->error) { - const int n = helper->doNextBlock (d, howMany); + const int n = helper->doNextBlock (d, (unsigned int) howMany); currentPos += n; if (n == 0) @@ -266,10 +286,3 @@ bool GZIPDecompressorInputStream::setPosition (int64 newPos) skipNextBytes (newPos - currentPos); return true; } - -// (This is used as a way for the zip file code to use the crc32 function without including zlib) -unsigned long juce_crc32 (unsigned long, const unsigned char*, unsigned); -unsigned long juce_crc32 (unsigned long crc, const unsigned char* buf, unsigned len) -{ - return zlibNamespace::crc32 (crc, buf, len); -} diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h index f0be470..78a0b77 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h +++ b/JuceLibraryCode/modules/juce_core/zip/juce_GZIPDecompressorInputStream.h @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ -#define __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ - -#include "../streams/juce_InputStream.h" -#include "../memory/juce_OptionalScopedPointer.h" -#include "../memory/juce_HeapBlock.h" +#ifndef JUCE_GZIPDECOMPRESSORINPUTSTREAM_H_INCLUDED +#define JUCE_GZIPDECOMPRESSORINPUTSTREAM_H_INCLUDED //============================================================================== @@ -72,15 +71,14 @@ public: ~GZIPDecompressorInputStream(); //============================================================================== - int64 getPosition(); - bool setPosition (int64 pos); - int64 getTotalLength(); - bool isExhausted(); - int read (void* destBuffer, int maxBytesToRead); + int64 getPosition() override; + bool setPosition (int64 pos) override; + int64 getTotalLength() override; + bool isExhausted() override; + int read (void* destBuffer, int maxBytesToRead) override; - - //============================================================================== private: + //============================================================================== OptionalScopedPointer sourceStream; const int64 uncompressedStreamLength; const bool noWrap; @@ -90,10 +88,10 @@ private: HeapBlock buffer; class GZIPDecompressHelper; - friend class ScopedPointer ; - ScopedPointer helper; + friend struct ContainerDeletePolicy; + ScopedPointer helper; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GZIPDecompressorInputStream) }; -#endif // __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ +#endif // JUCE_GZIPDECOMPRESSORINPUTSTREAM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp index 8affdc6..629443b 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp @@ -1,24 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ @@ -111,21 +114,21 @@ namespace class ZipFile::ZipInputStream : public InputStream { public: - ZipInputStream (ZipFile& file_, ZipFile::ZipEntryHolder& zei) - : file (file_), + ZipInputStream (ZipFile& zf, ZipFile::ZipEntryHolder& zei) + : file (zf), zipEntryHolder (zei), pos (0), headerSize (0), - inputStream (file_.inputStream) + inputStream (zf.inputStream) { - if (file_.inputSource != nullptr) + if (zf.inputSource != nullptr) { inputStream = streamToDelete = file.inputSource->createInputStream(); } else { #if JUCE_DEBUG - file_.streamCounter.numOpenStreams++; + zf.streamCounter.numOpenStreams++; #endif } @@ -206,7 +209,7 @@ private: InputStream* inputStream; ScopedPointer streamToDelete; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ZipInputStream); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ZipInputStream) }; @@ -233,9 +236,9 @@ ZipFile::ZipFile (const File& file) init(); } -ZipFile::ZipFile (InputSource* const inputSource_) +ZipFile::ZipFile (InputSource* const source) : inputStream (nullptr), - inputSource (inputSource_) + inputSource (source) { init(); } @@ -265,8 +268,10 @@ int ZipFile::getNumEntries() const noexcept const ZipFile::ZipEntry* ZipFile::getEntry (const int index) const noexcept { - ZipEntryHolder* const zei = entries [index]; - return zei != nullptr ? &(zei->entry) : nullptr; + if (ZipEntryHolder* const zei = entries [index]) + return &(zei->entry); + + return nullptr; } int ZipFile::getIndexOfFileName (const String& fileName) const noexcept @@ -285,10 +290,9 @@ const ZipFile::ZipEntry* ZipFile::getEntry (const String& fileName) const noexce InputStream* ZipFile::createStreamForEntry (const int index) { - ZipEntryHolder* const zei = entries[index]; InputStream* stream = nullptr; - if (zei != nullptr) + if (ZipEntryHolder* const zei = entries[index]) { stream = new ZipInputStream (*this, *zei); @@ -305,6 +309,15 @@ InputStream* ZipFile::createStreamForEntry (const int index) return stream; } +InputStream* ZipFile::createStreamForEntry (const ZipEntry& entry) +{ + for (int i = 0; i < entries.size(); ++i) + if (&entries.getUnchecked (i)->entry == &entry) + return createStreamForEntry (i); + + return nullptr; +} + void ZipFile::sortEntriesByFilename() { ZipEntryHolder::FileNameComparator sorter; @@ -381,9 +394,15 @@ Result ZipFile::uncompressEntry (const int index, { const ZipEntryHolder* zei = entries.getUnchecked (index); - const File targetFile (targetDirectory.getChildFile (zei->entry.filename)); + #if JUCE_WINDOWS + const String entryPath (zei->entry.filename); + #else + const String entryPath (zei->entry.filename.replaceCharacter ('\\', '/')); + #endif - if (zei->entry.filename.endsWithChar ('/')) + const File targetFile (targetDirectory.getChildFile (entryPath)); + + if (entryPath.endsWithChar ('/') || entryPath.endsWithChar ('\\')) return targetFile.createDirectory(); // (entry is a directory, not a file) ScopedPointer in (createStreamForEntry (index)); @@ -421,23 +440,19 @@ Result ZipFile::uncompressEntry (const int index, //============================================================================= -extern unsigned long juce_crc32 (unsigned long crc, const unsigned char* buf, unsigned len); - class ZipFile::Builder::Item { public: - Item (const File& file_, const int compressionLevel_, const String& storedPathName_) - : file (file_), - storedPathname (storedPathName_.isEmpty() ? file_.getFileName() : storedPathName_), - compressionLevel (compressionLevel_), - compressedSize (0), - headerStart (0) + Item (const File& f, InputStream* s, const int compression, const String& storedPath, Time time) + : file (f), stream (s), storedPathname (storedPath), + fileTime (time), compressionLevel (compression), + compressedSize (0), uncompressedSize (0), headerStart (0), checksum (0) { } bool writeData (OutputStream& target, const int64 overallStartPosition) { - MemoryOutputStream compressedData; + MemoryOutputStream compressedData ((size_t) file.getSize()); if (compressionLevel > 0) { @@ -480,65 +495,81 @@ public: private: const File file; + ScopedPointer stream; String storedPathname; - int compressionLevel, compressedSize, headerStart; + Time fileTime; + int compressionLevel, compressedSize, uncompressedSize, headerStart; unsigned long checksum; - void writeTimeAndDate (OutputStream& target) const + static void writeTimeAndDate (OutputStream& target, Time t) { - const Time t (file.getLastModificationTime()); target.writeShort ((short) (t.getSeconds() + (t.getMinutes() << 5) + (t.getHours() << 11))); target.writeShort ((short) (t.getDayOfMonth() + ((t.getMonth() + 1) << 5) + ((t.getYear() - 1980) << 9))); } bool writeSource (OutputStream& target) { + if (stream == nullptr) + { + stream = file.createInputStream(); + + if (stream == nullptr) + return false; + } + checksum = 0; - FileInputStream input (file); - - if (input.failedToOpen()) - return false; - - const int bufferSize = 2048; + uncompressedSize = 0; + const int bufferSize = 4096; HeapBlock buffer (bufferSize); - while (! input.isExhausted()) + while (! stream->isExhausted()) { - const int bytesRead = input.read (buffer, bufferSize); + const int bytesRead = stream->read (buffer, bufferSize); if (bytesRead < 0) return false; - checksum = juce_crc32 (checksum, buffer, (unsigned int) bytesRead); - target.write (buffer, bytesRead); + checksum = zlibNamespace::crc32 (checksum, buffer, (unsigned int) bytesRead); + target.write (buffer, (size_t) bytesRead); + uncompressedSize += bytesRead; } + stream = nullptr; return true; } void writeFlagsAndSizes (OutputStream& target) const { target.writeShort (10); // version needed - target.writeShort (0); // flags + target.writeShort ((short) (1 << 11)); // this flag indicates UTF-8 filename encoding target.writeShort (compressionLevel > 0 ? (short) 8 : (short) 0); - writeTimeAndDate (target); + writeTimeAndDate (target, fileTime); target.writeInt ((int) checksum); target.writeInt (compressedSize); - target.writeInt ((int) file.getSize()); + target.writeInt (uncompressedSize); target.writeShort ((short) storedPathname.toUTF8().sizeInBytes() - 1); target.writeShort (0); // extra field length } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Item); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Item) }; //============================================================================= ZipFile::Builder::Builder() {} ZipFile::Builder::~Builder() {} -void ZipFile::Builder::addFile (const File& fileToAdd, const int compressionLevel, const String& storedPathName) +void ZipFile::Builder::addFile (const File& file, const int compression, const String& path) { - items.add (new Item (fileToAdd, compressionLevel, storedPathName)); + items.add (new Item (file, nullptr, compression, + path.isEmpty() ? file.getFileName() : path, + file.getLastModificationTime())); +} + +void ZipFile::Builder::addEntry (InputStream* stream, int compression, const String& path, Time time) +{ + jassert (stream != nullptr); // must not be null! + jassert (path.isNotEmpty()); + items.add (new Item (File(), stream, compression, path, time)); } bool ZipFile::Builder::writeToStream (OutputStream& target, double* const progress) const diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h index af65a2e..ccf9064 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h +++ b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.h @@ -1,35 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission to use, copy, modify, and/or distribute this software for any purpose with + or without fee is hereby granted, provided that the above copyright notice and this + permission notice appear in all copies. - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ------------------------------------------------------------------------------ - ------------------------------------------------------------------------------ + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + For more details, visit www.juce.com ============================================================================== */ -#ifndef __JUCE_ZIPFILE_JUCEHEADER__ -#define __JUCE_ZIPFILE_JUCEHEADER__ - -#include "../files/juce_File.h" -#include "../streams/juce_InputSource.h" -#include "../threads/juce_CriticalSection.h" -#include "../containers/juce_OwnedArray.h" +#ifndef JUCE_ZIPFILE_H_INCLUDED +#define JUCE_ZIPFILE_H_INCLUDED //============================================================================== @@ -141,7 +139,7 @@ public: The stream must not be used after the ZipFile object that created has been deleted. */ - InputStream* createStreamForEntry (ZipEntry& entry); + InputStream* createStreamForEntry (const ZipEntry& entry); //============================================================================== /** Uncompresses all of the files in the zip file. @@ -198,6 +196,21 @@ public: void addFile (const File& fileToAdd, int compressionLevel, const String& storedPathName = String::empty); + /** Adds a file while should be added to the archive. + + @param streamToRead this stream isn't read immediately - a pointer to the stream is + stored, then used later when the writeToStream() method is called, and + deleted by the Builder object when no longer needed, so be very careful + about its lifetime and the lifetime of any objects on which it depends! + This must not be null. + @param compressionLevel this can be between 0 (no compression), and 9 (maximum compression). + @param storedPathName the partial pathname that will be stored for this file + @param fileModificationTime the timestamp that will be stored as the last modification time + of this entry + */ + void addEntry (InputStream* streamToRead, int compressionLevel, + const String& storedPathName, Time fileModificationTime); + /** Generates the zip file, writing it to the specified stream. If the progress parameter is non-null, it will be updated with an approximate progress status between 0 and 1.0 @@ -207,10 +220,10 @@ public: //============================================================================== private: class Item; - friend class OwnedArray; + friend struct ContainerDeletePolicy; OwnedArray items; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Builder); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Builder) }; private: @@ -240,7 +253,7 @@ private: void init(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ZipFile); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ZipFile) }; -#endif // __JUCE_ZIPFILE_JUCEHEADER__ +#endif // JUCE_ZIPFILE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_core/zip/zlib/crc32.c b/JuceLibraryCode/modules/juce_core/zip/zlib/crc32.c index 049276d..500d536 100644 --- a/JuceLibraryCode/modules/juce_core/zip/zlib/crc32.c +++ b/JuceLibraryCode/modules/juce_core/zip/zlib/crc32.c @@ -251,8 +251,8 @@ unsigned long ZEXPORT crc32 (unsigned long crc, const unsigned char FAR *buf, un /* ========================================================================= */ #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] + c = (u4) (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 /* ========================================================================= */ @@ -264,7 +264,7 @@ local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf c = (u4)crc; c = ~c; while (len && ((ptrdiff_t)buf & 3)) { - c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + c = (u4) (crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8)); len--; } @@ -280,7 +280,7 @@ local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf buf = (const unsigned char FAR *)buf4; if (len) do { - c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + c = (u4) (crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8)); } while (--len); c = ~c; return (unsigned long)c; @@ -288,8 +288,8 @@ local unsigned long crc32_little(unsigned long crc, const unsigned char FAR *buf /* ========================================================================= */ #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] + c = (u4) (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 /* ========================================================================= */ @@ -301,7 +301,7 @@ local unsigned long crc32_big (unsigned long crc, const unsigned char FAR *buf, c = REV((u4)crc); c = ~c; while (len && ((ptrdiff_t)buf & 3)) { - c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + c = (u4) (crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8)); len--; } @@ -319,7 +319,7 @@ local unsigned long crc32_big (unsigned long crc, const unsigned char FAR *buf, buf = (const unsigned char FAR *)buf4; if (len) do { - c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + c = (u4) (crc_table[4][(c >> 24) ^ (u4) *buf++] ^ (c << 8)); } while (--len); c = ~c; return (unsigned long)(REV(c)); diff --git a/JuceLibraryCode/modules/juce_core/zip/zlib/deflate.c b/JuceLibraryCode/modules/juce_core/zip/zlib/deflate.c index a31971a..4b8bda5 100644 --- a/JuceLibraryCode/modules/juce_core/zip/zlib/deflate.c +++ b/JuceLibraryCode/modules/juce_core/zip/zlib/deflate.c @@ -333,7 +333,8 @@ int ZEXPORT deflateSetDictionary (z_streamp strm, const Bytef *dictionary, uInt for (n = 0; n <= length - MIN_MATCH; n++) { INSERT_STRING(s, n, hash_head); } - if (hash_head) hash_head = 0; /* to make compiler happy */ + + (void) hash_head; /* to make compiler happy */ return Z_OK; } diff --git a/JuceLibraryCode/modules/juce_core/zip/zlib/inflate.c b/JuceLibraryCode/modules/juce_core/zip/zlib/inflate.c index f70c52b..eef0d37 100644 --- a/JuceLibraryCode/modules/juce_core/zip/zlib/inflate.c +++ b/JuceLibraryCode/modules/juce_core/zip/zlib/inflate.c @@ -131,7 +131,7 @@ int ZEXPORT inflatePrime (z_streamp strm, int bits, int value) if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; state = (struct inflate_state FAR *)strm->state; if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; - value &= (1L << bits) - 1; + value &= (1 << bits) - 1; state->hold += value << state->bits; state->bits += bits; return Z_OK; diff --git a/JuceLibraryCode/modules/juce_core/zip/zlib/trees.c b/JuceLibraryCode/modules/juce_core/zip/zlib/trees.c index 91ed766..36a124d 100644 --- a/JuceLibraryCode/modules/juce_core/zip/zlib/trees.c +++ b/JuceLibraryCode/modules/juce_core/zip/zlib/trees.c @@ -203,7 +203,7 @@ local void send_bits (deflate_state *s, int value, int length) s->bi_buf |= (value << s->bi_valid); put_short(s, s->bi_buf); s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); - s->bi_valid += length - Buf_size; + s->bi_valid += (int) (length - Buf_size); } else { s->bi_buf |= value << s->bi_valid; s->bi_valid += length; diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp index 7079802..6db825e 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h index 97aa144..21edbc2 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h @@ -1,32 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ -#define __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ +#ifndef JUCE_APPLICATIONPROPERTIES_H_INCLUDED +#define JUCE_APPLICATIONPROPERTIES_H_INCLUDED -#include "juce_PropertiesFile.h" //============================================================================== /** @@ -39,8 +37,7 @@ 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. + getUserSettings() and getCommonSettings() methods. After creating an instance of an ApplicationProperties object, you should first of all call setStorageParameters() to tell it the parameters to use to create @@ -69,6 +66,11 @@ public: */ void setStorageParameters (const PropertiesFile::Options& options); + /** Returns the current storage parameters. + @see setStorageParameters + */ + const PropertiesFile::Options& getStorageParameters() const noexcept { return options; } + //============================================================================== /** Returns the user settings file. @@ -123,8 +125,8 @@ private: void openFiles(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationProperties); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationProperties) }; -#endif // __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ +#endif // JUCE_APPLICATIONPROPERTIES_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp index e51eaf9..ece2121 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -38,6 +37,7 @@ namespace PropertyFileConstants PropertiesFile::Options::Options() : commonToAllUsers (false), ignoreCaseOfKeyNames (false), + doNotSave (false), millisecondsBeforeSaving (3000), storageFormat (PropertiesFile::storeAsXML), processLock (nullptr) @@ -49,11 +49,11 @@ File PropertiesFile::Options::getDefaultFile() const // mustn't have illegal characters in this name.. jassert (applicationName == File::createLegalFileName (applicationName)); - #if JUCE_MAC || JUCE_IOS + #if JUCE_MAC || JUCE_IOS File dir (commonToAllUsers ? "/Library/" : "~/Library/"); - if (osxLibrarySubFolder != "Preferences" && osxLibrarySubFolder != "Application Support") + if (osxLibrarySubFolder != "Preferences" && ! osxLibrarySubFolder.startsWith ("Application Support")) { /* The PropertiesFile class always used to put its settings files in "Library/Preferences", but Apple have changed their advice, and now stipulate that settings should go in "Library/Application Support". @@ -61,7 +61,8 @@ File PropertiesFile::Options::getDefaultFile() const Because older apps would be broken by a silent change in this class's behaviour, you must now explicitly set the osxLibrarySubFolder value to indicate which path you want to use. - In newer apps, you should always set this to "Application Support". + In newer apps, you should always set this to "Application Support" + or "Application Support/YourSubFolderName". If your app needs to load settings files that were created by older versions of juce and you want to maintain backwards-compatibility, then you can set this to "Preferences". @@ -81,21 +82,21 @@ File PropertiesFile::Options::getDefaultFile() const if (folderName.isNotEmpty()) dir = dir.getChildFile (folderName); - #elif JUCE_LINUX || JUCE_ANDROID - const File dir ((commonToAllUsers ? "/var/" : "~/") - + (folderName.isNotEmpty() ? folderName - : ("." + applicationName))); + #elif JUCE_LINUX || JUCE_ANDROID + const File dir (File (commonToAllUsers ? "/var" : "~") + .getChildFile (folderName.isNotEmpty() ? folderName + : ("." + applicationName))); - #elif JUCE_WINDOWS + #elif JUCE_WINDOWS File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory : File::userApplicationDataDirectory)); - if (dir == File::nonexistent) - return File::nonexistent; + if (dir == File()) + return File(); dir = dir.getChildFile (folderName.isNotEmpty() ? folderName : applicationName); - #endif + #endif return dir.getChildFile (applicationName) .withFileExtension (filenameSuffix); @@ -103,108 +104,36 @@ File PropertiesFile::Options::getDefaultFile() const //============================================================================== -PropertiesFile::PropertiesFile (const File& file_, const Options& options_) - : PropertySet (options_.ignoreCaseOfKeyNames), - file (file_), options (options_), +PropertiesFile::PropertiesFile (const File& f, const Options& o) + : PropertySet (o.ignoreCaseOfKeyNames), + file (f), options (o), loadedOk (false), needsWriting (false) { - initialise(); + reload(); } -PropertiesFile::PropertiesFile (const Options& options_) - : PropertySet (options_.ignoreCaseOfKeyNames), - file (options_.getDefaultFile()), options (options_), +PropertiesFile::PropertiesFile (const Options& o) + : PropertySet (o.ignoreCaseOfKeyNames), + file (o.getDefaultFile()), options (o), loadedOk (false), needsWriting (false) { - initialise(); + reload(); } -void PropertiesFile::initialise() +bool PropertiesFile::reload() { - // You need to correctly specify just one storage format for the file ProcessScopedLock pl (createProcessLock()); if (pl != nullptr && ! pl->isLocked()) - return; // locking failure.. + return false; // locking failure.. - ScopedPointer fileStream (file.createInputStream()); - - if (fileStream != nullptr) - { - int magicNumber = fileStream->readInt(); - - if (magicNumber == PropertyFileConstants::magicNumberCompressed) - { - fileStream = new GZIPDecompressorInputStream (new SubregionStream (fileStream.release(), 4, -1, true), true); - magicNumber = PropertyFileConstants::magicNumber; - } - - if (magicNumber == PropertyFileConstants::magicNumber) - { - loadedOk = true; - BufferedInputStream in (fileStream.release(), 2048, true); - - int numValues = in.readInt(); - - while (--numValues >= 0 && ! in.isExhausted()) - { - const String key (in.readString()); - const String value (in.readString()); - - jassert (key.isNotEmpty()); - if (key.isNotEmpty()) - getAllProperties().set (key, value); - } - } - else - { - // Not a binary props file - let's see if it's XML.. - fileStream = nullptr; - - XmlDocument parser (file); - ScopedPointer doc (parser.getDocumentElement (true)); - - if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag)) - { - doc = parser.getDocumentElement(); - - if (doc != nullptr) - { - loadedOk = true; - - forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) - { - const String name (e->getStringAttribute (PropertyFileConstants::nameAttribute)); - - if (name.isNotEmpty()) - { - getAllProperties().set (name, - e->getFirstChildElement() != nullptr - ? e->getFirstChildElement()->createDocument (String::empty, true) - : e->getStringAttribute (PropertyFileConstants::valueAttribute)); - } - } - } - else - { - // must be a pretty broken XML file we're trying to parse here, - // or a sign that this object needs an InterProcessLock, - // or just a failure reading the file. This last reason is why - // we don't jassertfalse here. - } - } - } - } - else - { - loadedOk = ! file.exists(); - } + loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml(); + return loadedOk; } PropertiesFile::~PropertiesFile() { - if (! saveIfNeeded()) - jassertfalse; + saveIfNeeded(); } InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const @@ -236,87 +165,174 @@ bool PropertiesFile::save() stopTimer(); - if (file == File::nonexistent + if (options.doNotSave + || file == File() || file.isDirectory() || ! file.getParentDirectory().createDirectory()) return false; if (options.storageFormat == storeAsXML) + return saveAsXml(); + + return saveAsBinary(); +} + +bool PropertiesFile::loadAsXml() +{ + XmlDocument parser (file); + ScopedPointer doc (parser.getDocumentElement (true)); + + if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag)) { - XmlElement doc (PropertyFileConstants::fileTag); + doc = parser.getDocumentElement(); - for (int i = 0; i < getAllProperties().size(); ++i) + if (doc != nullptr) { - XmlElement* const e = doc.createNewChildElement (PropertyFileConstants::valueTag); - e->setAttribute (PropertyFileConstants::nameAttribute, getAllProperties().getAllKeys() [i]); + forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag) + { + const String name (e->getStringAttribute (PropertyFileConstants::nameAttribute)); - // if the value seems to contain xml, store it as such.. - XmlElement* const childElement = XmlDocument::parse (getAllProperties().getAllValues() [i]); + if (name.isNotEmpty()) + { + getAllProperties().set (name, + e->getFirstChildElement() != nullptr + ? e->getFirstChildElement()->createDocument ("", true) + : e->getStringAttribute (PropertyFileConstants::valueAttribute)); + } + } - if (childElement != nullptr) - e->addChildElement (childElement); - else - e->setAttribute (PropertyFileConstants::valueAttribute, - getAllProperties().getAllValues() [i]); + return true; } - ProcessScopedLock pl (createProcessLock()); + // must be a pretty broken XML file we're trying to parse here, + // or a sign that this object needs an InterProcessLock, + // or just a failure reading the file. This last reason is why + // we don't jassertfalse here. + } - if (pl != nullptr && ! pl->isLocked()) - return false; // locking failure.. + return false; +} - if (doc.writeToFile (file, String::empty)) +bool PropertiesFile::saveAsXml() +{ + XmlElement doc (PropertyFileConstants::fileTag); + const StringPairArray& props = getAllProperties(); + + for (int i = 0; i < props.size(); ++i) + { + XmlElement* const e = doc.createNewChildElement (PropertyFileConstants::valueTag); + e->setAttribute (PropertyFileConstants::nameAttribute, props.getAllKeys() [i]); + + // if the value seems to contain xml, store it as such.. + if (XmlElement* const childElement = XmlDocument::parse (props.getAllValues() [i])) + e->addChildElement (childElement); + else + e->setAttribute (PropertyFileConstants::valueAttribute, props.getAllValues() [i]); + } + + ProcessScopedLock pl (createProcessLock()); + + if (pl != nullptr && ! pl->isLocked()) + return false; // locking failure.. + + if (doc.writeToFile (file, String())) + { + needsWriting = false; + return true; + } + + return false; +} + +bool PropertiesFile::loadAsBinary() +{ + FileInputStream fileStream (file); + + if (fileStream.openedOk()) + { + const int magicNumber = fileStream.readInt(); + + if (magicNumber == PropertyFileConstants::magicNumberCompressed) + { + SubregionStream subStream (&fileStream, 4, -1, false); + GZIPDecompressorInputStream gzip (subStream); + return loadAsBinary (gzip); + } + + if (magicNumber == PropertyFileConstants::magicNumber) + return loadAsBinary (fileStream); + } + + return false; +} + +bool PropertiesFile::loadAsBinary (InputStream& input) +{ + BufferedInputStream in (input, 2048); + + int numValues = in.readInt(); + + while (--numValues >= 0 && ! in.isExhausted()) + { + const String key (in.readString()); + const String value (in.readString()); + + jassert (key.isNotEmpty()); + if (key.isNotEmpty()) + getAllProperties().set (key, value); + } + + return true; +} + +bool PropertiesFile::saveAsBinary() +{ + ProcessScopedLock pl (createProcessLock()); + + if (pl != nullptr && ! pl->isLocked()) + return false; // locking failure.. + + TemporaryFile tempFile (file); + ScopedPointer out (tempFile.getFile().createOutputStream()); + + if (out != nullptr) + { + if (options.storageFormat == storeAsCompressedBinary) + { + out->writeInt (PropertyFileConstants::magicNumberCompressed); + out->flush(); + + out = new GZIPCompressorOutputStream (out.release(), 9, true); + } + else + { + // have you set up the storage option flags correctly? + jassert (options.storageFormat == storeAsBinary); + + out->writeInt (PropertyFileConstants::magicNumber); + } + + const StringPairArray& props = getAllProperties(); + const int numProperties = props.size(); + const StringArray& keys = props.getAllKeys(); + const StringArray& values = props.getAllValues(); + + out->writeInt (numProperties); + + for (int i = 0; i < numProperties; ++i) + { + out->writeString (keys[i]); + out->writeString (values[i]); + } + + out = nullptr; + + if (tempFile.overwriteTargetFileWithTemporary()) { needsWriting = false; return true; } } - else - { - ProcessScopedLock pl (createProcessLock()); - - if (pl != nullptr && ! pl->isLocked()) - return false; // locking failure.. - - TemporaryFile tempFile (file); - ScopedPointer out (tempFile.getFile().createOutputStream()); - - if (out != nullptr) - { - if (options.storageFormat == storeAsCompressedBinary) - { - out->writeInt (PropertyFileConstants::magicNumberCompressed); - out->flush(); - - out = new GZIPCompressorOutputStream (out.release(), 9, true); - } - else - { - // have you set up the storage option flags correctly? - jassert (options.storageFormat == storeAsBinary); - - out->writeInt (PropertyFileConstants::magicNumber); - } - - const int numProperties = getAllProperties().size(); - - out->writeInt (numProperties); - - for (int i = 0; i < numProperties; ++i) - { - out->writeString (getAllProperties().getAllKeys() [i]); - out->writeString (getAllProperties().getAllValues() [i]); - } - - out = nullptr; - - if (tempFile.overwriteTargetFileWithTemporary()) - { - needsWriting = false; - return true; - } - } - } return false; } diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h index 8df6ecd..f3d9551 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PROPERTIESFILE_JUCEHEADER__ -#define __JUCE_PROPERTIESFILE_JUCEHEADER__ +#ifndef JUCE_PROPERTIESFILE_H_INCLUDED +#define JUCE_PROPERTIESFILE_H_INCLUDED //============================================================================== @@ -58,7 +57,7 @@ public: struct JUCE_API Options { /** Creates an empty Options structure. - You'll need to fill-in the data memebers appropriately before using this structure. + You'll need to fill-in the data members appropriately before using this structure. */ Options(); @@ -86,7 +85,8 @@ public: Because older apps would be broken by a silent change in this class's behaviour, you must now explicitly set the osxLibrarySubFolder value to indicate which path you want to use. - In newer apps, you should always set this to "Application Support". + In newer apps, you should always set this to "Application Support" or + "Application Support/YourSubFolderName". If your app needs to load settings files that were created by older versions of juce and you want to maintain backwards-compatibility, then you can set this to "Preferences". @@ -107,6 +107,9 @@ public: */ bool ignoreCaseOfKeyNames; + /** If set to true, this prevents the file from being written to disk. */ + bool doNotSave; + /** 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 this zero, the file will be written to disk immediately on being changed (which might be slow, as it'll re-write @@ -142,7 +145,7 @@ public: C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[filenameSuffix] On Linux it'll return - ~/.[folderName]/[applicationName].[filenameSuffix] + ~/[folderName]/[applicationName].[filenameSuffix] If the folderName variable is empty, it'll use the app name for this (or omit the folder name on the Mac). @@ -210,9 +213,12 @@ public: */ void setNeedsToBeSaved (bool needsToBeSaved); + /** Attempts to reload the settings from the file. */ + bool reload(); + //============================================================================== /** Returns the file that's being used. */ - File getFile() const { return file; } + const File& getFile() const noexcept { return file; } protected: @@ -228,10 +234,14 @@ private: typedef const ScopedPointer ProcessScopedLock; InterProcessLock::ScopedLockType* createProcessLock() const; - void timerCallback(); - void initialise(); + void timerCallback() override; + bool saveAsXml(); + bool saveAsBinary(); + bool loadAsXml(); + bool loadAsBinary(); + bool loadAsBinary (InputStream&); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile) }; -#endif // __JUCE_PROPERTIESFILE_JUCEHEADER__ +#endif // JUCE_PROPERTIESFILE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp index f59335e..fd1d12a 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_DATA_STRUCTURES_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_DATA_STRUCTURES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -41,12 +40,10 @@ namespace juce { -// START_AUTOINCLUDE values/*.cpp, undomanager/*.cpp, app_properties/*.cpp #include "values/juce_Value.cpp" #include "values/juce_ValueTree.cpp" #include "undomanager/juce_UndoManager.cpp" #include "app_properties/juce_ApplicationProperties.cpp" #include "app_properties/juce_PropertiesFile.cpp" -// END_AUTOINCLUDE } diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h index 7ee8233..629b88d 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DATA_STRUCTURES_JUCEHEADER__ -#define __JUCE_DATA_STRUCTURES_JUCEHEADER__ +#ifndef JUCE_DATA_STRUCTURES_H_INCLUDED +#define JUCE_DATA_STRUCTURES_H_INCLUDED //============================================================================= #include "../juce_events/juce_events.h" @@ -32,27 +31,13 @@ namespace juce { -// START_AUTOINCLUDE values, undomanager, app_properties -#ifndef __JUCE_VALUE_JUCEHEADER__ - #include "values/juce_Value.h" -#endif -#ifndef __JUCE_VALUETREE_JUCEHEADER__ - #include "values/juce_ValueTree.h" -#endif -#ifndef __JUCE_UNDOABLEACTION_JUCEHEADER__ - #include "undomanager/juce_UndoableAction.h" -#endif -#ifndef __JUCE_UNDOMANAGER_JUCEHEADER__ - #include "undomanager/juce_UndoManager.h" -#endif -#ifndef __JUCE_APPLICATIONPROPERTIES_JUCEHEADER__ - #include "app_properties/juce_ApplicationProperties.h" -#endif -#ifndef __JUCE_PROPERTIESFILE_JUCEHEADER__ - #include "app_properties/juce_PropertiesFile.h" -#endif -// END_AUTOINCLUDE +#include "undomanager/juce_UndoableAction.h" +#include "undomanager/juce_UndoManager.h" +#include "values/juce_Value.h" +#include "values/juce_ValueTree.h" +#include "app_properties/juce_PropertiesFile.h" +#include "app_properties/juce_ApplicationProperties.h" } -#endif // __JUCE_DATA_STRUCTURES_JUCEHEADER__ +#endif // JUCE_DATA_STRUCTURES_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm index 0e3fd0e..2eb4fb4 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm +++ b/JuceLibraryCode/modules/juce_data_structures/juce_data_structures.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_module_info b/JuceLibraryCode/modules/juce_data_structures/juce_module_info index e11e931..064f14c 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_module_info +++ b/JuceLibraryCode/modules/juce_data_structures/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_data_structures", "name": "JUCE data model helper classes", - "version": "2.0.21", + "version": "3.0.6", "description": "Classes for undo/redo management, and smart data structures.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp index 59fee38..3557b90 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp @@ -1,28 +1,68 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +struct UndoManager::ActionSet +{ + ActionSet (const String& transactionName) + : name (transactionName), + time (Time::getCurrentTime()) + {} + + OwnedArray actions; + String name; + Time time; + + bool perform() const + { + for (int i = 0; i < actions.size(); ++i) + if (! actions.getUnchecked(i)->perform()) + return false; + + return true; + } + + bool undo() const + { + for (int i = actions.size(); --i >= 0;) + if (! actions.getUnchecked(i)->undo()) + return false; + + return true; + } + + int getTotalSize() const + { + int total = 0; + + for (int i = actions.size(); --i >= 0;) + total += actions.getUnchecked(i)->getSizeInUnits(); + + return total; + } +}; + +//============================================================================== UndoManager::UndoManager (const int maxNumberOfUnitsToKeep, const int minimumTransactions) : totalUnitsStored (0), @@ -36,14 +76,12 @@ UndoManager::UndoManager (const int maxNumberOfUnitsToKeep, UndoManager::~UndoManager() { - clearUndoHistory(); } //============================================================================== void UndoManager::clearUndoHistory() { transactions.clear(); - transactionNames.clear(); totalUnitsStored = 0; nextIndex = 0; sendChangeMessage(); @@ -62,83 +100,51 @@ void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep, } //============================================================================== -bool UndoManager::perform (UndoableAction* const command_, const String& actionName) +bool UndoManager::perform (UndoableAction* const newAction, const String& actionName) { - if (command_ != nullptr) + if (newAction != nullptr) { - ScopedPointer command (command_); + ScopedPointer action (newAction); + + if (reentrancyCheck) + { + jassertfalse; // don't call perform() recursively from the UndoableAction::perform() + // or undo() methods, or else these actions will be discarded! + return false; + } if (actionName.isNotEmpty()) currentTransactionName = actionName; - if (reentrancyCheck) + if (action->perform()) { - jassertfalse; // don't call perform() recursively from the UndoableAction::perform() or - // undo() methods, or else these actions won't actually get done. + ActionSet* actionSet = getCurrentSet(); - return false; - } - else if (command->perform()) - { - OwnedArray* commandSet = transactions [nextIndex - 1]; - - if (commandSet != nullptr && ! newTransaction) + if (actionSet != nullptr && ! newTransaction) { - UndoableAction* lastAction = commandSet->getLast(); - - if (lastAction != nullptr) + if (UndoableAction* const lastAction = actionSet->actions.getLast()) { - UndoableAction* coalescedAction = lastAction->createCoalescedAction (command); - - if (coalescedAction != nullptr) + if (UndoableAction* const coalescedAction = lastAction->createCoalescedAction (action)) { - command = coalescedAction; + action = coalescedAction; totalUnitsStored -= lastAction->getSizeInUnits(); - commandSet->removeLast(); + actionSet->actions.removeLast(); } } } else { - commandSet = new OwnedArray(); - transactions.insert (nextIndex, commandSet); - transactionNames.insert (nextIndex, currentTransactionName); + actionSet = new ActionSet (currentTransactionName); + transactions.insert (nextIndex, actionSet); ++nextIndex; } - totalUnitsStored += command->getSizeInUnits(); - commandSet->add (command.release()); + totalUnitsStored += action->getSizeInUnits(); + actionSet->actions.add (action.release()); newTransaction = false; - while (nextIndex < transactions.size()) - { - const OwnedArray * const lastSet = transactions.getLast(); - - for (int i = lastSet->size(); --i >= 0;) - totalUnitsStored -= lastSet->getUnchecked (i)->getSizeInUnits(); - - transactions.removeLast(); - transactionNames.remove (transactionNames.size() - 1); - } - - while (nextIndex > 0 - && totalUnitsStored > maxNumUnitsToKeep - && transactions.size() > minimumTransactionsToKeep) - { - const OwnedArray * const firstSet = transactions.getFirst(); - - for (int i = firstSet->size(); --i >= 0;) - totalUnitsStored -= firstSet->getUnchecked (i)->getSizeInUnits(); - - jassert (totalUnitsStored >= 0); // something fishy going on if this fails! - - transactions.remove (0); - transactionNames.remove (0); - --nextIndex; - } - + clearFutureTransactions(); sendChangeMessage(); - return true; } } @@ -146,6 +152,28 @@ bool UndoManager::perform (UndoableAction* const command_, const String& actionN return false; } +void UndoManager::clearFutureTransactions() +{ + while (nextIndex < transactions.size()) + { + totalUnitsStored -= transactions.getLast()->getTotalSize(); + transactions.removeLast(); + } + + while (nextIndex > 0 + && totalUnitsStored > maxNumUnitsToKeep + && transactions.size() > minimumTransactionsToKeep) + { + totalUnitsStored -= transactions.getFirst()->getTotalSize(); + transactions.remove (0); + --nextIndex; + + // if this fails, then some actions may not be returning + // consistent results from their getSizeInUnits() method + jassert (totalUnitsStored >= 0); + } +} + void UndoManager::beginNewTransaction (const String& actionName) { newTransaction = true; @@ -158,92 +186,80 @@ void UndoManager::setCurrentTransactionName (const String& newName) } //============================================================================== -bool UndoManager::canUndo() const -{ - return nextIndex > 0; -} +UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; } +UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; } -bool UndoManager::canRedo() const -{ - return nextIndex < transactions.size(); -} - -String UndoManager::getUndoDescription() const -{ - return transactionNames [nextIndex - 1]; -} - -String UndoManager::getRedoDescription() const -{ - return transactionNames [nextIndex]; -} +bool UndoManager::canUndo() const { return getCurrentSet() != nullptr; } +bool UndoManager::canRedo() const { return getNextSet() != nullptr; } bool UndoManager::undo() { - const OwnedArray* const commandSet = transactions [nextIndex - 1]; - - if (commandSet == nullptr) - return false; - - bool failed = false; - + if (const ActionSet* const s = getCurrentSet()) { const ScopedValueSetter setter (reentrancyCheck, true); - for (int i = commandSet->size(); --i >= 0;) - { - if (! commandSet->getUnchecked(i)->undo()) - { - jassertfalse; - failed = true; - break; - } - } + if (s->undo()) + --nextIndex; + else + clearUndoHistory(); + + beginNewTransaction(); + sendChangeMessage(); + return true; } - if (failed) - clearUndoHistory(); - else - --nextIndex; - - beginNewTransaction(); - - sendChangeMessage(); - return true; + return false; } bool UndoManager::redo() { - const OwnedArray* const commandSet = transactions [nextIndex]; - - if (commandSet == nullptr) - return false; - - bool failed = false; - + if (const ActionSet* const s = getNextSet()) { const ScopedValueSetter setter (reentrancyCheck, true); - for (int i = 0; i < commandSet->size(); ++i) - { - if (! commandSet->getUnchecked(i)->perform()) - { - jassertfalse; - failed = true; - break; - } - } + if (s->perform()) + ++nextIndex; + else + clearUndoHistory(); + + beginNewTransaction(); + sendChangeMessage(); + return true; } - if (failed) - clearUndoHistory(); - else - ++nextIndex; + return false; +} - beginNewTransaction(); +String UndoManager::getUndoDescription() const +{ + if (const ActionSet* const s = getCurrentSet()) + return s->name; - sendChangeMessage(); - return true; + return String(); +} + +String UndoManager::getRedoDescription() const +{ + if (const ActionSet* const s = getNextSet()) + return s->name; + + return String(); +} + +Time UndoManager::getTimeOfUndoTransaction() const +{ + if (const ActionSet* const s = getCurrentSet()) + return s->time; + + return Time(); +} + +Time UndoManager::getTimeOfRedoTransaction() const +{ + if (const ActionSet* const s = getNextSet()) + return s->time; + + return Time::getCurrentTime(); } bool UndoManager::undoCurrentTransactionOnly() @@ -253,21 +269,17 @@ bool UndoManager::undoCurrentTransactionOnly() void UndoManager::getActionsInCurrentTransaction (Array & actionsFound) const { - const OwnedArray * const commandSet = transactions [nextIndex - 1]; - - if (commandSet != nullptr && ! newTransaction) - { - for (int i = 0; i < commandSet->size(); ++i) - actionsFound.add (commandSet->getUnchecked(i)); - } + if (! newTransaction) + if (const ActionSet* const s = getCurrentSet()) + for (int i = 0; i < s->actions.size(); ++i) + actionsFound.add (s->actions.getUnchecked(i)); } int UndoManager::getNumActionsInCurrentTransaction() const { - const OwnedArray * const commandSet = transactions [nextIndex - 1]; - - if (commandSet != nullptr && ! newTransaction) - return commandSet->size(); + if (! newTransaction) + if (const ActionSet* const s = getCurrentSet()) + return s->actions.size(); return 0; } diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h index 9077551..eee9cbc 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_UNDOMANAGER_JUCEHEADER__ -#define __JUCE_UNDOMANAGER_JUCEHEADER__ - -#include "juce_UndoableAction.h" +#ifndef JUCE_UNDOMANAGER_H_INCLUDED +#define JUCE_UNDOMANAGER_H_INCLUDED //============================================================================== @@ -77,7 +74,6 @@ public: void clearUndoHistory(); /** Returns the current amount of space to use for storing UndoableAction objects. - @see setMaxNumberOfStoredUnits */ int getNumberOfUnitsTakenUpByStoredCommands() const; @@ -111,7 +107,7 @@ public: @see beginNewTransaction */ bool perform (UndoableAction* action, - const String& actionName = String::empty); + const String& actionName = String()); /** Starts a new group of actions that together will be treated as a single transaction. @@ -122,7 +118,7 @@ public: @param actionName a description of the transaction that is about to be performed */ - void beginNewTransaction (const String& actionName = String::empty); + void beginNewTransaction (const String& actionName = String()); /** Changes the name stored for the current transaction. @@ -134,7 +130,6 @@ public: //============================================================================== /** Returns true if there's at least one action in the list to undo. - @see getUndoDescription, undo, canRedo */ bool canUndo() const; @@ -149,7 +144,6 @@ public: 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 */ @@ -186,15 +180,23 @@ public: */ int getNumActionsInCurrentTransaction() const; + /** Returns the time to which the state would be restored if undo() was to be called. + If an undo isn't currently possible, it'll return Time(). + */ + Time getTimeOfUndoTransaction() const; + + /** Returns the time to which the state would be restored if redo() was to be called. + If a redo isn't currently possible, it'll return Time::getCurrentTime(). + */ + Time getTimeOfRedoTransaction() 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. @@ -203,23 +205,26 @@ public: 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 + @returns true if the transaction can be redone, and false if it fails, or + if there aren't any transactions to redo */ bool redo(); private: //============================================================================== - OwnedArray > transactions; - StringArray transactionNames; + struct ActionSet; + friend struct ContainerDeletePolicy; + OwnedArray transactions; String currentTransactionName; int totalUnitsStored, maxNumUnitsToKeep, minimumTransactionsToKeep, nextIndex; bool newTransaction, reentrancyCheck; + ActionSet* getCurrentSet() const noexcept; + ActionSet* getNextSet() const noexcept; + void clearFutureTransactions(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UndoManager); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UndoManager) }; -#endif // __JUCE_UNDOMANAGER_JUCEHEADER__ +#endif // JUCE_UNDOMANAGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h index a3c731a..866d488 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoableAction.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_UNDOABLEACTION_JUCEHEADER__ -#define __JUCE_UNDOABLEACTION_JUCEHEADER__ +#ifndef JUCE_UNDOABLEACTION_H_INCLUDED +#define JUCE_UNDOABLEACTION_H_INCLUDED //============================================================================== @@ -97,4 +96,4 @@ public: }; -#endif // __JUCE_UNDOABLEACTION_JUCEHEADER__ +#endif // JUCE_UNDOABLEACTION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp index acbc24c..dee3a47 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -29,28 +28,7 @@ Value::ValueSource::ValueSource() Value::ValueSource::~ValueSource() { -} - -void Value::ValueSource::sendChangeMessage (const bool synchronous) -{ - if (synchronous) - { - // (hold a local reference to this object in case it's freed during the callbacks) - const ReferenceCountedObjectPtr localRef (this); - - for (int i = valuesWithListeners.size(); --i >= 0;) - { - Value* const v = valuesWithListeners[i]; - - if (v != nullptr) - v->callListeners(); - } - } - else - { - if (valuesWithListeners.size() > 0) - triggerAsyncUpdate(); - } + cancelPendingUpdate(); } void Value::ValueSource::handleAsyncUpdate() @@ -58,6 +36,29 @@ void Value::ValueSource::handleAsyncUpdate() sendChangeMessage (true); } +void Value::ValueSource::sendChangeMessage (const bool synchronous) +{ + const int numListeners = valuesWithListeners.size(); + + if (numListeners > 0) + { + if (synchronous) + { + const ReferenceCountedObjectPtr localRef (this); + + cancelPendingUpdate(); + + for (int i = numListeners; --i >= 0;) + if (Value* const v = valuesWithListeners[i]) + v->callListeners(); + } + else + { + triggerAsyncUpdate(); + } + } +} + //============================================================================== class SimpleValueSource : public Value::ValueSource { @@ -88,7 +89,7 @@ public: private: var value; - JUCE_DECLARE_NON_COPYABLE (SimpleValueSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleValueSource) }; @@ -98,10 +99,10 @@ Value::Value() { } -Value::Value (ValueSource* const value_) - : value (value_) +Value::Value (ValueSource* const v) + : value (v) { - jassert (value_ != nullptr); + jassert (v != nullptr); } Value::Value (const var& initialValue) @@ -122,13 +123,13 @@ Value& Value::operator= (const Value& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Value::Value (Value&& other) noexcept - : value (static_cast &&> (other.value)) + : value (static_cast&&> (other.value)) { } Value& Value::operator= (Value&& other) noexcept { - value = static_cast &&> (other.value); + value = static_cast&&> (other.value); return *this; } #endif @@ -218,8 +219,11 @@ void Value::removeListener (ValueListener* const listener) void Value::callListeners() { - Value v (*this); // (create a copy in case this gets deleted by a callback) - listeners.call (&ValueListener::valueChanged, v); + if (listeners.size() > 0) + { + Value v (*this); // (create a copy in case this gets deleted by a callback) + listeners.call (&ValueListener::valueChanged, v); + } } OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const Value& value) diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h index 6fcd590..d67c173 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_Value.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_VALUE_JUCEHEADER__ -#define __JUCE_VALUE_JUCEHEADER__ +#ifndef JUCE_VALUE_H_INCLUDED +#define JUCE_VALUE_H_INCLUDED //============================================================================== @@ -76,8 +75,7 @@ public: operator var() const; /** Returns the value as a string. - - This is alternative to writing things like "myValue.getValue().toString()". + This is a shortcut for "myValue.getValue().toString()". */ String toString() const; @@ -169,8 +167,8 @@ public: 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 SingleThreadedReferenceCountedObject, - public AsyncUpdater + class JUCE_API ValueSource : public ReferenceCountedObject, + private AsyncUpdater { public: ValueSource(); @@ -195,11 +193,12 @@ public: protected: //============================================================================== friend class Value; - SortedSet valuesWithListeners; + SortedSet valuesWithListeners; - void handleAsyncUpdate(); + private: + void handleAsyncUpdate() override; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource) }; @@ -214,8 +213,8 @@ public: private: //============================================================================== friend class ValueSource; - ReferenceCountedObjectPtr value; - ListenerList listeners; + ReferenceCountedObjectPtr value; + ListenerList listeners; void callListeners(); @@ -230,4 +229,4 @@ OutputStream& JUCE_CALLTYPE operator<< (OutputStream&, const Value&); /** This typedef is just for compatibility with old code - newer code should use the Value::Listener class directly. */ typedef Value::Listener ValueListener; -#endif // __JUCE_VALUE_JUCEHEADER__ +#endif // JUCE_VALUE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp index 3afff8a..f776224 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -28,13 +27,14 @@ class ValueTree::SharedObject : public ReferenceCountedObject public: typedef ReferenceCountedObjectPtr Ptr; - explicit SharedObject (const Identifier& type_) noexcept - : type (type_), parent (nullptr) + explicit SharedObject (Identifier t) noexcept + : type (t), parent (nullptr) { } SharedObject (const SharedObject& other) - : type (other.type), properties (other.properties), parent (nullptr) + : ReferenceCountedObject(), + type (other.type), properties (other.properties), parent (nullptr) { for (int i = 0; i < other.children.size(); ++i) { @@ -57,32 +57,64 @@ public: } } - void sendPropertyChangeMessage (ValueTree& tree, const Identifier& property) + template + void callListeners (Method method, ValueTree& tree) const { - for (int i = valueTreesWithListeners.size(); --i >= 0;) + const int numListeners = valueTreesWithListeners.size(); + + if (numListeners > 0) { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) - v->listeners.call (&ValueTree::Listener::valueTreePropertyChanged, tree, property); + if (numListeners == 1) + { + valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree); + } + else + { + const SortedSet listenersCopy (valueTreesWithListeners); + + for (int i = 0; i < numListeners; ++i) + { + ValueTree* const v = listenersCopy.getUnchecked(i); + + if (i == 0 || valueTreesWithListeners.contains (v)) + v->listeners.call (method, tree); + } + } } } - void sendPropertyChangeMessage (const Identifier& property) + template + void callListeners (Method method, ValueTree& tree, ParamType& param2) const + { + const int numListeners = valueTreesWithListeners.size(); + + if (numListeners > 0) + { + if (numListeners == 1) + { + valueTreesWithListeners.getUnchecked(0)->listeners.call (method, tree, param2); + } + else + { + const SortedSet listenersCopy (valueTreesWithListeners); + + for (int i = 0; i < numListeners; ++i) + { + ValueTree* const v = listenersCopy.getUnchecked(i); + + if (i == 0 || valueTreesWithListeners.contains (v)) + v->listeners.call (method, tree, param2); + } + } + } + } + + void sendPropertyChangeMessage (const Identifier property) { ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) - t->sendPropertyChangeMessage (tree, property); - } - - void sendChildAddedMessage (ValueTree& tree, ValueTree& child) - { - for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) - v->listeners.call (&ValueTree::Listener::valueTreeChildAdded, tree, child); - } + t->callListeners (&ValueTree::Listener::valueTreePropertyChanged, tree, property); } void sendChildAddedMessage (ValueTree child) @@ -90,17 +122,7 @@ public: ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) - t->sendChildAddedMessage (tree, child); - } - - void sendChildRemovedMessage (ValueTree& tree, ValueTree& child) - { - for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) - v->listeners.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child); - } + t->callListeners (&ValueTree::Listener::valueTreeChildAdded, tree, child); } void sendChildRemovedMessage (ValueTree child) @@ -108,17 +130,7 @@ public: ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) - t->sendChildRemovedMessage (tree, child); - } - - void sendChildOrderChangedMessage (ValueTree& tree) - { - for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) - v->listeners.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree); - } + t->callListeners (&ValueTree::Listener::valueTreeChildRemoved, tree, child); } void sendChildOrderChangedMessage() @@ -126,7 +138,7 @@ public: ValueTree tree (this); for (ValueTree::SharedObject* t = this; t != nullptr; t = t->parent) - t->sendChildOrderChangedMessage (tree); + t->callListeners (&ValueTree::Listener::valueTreeChildOrderChanged, tree); } void sendParentChangeMessage() @@ -134,31 +146,13 @@ public: ValueTree tree (this); for (int j = children.size(); --j >= 0;) - { - SharedObject* const child = children.getObjectPointer (j); - if (child != nullptr) + if (SharedObject* const child = children.getObjectPointer (j)) child->sendParentChangeMessage(); - } - for (int i = valueTreesWithListeners.size(); --i >= 0;) - { - ValueTree* const v = valueTreesWithListeners[i]; - if (v != nullptr) - v->listeners.call (&ValueTree::Listener::valueTreeParentChanged, tree); - } + callListeners (&ValueTree::Listener::valueTreeParentChanged, tree); } - const var& getProperty (const Identifier& name) const noexcept - { - return properties [name]; - } - - var getProperty (const Identifier& name, const var& defaultReturnValue) const - { - return properties.getWithDefault (name, defaultReturnValue); - } - - void setProperty (const Identifier& name, const var& newValue, UndoManager* const undoManager) + void setProperty (const Identifier name, const var& newValue, UndoManager* const undoManager) { if (undoManager == nullptr) { @@ -167,26 +161,24 @@ public: } else { - const var* const existingValue = properties.getVarPointer (name); - - if (existingValue != nullptr) + if (const var* const existingValue = properties.getVarPointer (name)) { if (*existingValue != newValue) undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false)); } else { - undoManager->perform (new SetPropertyAction (this, name, newValue, var::null, true, false)); + undoManager->perform (new SetPropertyAction (this, name, newValue, var(), true, false)); } } } - bool hasProperty (const Identifier& name) const noexcept + bool hasProperty (const Identifier name) const noexcept { return properties.contains (name); } - void removeProperty (const Identifier& name, UndoManager* const undoManager) + void removeProperty (const Identifier name, UndoManager* const undoManager) { if (undoManager == nullptr) { @@ -196,7 +188,7 @@ public: else { if (properties.contains (name)) - undoManager->perform (new SetPropertyAction (this, name, var::null, properties [name], false, true)); + undoManager->perform (new SetPropertyAction (this, name, var(), properties [name], false, true)); } } @@ -214,12 +206,22 @@ public: else { for (int i = properties.size(); --i >= 0;) - undoManager->perform (new SetPropertyAction (this, properties.getName(i), var::null, + undoManager->perform (new SetPropertyAction (this, properties.getName(i), var(), properties.getValueAt(i), false, true)); } } - ValueTree getChildWithName (const Identifier& typeToMatch) const + void copyPropertiesFrom (const SharedObject& source, UndoManager* const undoManager) + { + for (int i = properties.size(); --i >= 0;) + if (! source.properties.contains (properties.getName (i))) + removeProperty (properties.getName (i), undoManager); + + for (int i = 0; i < source.properties.size(); ++i) + setProperty (source.properties.getName(i), source.properties.getValueAt(i), undoManager); + } + + ValueTree getChildWithName (const Identifier typeToMatch) const { for (int i = 0; i < children.size(); ++i) { @@ -228,10 +230,10 @@ public: return ValueTree (s); } - return ValueTree::invalid; + return ValueTree(); } - ValueTree getOrCreateChildWithName (const Identifier& typeToMatch, UndoManager* undoManager) + ValueTree getOrCreateChildWithName (const Identifier typeToMatch, UndoManager* undoManager) { for (int i = 0; i < children.size(); ++i) { @@ -246,30 +248,24 @@ public: } - ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const + ValueTree getChildWithProperty (const Identifier propertyName, const var& propertyValue) const { for (int i = 0; i < children.size(); ++i) { SharedObject* const s = children.getObjectPointerUnchecked (i); - if (s->getProperty (propertyName) == propertyValue) + if (s->properties[propertyName] == propertyValue) return ValueTree (s); } - return ValueTree::invalid; + return ValueTree(); } bool isAChildOf (const SharedObject* const possibleParent) const noexcept { - const SharedObject* p = parent; - - while (p != nullptr) - { + for (const SharedObject* p = parent; p != nullptr; p = p->parent) if (p == possibleParent) return true; - p = p->parent; - } - return false; } @@ -304,7 +300,7 @@ public: } else { - if (index < 0) + if (! isPositiveAndBelow (index, children.size())) index = children.size(); undoManager->perform (new AddOrRemoveChildAction (this, index, child)); @@ -321,9 +317,7 @@ public: void removeChild (const int childIndex, UndoManager* const undoManager) { - const Ptr child (children.getObjectPointer (childIndex)); - - if (child != nullptr) + if (const Ptr child = children.getObjectPointer (childIndex)) { if (undoManager == nullptr) { @@ -415,11 +409,12 @@ public: XmlElement* createXml() const { - XmlElement* const xml = new XmlElement (type.toString()); + XmlElement* const xml = new XmlElement (type); properties.copyToXmlAttributes (*xml); - for (int i = 0; i < children.size(); ++i) - xml->addChildElement (children.getObjectPointerUnchecked(i)->createXml()); + // (NB: it's faster to add nodes to XML elements in reverse order) + for (int i = children.size(); --i >= 0;) + xml->prependChildElement (children.getObjectPointerUnchecked(i)->createXml()); return xml; } @@ -449,7 +444,7 @@ public: } else { - output.writeString (String::empty); + output.writeString (String()); output.writeCompressedInt (0); output.writeCompressedInt (0); } @@ -459,11 +454,10 @@ public: class SetPropertyAction : public UndoableAction { public: - SetPropertyAction (SharedObject* const target_, const Identifier& name_, - const var& newValue_, const var& oldValue_, - const bool isAddingNewProperty_, const bool isDeletingProperty_) - : target (target_), name (name_), newValue (newValue_), oldValue (oldValue_), - isAddingNewProperty (isAddingNewProperty_), isDeletingProperty (isDeletingProperty_) + SetPropertyAction (SharedObject* const so, const Identifier propertyName, + const var& newVal, const var& oldVal, bool isAdding, bool isDeleting) + : target (so), name (propertyName), newValue (newVal), oldValue (oldVal), + isAddingNewProperty (isAdding), isDeletingProperty (isDeleting) { } @@ -498,13 +492,10 @@ public: { if (! (isAddingNewProperty || isDeletingProperty)) { - SetPropertyAction* const next = dynamic_cast (nextAction); - - if (next != nullptr && next->target == target && next->name == name - && ! (next->isAddingNewProperty || next->isDeletingProperty)) - { - return new SetPropertyAction (target, name, next->newValue, oldValue, false, false); - } + if (SetPropertyAction* const next = dynamic_cast (nextAction)) + if (next->target == target && next->name == name + && ! (next->isAddingNewProperty || next->isDeletingProperty)) + return new SetPropertyAction (target, name, next->newValue, oldValue, false, false); } return nullptr; @@ -517,18 +508,18 @@ public: var oldValue; const bool isAddingNewProperty : 1, isDeletingProperty : 1; - JUCE_DECLARE_NON_COPYABLE (SetPropertyAction); + JUCE_DECLARE_NON_COPYABLE (SetPropertyAction) }; //============================================================================== class AddOrRemoveChildAction : public UndoableAction { public: - AddOrRemoveChildAction (SharedObject* target_, const int childIndex_, SharedObject* newChild_) - : target (target_), - child (newChild_ != nullptr ? newChild_ : target_->children.getObjectPointer (childIndex_)), - childIndex (childIndex_), - isDeleting (newChild_ == nullptr) + AddOrRemoveChildAction (SharedObject* parentObject, int index, SharedObject* newChild) + : target (parentObject), + child (newChild != nullptr ? newChild : parentObject->children.getObjectPointer (index)), + childIndex (index), + isDeleting (newChild == nullptr) { jassert (child != nullptr); } @@ -570,15 +561,15 @@ public: const int childIndex; const bool isDeleting; - JUCE_DECLARE_NON_COPYABLE (AddOrRemoveChildAction); + JUCE_DECLARE_NON_COPYABLE (AddOrRemoveChildAction) }; //============================================================================== class MoveChildAction : public UndoableAction { public: - MoveChildAction (SharedObject* const parent_, const int startIndex_, const int endIndex_) noexcept - : parent (parent_), startIndex (startIndex_), endIndex (endIndex_) + MoveChildAction (SharedObject* parentObject, int fromIndex, int toIndex) noexcept + : parent (parentObject), startIndex (fromIndex), endIndex (toIndex) { } @@ -601,10 +592,9 @@ public: UndoableAction* createCoalescedAction (UndoableAction* nextAction) { - MoveChildAction* next = dynamic_cast (nextAction); - - if (next != nullptr && next->parent == parent && next->startIndex == endIndex) - return new MoveChildAction (parent, startIndex, next->endIndex); + if (MoveChildAction* next = dynamic_cast (nextAction)) + if (next->parent == parent && next->startIndex == endIndex) + return new MoveChildAction (parent, startIndex, next->endIndex); return nullptr; } @@ -613,7 +603,7 @@ public: const Ptr parent; const int startIndex, endIndex; - JUCE_DECLARE_NON_COPYABLE (MoveChildAction); + JUCE_DECLARE_NON_COPYABLE (MoveChildAction) }; //============================================================================== @@ -625,7 +615,7 @@ public: private: SharedObject& operator= (const SharedObject&); - JUCE_LEAK_DETECTOR (SharedObject); + JUCE_LEAK_DETECTOR (SharedObject) }; //============================================================================== @@ -635,48 +625,49 @@ ValueTree::ValueTree() noexcept const ValueTree ValueTree::invalid; -ValueTree::ValueTree (const Identifier& type_) - : object (new ValueTree::SharedObject (type_)) +ValueTree::ValueTree (Identifier type) : object (new ValueTree::SharedObject (type)) { - jassert (type_.toString().isNotEmpty()); // All objects should be given a sensible type name! + jassert (type.toString().isNotEmpty()); // All objects must be given a sensible type name! } -ValueTree::ValueTree (SharedObject* const object_) - : object (object_) +ValueTree::ValueTree (SharedObject* so) : object (so) { } -ValueTree::ValueTree (const ValueTree& other) - : object (other.object) +ValueTree::ValueTree (const ValueTree& other) : object (other.object) { } ValueTree& ValueTree::operator= (const ValueTree& other) { - if (listeners.size() > 0) + if (object != other.object) { - if (object != nullptr) - object->valueTreesWithListeners.removeValue (this); + if (listeners.isEmpty()) + { + object = other.object; + } + else + { + if (object != nullptr) + object->valueTreesWithListeners.removeValue (this); - if (other.object != nullptr) - other.object->valueTreesWithListeners.add (this); + if (other.object != nullptr) + other.object->valueTreesWithListeners.add (this); + + object = other.object; + + listeners.call (&ValueTree::Listener::valueTreeRedirected, *this); + } } - object = other.object; return *this; } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS ValueTree::ValueTree (ValueTree&& other) noexcept - : object (static_cast (other.object)) + : object (static_cast (other.object)) { } - -ValueTree& ValueTree::operator= (ValueTree&& other) noexcept -{ - object = static_cast (other.object); - return *this; -} #endif ValueTree::~ValueTree() @@ -704,10 +695,10 @@ bool ValueTree::isEquivalentTo (const ValueTree& other) const ValueTree ValueTree::createCopy() const { - return ValueTree (createCopyIfNotNull (object.getObject())); + return ValueTree (createCopyIfNotNull (object.get())); } -bool ValueTree::hasType (const Identifier& typeName) const +bool ValueTree::hasType (const Identifier typeName) const { return object != nullptr && object->type == typeName; } @@ -732,26 +723,27 @@ ValueTree ValueTree::getSibling (const int delta) const return ValueTree (object->parent->children.getObjectPointer (index)); } -const var& ValueTree::operator[] (const Identifier& name) const +const var& ValueTree::operator[] (const Identifier name) const { - return object == nullptr ? var::null : object->getProperty (name); + return object == nullptr ? var::null : object->properties[name]; } -const var& ValueTree::getProperty (const Identifier& name) const +const var& ValueTree::getProperty (const Identifier name) const { - return object == nullptr ? var::null : object->getProperty (name); + return object == nullptr ? var::null : object->properties[name]; } -var ValueTree::getProperty (const Identifier& name, const var& defaultReturnValue) const +var ValueTree::getProperty (const Identifier name, const var& defaultReturnValue) const { return object == nullptr ? defaultReturnValue - : object->getProperty (name, defaultReturnValue); + : object->properties.getWithDefault (name, defaultReturnValue); } -ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, +ValueTree& ValueTree::setProperty (const Identifier name, const var& newValue, UndoManager* const undoManager) { - jassert (name.toString().isNotEmpty()); + jassert (name.toString().isNotEmpty()); // Must have a valid property name! + jassert (object != nullptr); // Trying to add a property to a null ValueTree will fail! if (object != nullptr) object->setProperty (name, newValue, undoManager); @@ -759,12 +751,12 @@ ValueTree& ValueTree::setProperty (const Identifier& name, const var& newValue, return *this; } -bool ValueTree::hasProperty (const Identifier& name) const +bool ValueTree::hasProperty (const Identifier name) const { return object != nullptr && object->hasProperty (name); } -void ValueTree::removeProperty (const Identifier& name, UndoManager* const undoManager) +void ValueTree::removeProperty (const Identifier name, UndoManager* const undoManager) { if (object != nullptr) object->removeProperty (name, undoManager); @@ -787,14 +779,23 @@ Identifier ValueTree::getPropertyName (const int index) const : object->properties.getName (index); } +void ValueTree::copyPropertiesFrom (const ValueTree& source, UndoManager* const undoManager) +{ + jassert (object != nullptr || source.object == nullptr); // Trying to add properties to a null ValueTree will fail! + + if (source.object == nullptr) + removeAllProperties (undoManager); + else if (object != nullptr) + object->copyPropertiesFrom (*(source.object), undoManager); +} + //============================================================================== class ValueTreePropertyValueSource : public Value::ValueSource, - public ValueTree::Listener + private ValueTree::Listener { public: - ValueTreePropertyValueSource (const ValueTree& tree_, const Identifier& property_, - UndoManager* const undoManager_) - : tree (tree_), property (property_), undoManager (undoManager_) + ValueTreePropertyValueSource (const ValueTree& vt, const Identifier prop, UndoManager* um) + : tree (vt), property (prop), undoManager (um) { tree.addListener (this); } @@ -807,27 +808,26 @@ public: var getValue() const { return tree [property]; } void setValue (const var& newValue) { tree.setProperty (property, newValue, undoManager); } - void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged, - const Identifier& changedProperty) - { - if (tree == treeWhosePropertyHasChanged && property == changedProperty) - sendChangeMessage (false); - } - - void valueTreeChildAdded (ValueTree&, ValueTree&) {} - void valueTreeChildRemoved (ValueTree&, ValueTree&) {} - void valueTreeChildOrderChanged (ValueTree&) {} - void valueTreeParentChanged (ValueTree&) {} - private: ValueTree tree; const Identifier property; UndoManager* const undoManager; - JUCE_DECLARE_NON_COPYABLE (ValueTreePropertyValueSource); + void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override + { + if (tree == changedTree && property == changedProperty) + sendChangeMessage (false); + } + + void valueTreeChildAdded (ValueTree&, ValueTree&) override {} + void valueTreeChildRemoved (ValueTree&, ValueTree&) override {} + void valueTreeChildOrderChanged (ValueTree&) override {} + void valueTreeParentChanged (ValueTree&) override {} + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreePropertyValueSource) }; -Value ValueTree::getPropertyAsValue (const Identifier& name, UndoManager* const undoManager) +Value ValueTree::getPropertyAsValue (const Identifier name, UndoManager* const undoManager) { return Value (new ValueTreePropertyValueSource (*this, name, undoManager)); } @@ -844,19 +844,19 @@ ValueTree ValueTree::getChild (int index) const : static_cast (nullptr)); } -ValueTree ValueTree::getChildWithName (const Identifier& type) const +ValueTree ValueTree::getChildWithName (const Identifier type) const { - return object != nullptr ? object->getChildWithName (type) : ValueTree::invalid; + return object != nullptr ? object->getChildWithName (type) : ValueTree(); } -ValueTree ValueTree::getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager) +ValueTree ValueTree::getOrCreateChildWithName (const Identifier type, UndoManager* undoManager) { - return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree::invalid; + return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree(); } -ValueTree ValueTree::getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const +ValueTree ValueTree::getChildWithProperty (const Identifier propertyName, const var& propertyValue) const { - return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; + return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree(); } bool ValueTree::isAChildOf (const ValueTree& possibleParent) const @@ -871,6 +871,8 @@ int ValueTree::indexOf (const ValueTree& child) const void ValueTree::addChild (const ValueTree& child, int index, UndoManager* const undoManager) { + jassert (object != nullptr); // Trying to add a child to a null ValueTree! + if (object != nullptr) object->addChild (child.object, index, undoManager); } @@ -919,7 +921,7 @@ void ValueTree::addListener (Listener* listener) { if (listener != nullptr) { - if (listeners.size() == 0 && object != nullptr) + if (listeners.isEmpty() && object != nullptr) object->valueTreesWithListeners.add (this); listeners.add (listener); @@ -930,11 +932,11 @@ void ValueTree::removeListener (Listener* listener) { listeners.remove (listener); - if (listeners.size() == 0 && object != nullptr) + if (listeners.isEmpty() && object != nullptr) object->valueTreesWithListeners.removeValue (this); } -void ValueTree::sendPropertyChangeMessage (const Identifier& property) +void ValueTree::sendPropertyChangeMessage (const Identifier property) { if (object != nullptr) object->sendPropertyChangeMessage (property); @@ -957,8 +959,14 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) return v; } +String ValueTree::toXmlString() const +{ + const ScopedPointer xml (createXml()); + return xml != nullptr ? xml->createDocument ("") : String(); +} + //============================================================================== -void ValueTree::writeToStream (OutputStream& output) +void ValueTree::writeToStream (OutputStream& output) const { SharedObject::writeObjectToStream (output, object); } @@ -968,7 +976,7 @@ ValueTree ValueTree::readFromStream (InputStream& input) const String type (input.readString()); if (type.isEmpty()) - return ValueTree::invalid; + return ValueTree(); ValueTree v (type); @@ -980,8 +988,7 @@ ValueTree ValueTree::readFromStream (InputStream& input) return v; } - int i; - for (i = 0; i < numProps; ++i) + for (int i = 0; i < numProps; ++i) { const String name (input.readString()); jassert (name.isNotEmpty()); @@ -990,8 +997,9 @@ ValueTree ValueTree::readFromStream (InputStream& input) } const int numChildren = input.readCompressedInt(); + v.object->children.ensureStorageAllocated (numChildren); - for (i = 0; i < numChildren; ++i) + for (int i = 0; i < numChildren; ++i) { ValueTree child (readFromStream (input)); @@ -1015,6 +1023,8 @@ ValueTree ValueTree::readFromGZIPData (const void* const data, const size_t numB return readFromStream (gzipStream); } +void ValueTree::Listener::valueTreeRedirected (ValueTree&) {} + //============================================================================== #if JUCE_UNIT_TESTS @@ -1023,11 +1033,10 @@ class ValueTreeTests : public UnitTest public: ValueTreeTests() : UnitTest ("ValueTrees") {} - static String createRandomIdentifier() + static String createRandomIdentifier (Random& r) { char buffer[50] = { 0 }; const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-:"; - Random r; for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;) buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; @@ -1035,10 +1044,9 @@ public: return CharPointer_ASCII (buffer); } - static String createRandomWideCharString() + static String createRandomWideCharString (Random& r) { juce_wchar buffer[50] = { 0 }; - Random r; for (int i = r.nextInt (numElementsInArray (buffer) - 1); --i >= 0;) { @@ -1057,20 +1065,19 @@ public: return CharPointer_UTF32 (buffer); } - static ValueTree createRandomTree (UndoManager* undoManager, int depth) + static ValueTree createRandomTree (UndoManager* undoManager, int depth, Random& r) { - Random r; - ValueTree v (createRandomIdentifier()); + ValueTree v (createRandomIdentifier (r)); for (int i = r.nextInt (10); --i >= 0;) { switch (r.nextInt (5)) { - case 0: v.setProperty (createRandomIdentifier(), createRandomWideCharString(), undoManager); break; - case 1: v.setProperty (createRandomIdentifier(), r.nextInt(), undoManager); break; - case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1), r.nextInt (v.getNumChildren() + 1), undoManager); break; - case 3: v.setProperty (createRandomIdentifier(), r.nextBool(), undoManager); break; - case 4: v.setProperty (createRandomIdentifier(), r.nextDouble(), undoManager); break; + case 0: v.setProperty (createRandomIdentifier (r), createRandomWideCharString (r), undoManager); break; + case 1: v.setProperty (createRandomIdentifier (r), r.nextInt(), undoManager); break; + case 2: if (depth < 5) v.addChild (createRandomTree (undoManager, depth + 1, r), r.nextInt (v.getNumChildren() + 1), undoManager); break; + case 3: v.setProperty (createRandomIdentifier (r), r.nextBool(), undoManager); break; + case 4: v.setProperty (createRandomIdentifier (r), r.nextDouble(), undoManager); break; default: break; } } @@ -1081,11 +1088,12 @@ public: void runTest() { beginTest ("ValueTree"); + Random r = getRandom(); for (int i = 10; --i >= 0;) { MemoryOutputStream mo; - ValueTree v1 (createRandomTree (nullptr, 0)); + ValueTree v1 (createRandomTree (nullptr, 0, r)); v1.writeToStream (mo); MemoryInputStream mi (mo.getData(), mo.getDataSize(), false); diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h index d818c71..311d722 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_VALUETREE_JUCEHEADER__ -#define __JUCE_VALUETREE_JUCEHEADER__ - -#include "juce_Value.h" -#include "../undomanager/juce_UndoManager.h" +#ifndef JUCE_VALUETREE_H_INCLUDED +#define JUCE_VALUETREE_H_INCLUDED //============================================================================== @@ -83,17 +79,16 @@ public: Like an XmlElement, each ValueTree node has a type, which you can access with getType() and hasType(). */ - explicit ValueTree (const Identifier& type); + explicit ValueTree (Identifier type); /** Creates a reference to another ValueTree. */ - ValueTree (const ValueTree& other); + ValueTree (const ValueTree&); /** Makes this object reference another node. */ - ValueTree& operator= (const ValueTree& other); + ValueTree& operator= (const ValueTree&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - ValueTree (ValueTree&& other) noexcept; - ValueTree& operator= (ValueTree&& other) noexcept; + ValueTree (ValueTree&&) noexcept; #endif /** Destructor. */ @@ -103,13 +98,13 @@ public: 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 noexcept; + bool operator== (const ValueTree&) const noexcept; /** 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 noexcept; + bool operator!= (const ValueTree&) const noexcept; /** Performs a deep comparison between the properties and children of two trees. If all the properties and children of the two trees are the same (recursively), this @@ -117,7 +112,7 @@ public: The normal operator==() only checks whether two trees refer to the same shared data structure, so use this method if you need to do a proper value comparison. */ - bool isEquivalentTo (const ValueTree& other) const; + bool isEquivalentTo (const ValueTree&) const; //============================================================================== /** Returns true if this node refers to some valid data. @@ -139,7 +134,7 @@ public: /** Returns true if the node has this type. The comparison is case-sensitive. */ - bool hasType (const Identifier& typeName) const; + bool hasType (const Identifier typeName) const; //============================================================================== /** Returns the value of a named property. @@ -147,21 +142,21 @@ public: You can also use operator[] to get a property. @see var, setProperty, hasProperty */ - const var& getProperty (const Identifier& name) const; + const var& getProperty (const 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 */ - var getProperty (const Identifier& name, const var& defaultReturnValue) const; + var getProperty (const 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 Identifier& name) const; + const var& operator[] (const Identifier name) const; /** Changes a named property of the node. The name identifier must not be an empty string. @@ -170,16 +165,16 @@ public: @see var, getProperty, removeProperty @returns a reference to the value tree, so that you can daisy-chain calls to this method. */ - ValueTree& setProperty (const Identifier& name, const var& newValue, UndoManager* undoManager); + ValueTree& setProperty (const Identifier name, const var& newValue, UndoManager* undoManager); /** Returns true if the node contains a named property. */ - bool hasProperty (const Identifier& name) const; + bool hasProperty (const 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 Identifier& name, UndoManager* undoManager); + void removeProperty (const Identifier name, UndoManager* undoManager); /** Removes all properties from the node. If the undoManager parameter is non-null, its UndoManager::perform() method will be used, @@ -203,7 +198,13 @@ public: it needs to change the value. Attaching a Value::Listener to the value object will provide callbacks whenever the property changes. */ - Value getPropertyAsValue (const Identifier& name, UndoManager* undoManager); + Value getPropertyAsValue (const Identifier name, UndoManager* undoManager); + + /** Overwrites all the properties in this tree with the properties of the source tree. + Any properties that already exist will be updated; and new ones will be added, and + any that are not present in the source tree will be removed. + */ + void copyPropertiesFrom (const ValueTree& source, UndoManager* undoManager); //============================================================================== /** Returns the number of child nodes belonging to this one. @@ -222,7 +223,7 @@ public: whether a node is valid). @see getOrCreateChildWithName */ - ValueTree getChildWithName (const Identifier& type) const; + ValueTree getChildWithName (const Identifier type) const; /** Returns the first child node with the speficied type name, creating and adding a child with this name if there wasn't already one there. @@ -231,7 +232,7 @@ public: the method on is itself invalid. @see getChildWithName */ - ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); + ValueTree getOrCreateChildWithName (const Identifier type, UndoManager* undoManager); /** Looks for the first child node that has the speficied property value. @@ -241,7 +242,7 @@ public: 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 Identifier& propertyName, const var& propertyValue) const; + ValueTree getChildWithProperty (const Identifier propertyName, const var& propertyValue) const; /** Adds a child to this node. @@ -316,7 +317,7 @@ public: //============================================================================== /** 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 + If this node is invalid, this may return nullptr. Otherwise, the XML that is produced can be used to recreate a similar node by calling fromXml() @see fromXml */ @@ -329,6 +330,11 @@ public: */ static ValueTree fromXml (const XmlElement& xml); + /** This returns a string containing an XML representation of the tree. + This is quite handy for debugging purposes, as it provides a quick way to view a tree. + */ + String toXmlString() const; + //============================================================================== /** Stores this tree (and all its children) in a binary format. @@ -337,7 +343,7 @@ public: 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); + void writeToStream (OutputStream& output) const; /** Reloads a tree from a stream that was written with writeToStream(). */ static ValueTree readFromStream (InputStream& input); @@ -412,6 +418,12 @@ public: the listener is registered, and not to any of its children. */ virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) = 0; + + /** This method is called when a tree is made to point to a different internal shared object. + When operator= is used to make a ValueTree refer to a different object, this callback + will be made. + */ + virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged); }; /** Adds a listener to receive callbacks when this node is changed. @@ -436,7 +448,7 @@ public: /** Causes a property-change callback to be triggered for the specified property, calling any listeners that are registered. */ - void sendPropertyChangeMessage (const Identifier& property); + void sendPropertyChangeMessage (const Identifier property); //============================================================================== /** This method uses a comparator object to sort the tree's children into order. @@ -480,7 +492,7 @@ public: private: //============================================================================== - class SharedObject; + JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject) friend class SharedObject; ReferenceCountedObjectPtr object; @@ -489,7 +501,7 @@ private: template struct ComparatorAdapter { - ComparatorAdapter (ElementComparator& comparator_) noexcept : comparator (comparator_) {} + ComparatorAdapter (ElementComparator& comp) noexcept : comparator (comp) {} int compareElements (const ValueTree* const first, const ValueTree* const second) { @@ -498,7 +510,7 @@ private: private: ElementComparator& comparator; - JUCE_DECLARE_NON_COPYABLE (ComparatorAdapter); + JUCE_DECLARE_NON_COPYABLE (ComparatorAdapter) }; void createListOfChildren (OwnedArray&) const; @@ -508,4 +520,4 @@ private: }; -#endif // __JUCE_VALUETREE_JUCEHEADER__ +#endif // JUCE_VALUETREE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp index 7b874c8..a7d56a6 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -34,12 +33,11 @@ public: listener (listener_) {} - void messageCallback() + void messageCallback() override { - const ActionBroadcaster* const b = broadcaster; - - if (b != nullptr && b->actionListeners.contains (listener)) - listener->actionListenerCallback (message); + if (const ActionBroadcaster* const b = broadcaster) + if (b->actionListeners.contains (listener)) + listener->actionListenerCallback (message); } private: @@ -47,7 +45,7 @@ private: const String message; ActionListener* const listener; - JUCE_DECLARE_NON_COPYABLE (ActionMessage); + JUCE_DECLARE_NON_COPYABLE (ActionMessage) }; //============================================================================== diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h index 01248a8..8052bed 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionBroadcaster.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__ -#define __JUCE_ACTIONBROADCASTER_JUCEHEADER__ - -#include "juce_ActionListener.h" +#ifndef JUCE_ACTIONBROADCASTER_H_INCLUDED +#define JUCE_ACTIONBROADCASTER_H_INCLUDED //============================================================================== @@ -76,11 +73,11 @@ private: class ActionMessage; friend class ActionMessage; - SortedSet actionListeners; + SortedSet actionListeners; CriticalSection actionListenerLock; - JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster); + JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster) }; -#endif // __JUCE_ACTIONBROADCASTER_JUCEHEADER__ +#endif // JUCE_ACTIONBROADCASTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h index 364c415..12257f9 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ActionListener.h @@ -1,38 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_ACTIONLISTENER_JUCEHEADER__ -#define __JUCE_ACTIONLISTENER_JUCEHEADER__ +#ifndef JUCE_ACTIONLISTENER_H_INCLUDED +#define JUCE_ACTIONLISTENER_H_INCLUDED //============================================================================== /** - 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. + Interface class for delivery of events that are sent by an ActionBroadcaster. @see ActionBroadcaster, ChangeListener */ @@ -51,4 +47,4 @@ public: }; -#endif // __JUCE_ACTIONLISTENER_JUCEHEADER__ +#endif // JUCE_ACTIONLISTENER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp index b7b8012..de12202 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,12 +25,9 @@ class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage { public: - AsyncUpdaterMessage (AsyncUpdater& owner_) - : owner (owner_) - { - } + AsyncUpdaterMessage (AsyncUpdater& au) : owner (au) {} - void messageCallback() + void messageCallback() override { if (shouldDeliver.compareAndSetBool (0, 1)) owner.handleAsyncUpdate(); @@ -42,13 +38,13 @@ public: private: AsyncUpdater& owner; - JUCE_DECLARE_NON_COPYABLE (AsyncUpdaterMessage); + JUCE_DECLARE_NON_COPYABLE (AsyncUpdaterMessage) }; //============================================================================== AsyncUpdater::AsyncUpdater() { - message = new AsyncUpdaterMessage (*this); + activeMessage = new AsyncUpdaterMessage (*this); } AsyncUpdater::~AsyncUpdater() @@ -59,18 +55,18 @@ AsyncUpdater::~AsyncUpdater() // deleting this object, or find some other way to avoid such a race condition. jassert ((! isUpdatePending()) || MessageManager::getInstance()->currentThreadHasLockedMessageManager()); - message->shouldDeliver.set (0); + activeMessage->shouldDeliver.set (0); } void AsyncUpdater::triggerAsyncUpdate() { - if (message->shouldDeliver.compareAndSetBool (1, 0)) - message->post(); + if (activeMessage->shouldDeliver.compareAndSetBool (1, 0)) + activeMessage->post(); } void AsyncUpdater::cancelPendingUpdate() noexcept { - message->shouldDeliver.set (0); + activeMessage->shouldDeliver.set (0); } void AsyncUpdater::handleUpdateNowIfNeeded() @@ -78,11 +74,11 @@ void AsyncUpdater::handleUpdateNowIfNeeded() // This can only be called by the event thread. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); - if (message->shouldDeliver.exchange (0) != 0) + if (activeMessage->shouldDeliver.exchange (0) != 0) handleAsyncUpdate(); } bool AsyncUpdater::isUpdatePending() const noexcept { - return message->shouldDeliver.value != 0; + return activeMessage->shouldDeliver.value != 0; } diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h index 236e85b..79ab6e1 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_AsyncUpdater.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__ -#define __JUCE_ASYNCUPDATER_JUCEHEADER__ +#ifndef JUCE_ASYNCUPDATER_H_INCLUDED +#define JUCE_ASYNCUPDATER_H_INCLUDED //============================================================================== @@ -45,7 +44,6 @@ public: AsyncUpdater(); /** Destructor. - If there are any pending callbacks when the object is deleted, these are lost. */ virtual ~AsyncUpdater(); @@ -70,8 +68,8 @@ public: callback happens, this will cancel the handleAsyncUpdate() callback. Note that this method simply cancels the next callback - if a callback is already - in progress on a different thread, this won't block until it finishes, so there's - no guarantee that the callback isn't still running when you return from + in progress on a different thread, this won't block until the callback finishes, so + there's no guarantee that the callback isn't still running when the method returns. */ void cancelPendingUpdate() noexcept; @@ -102,10 +100,10 @@ private: //============================================================================== class AsyncUpdaterMessage; friend class ReferenceCountedObjectPtr; - ReferenceCountedObjectPtr message; + ReferenceCountedObjectPtr activeMessage; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater) }; -#endif // __JUCE_ASYNCUPDATER_JUCEHEADER__ +#endif // JUCE_ASYNCUPDATER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp index 7c9a8db..bff1f1a 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h index b554efe..8d9aca9 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h @@ -1,34 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__ -#define __JUCE_CHANGEBROADCASTER_JUCEHEADER__ - -#include "juce_ChangeListener.h" -#include "juce_ListenerList.h" -#include "juce_AsyncUpdater.h" +#ifndef JUCE_CHANGEBROADCASTER_H_INCLUDED +#define JUCE_CHANGEBROADCASTER_H_INCLUDED //============================================================================== @@ -92,7 +87,7 @@ private: { public: ChangeBroadcasterCallback(); - void handleAsyncUpdate(); + void handleAsyncUpdate() override; ChangeBroadcaster* owner; }; @@ -103,8 +98,8 @@ private: void callListeners(); - JUCE_DECLARE_NON_COPYABLE (ChangeBroadcaster); + JUCE_DECLARE_NON_COPYABLE (ChangeBroadcaster) }; -#endif // __JUCE_CHANGEBROADCASTER_JUCEHEADER__ +#endif // JUCE_CHANGEBROADCASTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h index 620e06b..59e3025 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ChangeListener.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_CHANGELISTENER_JUCEHEADER__ -#define __JUCE_CHANGELISTENER_JUCEHEADER__ +#ifndef JUCE_CHANGELISTENER_H_INCLUDED +#define JUCE_CHANGELISTENER_H_INCLUDED class ChangeBroadcaster; @@ -62,4 +61,4 @@ public: }; -#endif // __JUCE_CHANGELISTENER_JUCEHEADER__ +#endif // JUCE_CHANGELISTENER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ListenerList.h b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ListenerList.h index a290d83..853e252 100644 --- a/JuceLibraryCode/modules/juce_events/broadcasters/juce_ListenerList.h +++ b/JuceLibraryCode/modules/juce_events/broadcasters/juce_ListenerList.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_LISTENERLIST_JUCEHEADER__ -#define __JUCE_LISTENERLIST_JUCEHEADER__ +#ifndef JUCE_LISTENERLIST_H_INCLUDED +#define JUCE_LISTENERLIST_H_INCLUDED //============================================================================== @@ -66,7 +65,7 @@ ListenerList::DummyBailOutChecker, which is a dummy checker that always returns false. */ template > + class ArrayType = Array > class ListenerList { // Horrible macros required to support VC7.. @@ -115,7 +114,7 @@ public: // Listeners can't be null pointers! jassert (listenerToRemove != nullptr); - listeners.removeValue (listenerToRemove); + listeners.removeFirstMatchingValue (listenerToRemove); } /** Returns the number of registered listeners. */ @@ -263,6 +262,27 @@ public: (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5); } + //============================================================================== + /** Calls a member function on each listener in the list, with 5 parameters. */ + template + void call (void (ListenerClass::*callbackFunction) (P1, P2, P3, P4, P5, P6), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) + { + for (Iterator iter (*this); iter.next();) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); + } + + /** 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, P6), + LL_PARAM(1), LL_PARAM(2), LL_PARAM(3), LL_PARAM(4), LL_PARAM(5), LL_PARAM(6)) + { + for (Iterator iter (*this); iter.next (bailOutChecker);) + (iter.getListener()->*callbackFunction) (param1, param2, param3, param4, param5, param6); + } + //============================================================================== /** A dummy bail-out checker that always returns false. @@ -281,8 +301,8 @@ public: { public: //============================================================================== - Iterator (const ListType& list_) noexcept - : list (list_), index (list_.size()) + Iterator (const ListType& listToIterate) noexcept + : list (listToIterate), index (listToIterate.size()) {} ~Iterator() noexcept {} @@ -317,7 +337,7 @@ public: const ListType& list; int index; - JUCE_DECLARE_NON_COPYABLE (Iterator); + JUCE_DECLARE_NON_COPYABLE (Iterator) }; typedef ListenerList ThisType; @@ -329,11 +349,11 @@ private: //============================================================================== ArrayType listeners; - JUCE_DECLARE_NON_COPYABLE (ListenerList); + JUCE_DECLARE_NON_COPYABLE (ListenerList) #undef LL_TEMPLATE #undef LL_PARAM }; -#endif // __JUCE_LISTENERLIST_JUCEHEADER__ +#endif // JUCE_LISTENERLIST_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp new file mode 100644 index 0000000..926798c --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp @@ -0,0 +1,259 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +enum { magicMastSlaveConnectionHeader = 0x712baf04 }; + +static const char* startMessage = "__ipc_st"; +static const char* killMessage = "__ipc_k_"; +static const char* pingMessage = "__ipc_p_"; +enum { specialMessageSize = 8 }; + +static String getCommandLinePrefix (const String& commandLineUniqueID) +{ + return "--" + commandLineUniqueID + ":"; +} + +//============================================================================== +// This thread sends and receives ping messages every second, so that it +// can find out if the other process has stopped running. +struct ChildProcessPingThread : public Thread, + private AsyncUpdater +{ + ChildProcessPingThread() : Thread ("IPC ping"), timeoutMs (8000) + { + pingReceived(); + } + + static bool isPingMessage (const MemoryBlock& m) noexcept + { + return memcmp (m.getData(), pingMessage, specialMessageSize) == 0; + } + + void pingReceived() noexcept { countdown = timeoutMs / 1000 + 1; } + void triggerConnectionLostMessage() { triggerAsyncUpdate(); } + + virtual bool sendPingMessage (const MemoryBlock&) = 0; + virtual void pingFailed() = 0; + + int timeoutMs; + +private: + Atomic countdown; + + void handleAsyncUpdate() override { pingFailed(); } + + void run() override + { + while (! threadShouldExit()) + { + if (--countdown <= 0 || ! sendPingMessage (MemoryBlock (pingMessage, specialMessageSize))) + { + triggerConnectionLostMessage(); + break; + } + + wait (1000); + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessPingThread) +}; + +//============================================================================== +struct ChildProcessMaster::Connection : public InterprocessConnection, + private ChildProcessPingThread +{ + Connection (ChildProcessMaster& m, const String& pipeName) + : InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (m) + { + if (createPipe (pipeName, timeoutMs)) + startThread (4); + } + + ~Connection() + { + stopThread (10000); + } + +private: + void connectionMade() override {} + void connectionLost() override { owner.handleConnectionLost(); } + + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToSlave (m); } + void pingFailed() override { connectionLost(); } + + void messageReceived (const MemoryBlock& m) override + { + pingReceived(); + + if (m.getSize() != specialMessageSize || ! isPingMessage (m)) + owner.handleMessageFromSlave (m); + } + + ChildProcessMaster& owner; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) +}; + +//============================================================================== +ChildProcessMaster::ChildProcessMaster() {} + +ChildProcessMaster::~ChildProcessMaster() +{ + if (connection != nullptr) + { + sendMessageToSlave (MemoryBlock (killMessage, specialMessageSize)); + connection->disconnect(); + connection = nullptr; + } +} + +void ChildProcessMaster::handleConnectionLost() {} + +bool ChildProcessMaster::sendMessageToSlave (const MemoryBlock& mb) +{ + if (connection != nullptr) + return connection->sendMessage (mb); + + jassertfalse; // this can only be used when the connection is active! + return false; +} + +bool ChildProcessMaster::launchSlaveProcess (const File& executable, const String& commandLineUniqueID) +{ + connection = nullptr; + jassert (childProcess.kill()); + + const String pipeName ("p" + String::toHexString (Random().nextInt64())); + + StringArray args; + args.add (executable.getFullPathName()); + args.add (getCommandLinePrefix (commandLineUniqueID) + pipeName); + + if (childProcess.start (args)) + { + connection = new Connection (*this, pipeName); + + if (connection->isConnected()) + { + sendMessageToSlave (MemoryBlock (startMessage, specialMessageSize)); + return true; + } + + connection = nullptr; + } + + return false; +} + +//============================================================================== +struct ChildProcessSlave::Connection : public InterprocessConnection, + private ChildProcessPingThread +{ + Connection (ChildProcessSlave& p, const String& pipeName) + : InterprocessConnection (false, magicMastSlaveConnectionHeader), owner (p) + { + connectToPipe (pipeName, timeoutMs); + startThread (4); + } + + ~Connection() + { + stopThread (10000); + } + +private: + ChildProcessSlave& owner; + + void connectionMade() override {} + void connectionLost() override { owner.handleConnectionLost(); } + + bool sendPingMessage (const MemoryBlock& m) override { return owner.sendMessageToMaster (m); } + void pingFailed() override { connectionLost(); } + + void messageReceived (const MemoryBlock& m) override + { + pingReceived(); + + if (m.getSize() == specialMessageSize) + { + if (isPingMessage (m)) + return; + + if (memcmp (m.getData(), killMessage, specialMessageSize) == 0) + { + triggerConnectionLostMessage(); + return; + } + + if (memcmp (m.getData(), startMessage, specialMessageSize) == 0) + { + owner.handleConnectionMade(); + return; + } + } + + owner.handleMessageFromMaster (m); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Connection) +}; + +//============================================================================== +ChildProcessSlave::ChildProcessSlave() {} +ChildProcessSlave::~ChildProcessSlave() {} + +void ChildProcessSlave::handleConnectionMade() {} +void ChildProcessSlave::handleConnectionLost() {} + +bool ChildProcessSlave::sendMessageToMaster (const MemoryBlock& mb) +{ + if (connection != nullptr) + return connection->sendMessage (mb); + + jassertfalse; // this can only be used when the connection is active! + return false; +} + +bool ChildProcessSlave::initialiseFromCommandLine (const String& commandLine, + const String& commandLineUniqueID) +{ + String prefix (getCommandLinePrefix (commandLineUniqueID)); + + if (commandLine.trim().startsWith (prefix)) + { + String pipeName (commandLine.fromFirstOccurrenceOf (prefix, false, false) + .upToFirstOccurrenceOf (" ", false, false).trim()); + + if (pipeName.isNotEmpty()) + { + connection = new Connection (*this, pipeName); + + if (! connection->isConnected()) + connection = nullptr; + } + } + + return connection != nullptr; +} diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h new file mode 100644 index 0000000..0b3ec6b --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_ConnectedChildProcess.h @@ -0,0 +1,179 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED +#define JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED + +//============================================================================== +/** + Acts as the slave end of a master/slave pair of connected processes. + + The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + to spawn a child process, and to manage a 2-way messaging connection to control it. + + To use the system, you need to create subclasses of both ChildProcessSlave and + ChildProcessMaster. To instantiate the ChildProcessSlave object, you must + add some code to your main() or JUCEApplication::initialise() function that + calls the initialiseFromCommandLine() method to check the app's command-line + parameters to see whether it's being launched as a child process. If this returns + true then the slave process can be allowed to run, and its handleMessageFromMaster() + method will be called whenever a message arrives. + + The juce demo app has a good example of this class in action. + + @see ChildProcessMaster, InterprocessConnection, ChildProcess +*/ +class JUCE_API ChildProcessSlave +{ +public: + /** Creates a non-connected slave process. + Use initialiseFromCommandLine to connect to a master process. + */ + ChildProcessSlave(); + + /** Destructor. */ + virtual ~ChildProcessSlave(); + + /** This checks some command-line parameters to see whether they were generated by + ChildProcessMaster::launchSlaveProcess(), and if so, connects to that master process. + + In an exe that can be used as a child process, you should add some code to your + main() or JUCEApplication::initialise() that calls this method. + + The commandLineUniqueID should be a short alphanumeric identifier (no spaces!) + that matches the string passed to ChildProcessMaster::launchSlaveProcess(). + + Returns true if the command-line matches and the connection is made successfully. + */ + bool initialiseFromCommandLine (const String& commandLine, + const String& commandLineUniqueID); + + //============================================================================== + /** This will be called to deliver messages from the master process. + The call will probably be made on a background thread, so be careful with your + thread-safety! You may want to respond by sending back a message with + sendMessageToMaster() + */ + virtual void handleMessageFromMaster (const MemoryBlock&) = 0; + + /** This will be called when the master process finishes connecting to this slave. + The call will probably be made on a background thread, so be careful with your thread-safety! + */ + virtual void handleConnectionMade(); + + /** This will be called when the connection to the master process is lost. + The call may be made from any thread (including the message thread). + Typically, if your process only exists to act as a slave, you should probably exit + when this happens. + */ + virtual void handleConnectionLost(); + + /** Tries to send a message to the master process. + This returns true if the message was sent, but doesn't check that it actually gets + delivered at the other end. If successful, the data will emerge in a call to your + ChildProcessMaster::handleMessageFromSlave(). + */ + bool sendMessageToMaster (const MemoryBlock&); + +private: + struct Connection; + friend struct Connection; + friend struct ContainerDeletePolicy; + ScopedPointer connection; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessSlave) +}; + +//============================================================================== +/** + Acts as the master in a master/slave pair of connected processes. + + The ChildProcessSlave and ChildProcessMaster classes make it easy for an app + to spawn a child process, and to manage a 2-way messaging connection to control it. + + To use the system, you need to create subclasses of both ChildProcessSlave and + ChildProcessMaster. When you want your master process to launch the slave, you + just call launchSlaveProcess(), and it'll attempt to launch the executable that + you specify (which may be the same exe), and assuming it has been set-up to + correctly parse the command-line parameters (see ChildProcessSlave) then a + two-way connection will be created. + + The juce demo app has a good example of this class in action. + + @see ChildProcessSlave, InterprocessConnection, ChildProcess +*/ +class JUCE_API ChildProcessMaster +{ +public: + /** Creates an uninitialised master process object. + Use launchSlaveProcess to launch and connect to a child process. + */ + ChildProcessMaster(); + + /** Destructor. */ + virtual ~ChildProcessMaster(); + + /** Attempts to launch and connect to a slave process. + This will start the given executable, passing it a special command-line + parameter based around the commandLineUniqueID string, which must be a + short alphanumeric string (no spaces!) that identifies your app. The exe + that gets launched must respond by calling ChildProcessSlave::initialiseFromCommandLine() + in its startup code, and must use a matching ID to commandLineUniqueID. + + If this all works, the method returns true, and you can begin sending and + receiving messages with the slave process. + */ + bool launchSlaveProcess (const File& executableToLaunch, + const String& commandLineUniqueID); + + /** This will be called to deliver a message from the slave process. + The call will probably be made on a background thread, so be careful with your thread-safety! + */ + virtual void handleMessageFromSlave (const MemoryBlock&) = 0; + + /** This will be called when the slave process dies or is somehow disconnected. + The call will probably be made on a background thread, so be careful with your thread-safety! + */ + virtual void handleConnectionLost(); + + /** Attempts to send a message to the slave process. + This returns true if the message was dispatched, but doesn't check that it actually + gets delivered at the other end. If successful, the data will emerge in a call to + your ChildProcessSlave::handleMessageFromMaster(). + */ + bool sendMessageToSlave (const MemoryBlock&); + +private: + ChildProcess childProcess; + + struct Connection; + friend struct Connection; + friend struct ContainerDeletePolicy; + ScopedPointer connection; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster) +}; + + +#endif // JUCE_CONNECTEDCHILDPROCESS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp index fb8c9c3..b1b72f7 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp @@ -1,36 +1,48 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +struct InterprocessConnection::ConnectionThread : public Thread +{ + ConnectionThread (InterprocessConnection& c) : Thread ("JUCE IPC"), owner (c) {} + + void run() override { owner.runThread(); } + +private: + InterprocessConnection& owner; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionThread); +}; + +//============================================================================== InterprocessConnection::InterprocessConnection (const bool callbacksOnMessageThread, const uint32 magicMessageHeaderNumber) - : Thread ("Juce IPC connection"), - callbackConnectionState (false), + : callbackConnectionState (false), useMessageThread (callbacksOnMessageThread), magicMessageHeader (magicMessageHeaderNumber), pipeReceiveMessageTimeout (-1) { + thread = new ConnectionThread (*this); } InterprocessConnection::~InterprocessConnection() @@ -38,9 +50,9 @@ InterprocessConnection::~InterprocessConnection() callbackConnectionState = false; disconnect(); masterReference.clear(); + thread = nullptr; } - //============================================================================== bool InterprocessConnection::connectToSocket (const String& hostName, const int portNumber, @@ -54,7 +66,7 @@ bool InterprocessConnection::connectToSocket (const String& hostName, if (socket->connect (hostName, portNumber, timeOutMillisecs)) { connectionMadeInt(); - startThread(); + thread->startThread(); return true; } else @@ -64,17 +76,16 @@ bool InterprocessConnection::connectToSocket (const String& hostName, } } -bool InterprocessConnection::connectToPipe (const String& pipeName, - const int pipeReceiveMessageTimeoutMs) +bool InterprocessConnection::connectToPipe (const String& pipeName, const int timeoutMs) { disconnect(); - ScopedPointer newPipe (new NamedPipe()); + ScopedPointer newPipe (new NamedPipe()); if (newPipe->openExisting (pipeName)) { const ScopedLock sl (pipeAndSocketLock); - pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; + pipeReceiveMessageTimeout = timeoutMs; initialiseWithPipe (newPipe.release()); return true; } @@ -82,17 +93,16 @@ bool InterprocessConnection::connectToPipe (const String& pipeName, return false; } -bool InterprocessConnection::createPipe (const String& pipeName, - const int pipeReceiveMessageTimeoutMs) +bool InterprocessConnection::createPipe (const String& pipeName, const int timeoutMs) { disconnect(); - ScopedPointer newPipe (new NamedPipe()); + ScopedPointer newPipe (new NamedPipe()); if (newPipe->createNewPipe (pipeName)) { const ScopedLock sl (pipeAndSocketLock); - pipeReceiveMessageTimeout = pipeReceiveMessageTimeoutMs; + pipeReceiveMessageTimeout = timeoutMs; initialiseWithPipe (newPipe.release()); return true; } @@ -102,39 +112,41 @@ bool InterprocessConnection::createPipe (const String& pipeName, void InterprocessConnection::disconnect() { - if (socket != nullptr) - socket->close(); - - if (pipe != nullptr) - pipe->close(); - - stopThread (4000); + thread->signalThreadShouldExit(); { const ScopedLock sl (pipeAndSocketLock); - socket = nullptr; - pipe = nullptr; + if (socket != nullptr) socket->close(); + if (pipe != nullptr) pipe->close(); } + thread->stopThread (4000); + deletePipeAndSocket(); connectionLostInt(); } +void InterprocessConnection::deletePipeAndSocket() +{ + const ScopedLock sl (pipeAndSocketLock); + socket = nullptr; + pipe = nullptr; +} + bool InterprocessConnection::isConnected() const { const ScopedLock sl (pipeAndSocketLock); return ((socket != nullptr && socket->isConnected()) || (pipe != nullptr && pipe->isOpen())) - && isThreadRunning(); + && thread->isThreadRunning(); } String InterprocessConnection::getConnectedHostName() const { if (pipe != nullptr) - { return "localhost"; - } - else if (socket != nullptr) + + if (socket != nullptr) { if (! socket->isLocal()) return socket->getHostName(); @@ -142,7 +154,7 @@ String InterprocessConnection::getConnectedHostName() const return "localhost"; } - return String::empty; + return String(); } //============================================================================== @@ -163,39 +175,38 @@ bool InterprocessConnection::sendMessage (const MemoryBlock& message) if (socket != nullptr) bytesWritten = socket->write (messageData.getData(), (int) messageData.getSize()); else if (pipe != nullptr) - bytesWritten = pipe->write (messageData.getData(), (int) messageData.getSize()); + bytesWritten = pipe->write (messageData.getData(), (int) messageData.getSize(), pipeReceiveMessageTimeout); return bytesWritten == (int) messageData.getSize(); } //============================================================================== -void InterprocessConnection::initialiseWithSocket (StreamingSocket* const socket_) +void InterprocessConnection::initialiseWithSocket (StreamingSocket* newSocket) { - jassert (socket == 0); - socket = socket_; + jassert (socket == nullptr && pipe == nullptr); + socket = newSocket; connectionMadeInt(); - startThread(); + thread->startThread(); } -void InterprocessConnection::initialiseWithPipe (NamedPipe* const pipe_) +void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe) { - jassert (pipe == 0); - pipe = pipe_; + jassert (socket == nullptr && pipe == nullptr); + pipe = newPipe; connectionMadeInt(); - startThread(); + thread->startThread(); } //============================================================================== struct ConnectionStateMessage : public MessageManager::MessageBase { - ConnectionStateMessage (InterprocessConnection* owner_, bool connectionMade_) noexcept - : owner (owner_), connectionMade (connectionMade_) + ConnectionStateMessage (InterprocessConnection* ipc, bool connected) noexcept + : owner (ipc), connectionMade (connected) {} - void messageCallback() + void messageCallback() override { - InterprocessConnection* const ipc = owner; - if (ipc != nullptr) + if (InterprocessConnection* const ipc = owner) { if (connectionMade) ipc->connectionMade(); @@ -206,6 +217,8 @@ struct ConnectionStateMessage : public MessageManager::MessageBase WeakReference owner; bool connectionMade; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionStateMessage) }; void InterprocessConnection::connectionMadeInt() @@ -236,14 +249,13 @@ void InterprocessConnection::connectionLostInt() struct DataDeliveryMessage : public Message { - DataDeliveryMessage (InterprocessConnection* owner_, const MemoryBlock& data_) - : owner (owner_), data (data_) + DataDeliveryMessage (InterprocessConnection* ipc, const MemoryBlock& d) + : owner (ipc), data (d) {} - void messageCallback() + void messageCallback() override { - InterprocessConnection* const ipc = owner; - if (ipc != nullptr) + if (InterprocessConnection* const ipc = owner) ipc->messageReceived (data); } @@ -264,30 +276,30 @@ void InterprocessConnection::deliverDataInt (const MemoryBlock& data) //============================================================================== bool InterprocessConnection::readNextMessageInt() { - const int maximumMessageSize = 1024 * 1024 * 10; // sanity check - uint32 messageHeader[2]; const int bytes = socket != nullptr ? socket->read (messageHeader, sizeof (messageHeader), true) - : pipe ->read (messageHeader, sizeof (messageHeader), pipeReceiveMessageTimeout); + : pipe ->read (messageHeader, sizeof (messageHeader), -1); if (bytes == sizeof (messageHeader) && ByteOrder::swapIfBigEndian (messageHeader[0]) == magicMessageHeader) { int bytesInMessage = (int) ByteOrder::swapIfBigEndian (messageHeader[1]); - if (bytesInMessage > 0 && bytesInMessage < maximumMessageSize) + if (bytesInMessage > 0) { MemoryBlock messageData ((size_t) bytesInMessage, true); int bytesRead = 0; while (bytesInMessage > 0) { - if (threadShouldExit()) + if (thread->threadShouldExit()) return false; const int numThisTime = jmin (bytesInMessage, 65536); - const int bytesIn = socket != nullptr ? socket->read (static_cast (messageData.getData()) + bytesRead, numThisTime, true) - : pipe ->read (static_cast (messageData.getData()) + bytesRead, numThisTime, pipeReceiveMessageTimeout); + void* const data = addBytesToPointer (messageData.getData(), bytesRead); + + const int bytesIn = socket != nullptr ? socket->read (data, numThisTime, true) + : pipe ->read (data, numThisTime, -1); if (bytesIn <= 0) break; @@ -302,10 +314,8 @@ bool InterprocessConnection::readNextMessageInt() } else if (bytes < 0) { - { - const ScopedLock sl (pipeAndSocketLock); - socket = nullptr; - } + if (socket != nullptr) + deletePipeAndSocket(); connectionLostInt(); return false; @@ -314,9 +324,9 @@ bool InterprocessConnection::readNextMessageInt() return true; } -void InterprocessConnection::run() +void InterprocessConnection::runThread() { - while (! threadShouldExit()) + while (! thread->threadShouldExit()) { if (socket != nullptr) { @@ -324,45 +334,32 @@ void InterprocessConnection::run() if (ready < 0) { - { - const ScopedLock sl (pipeAndSocketLock); - socket = nullptr; - } - + deletePipeAndSocket(); connectionLostInt(); break; } - else if (ready > 0) + + if (ready == 0) { - if (! readNextMessageInt()) - break; - } - else - { - Thread::sleep (2); + thread->wait (1); + continue; } } else if (pipe != nullptr) { if (! pipe->isOpen()) { - { - const ScopedLock sl (pipeAndSocketLock); - pipe = nullptr; - } - + deletePipeAndSocket(); connectionLostInt(); break; } - else - { - if (! readNextMessageInt()) - break; - } } else { break; } + + if (thread->threadShouldExit() || ! readNextMessageInt()) + break; } } diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h index 63794c8..8f21a29 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ -#define __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ +#ifndef JUCE_INTERPROCESSCONNECTION_H_INCLUDED +#define JUCE_INTERPROCESSCONNECTION_H_INCLUDED class InterprocessConnectionServer; class MemoryBlock; @@ -48,7 +47,7 @@ class MemoryBlock; @see InterprocessConnectionServer, Socket, NamedPipe */ -class JUCE_API InterprocessConnection : public Thread +class JUCE_API InterprocessConnection { public: //============================================================================== @@ -72,7 +71,7 @@ public: uint32 magicMessageHeaderNumber = 0xf2b49e2c); /** Destructor. */ - ~InterprocessConnection(); + virtual ~InterprocessConnection(); //============================================================================== /** Tries to connect this object to a socket. @@ -96,25 +95,26 @@ public: 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. - + @param pipeName the name to use for the pipe - this should be unique to your app + @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing + to the pipe, or -1 for an infinite timeout. @returns true if it connects successfully. @see createPipe, NamedPipe */ - bool connectToPipe (const String& pipeName, - int pipeReceiveMessageTimeoutMs = -1); + bool connectToPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs); /** 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. + @param pipeName the name to use for the pipe - this should be unique to your app + @param pipeReceiveMessageTimeoutMs a timeout length to be used when reading or writing + to the pipe, or -1 for an infinite timeout. + @returns true if the pipe was created, or false if it fails (e.g. if another process is + already using using the pipe). */ - bool createPipe (const String& pipeName, - int pipeReceiveMessageTimeoutMs = -1); + bool createPipe (const String& pipeName, int pipeReceiveMessageTimeoutMs); /** Disconnects and closes any currently-open sockets or pipes. */ void disconnect(); @@ -122,16 +122,14 @@ public: /** 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). */ + /** Returns the socket that this connection is using (or nullptr if it uses a pipe). */ StreamingSocket* getSocket() const noexcept { return socket; } - /** Returns the pipe that this connection is using (or null if it uses a socket). */ + /** Returns the pipe that this connection is using (or nullptr if it uses a socket). */ NamedPipe* getPipe() const noexcept { 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. + This may return an empty string if the name is unknown. */ String getConnectedHostName() const; @@ -192,13 +190,19 @@ private: friend class InterprocessConnectionServer; void initialiseWithSocket (StreamingSocket*); void initialiseWithPipe (NamedPipe*); + void deletePipeAndSocket(); void connectionMadeInt(); void connectionLostInt(); void deliverDataInt (const MemoryBlock&); bool readNextMessageInt(); - void run(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection); + struct ConnectionThread; + friend struct ConnectionThread; + friend struct ContainerDeletePolicy; + ScopedPointer thread; + void runThread(); + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection) }; -#endif // __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ +#endif // JUCE_INTERPROCESSCONNECTION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp index b7c9971..e9b5426 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -65,14 +64,10 @@ void InterprocessConnectionServer::run() { while ((! threadShouldExit()) && socket != nullptr) { - ScopedPointer clientSocket (socket->waitForNextConnection()); + ScopedPointer clientSocket (socket->waitForNextConnection()); if (clientSocket != nullptr) - { - InterprocessConnection* newConnection = createConnectionObject(); - - if (newConnection != nullptr) + if (InterprocessConnection* newConnection = createConnectionObject()) newConnection->initialiseWithSocket (clientSocket.release()); - } } } diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h index d126f68..8de7b8f 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ -#define __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ - -#include "juce_InterprocessConnection.h" +#ifndef JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED +#define JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED //============================================================================== @@ -87,10 +84,10 @@ private: //============================================================================== ScopedPointer socket; - void run(); + void run() override; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnectionServer); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnectionServer) }; -#endif // __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ +#endif // JUCE_INTERPROCESSCONNECTIONSERVER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/juce_events.cpp b/JuceLibraryCode/modules/juce_events/juce_events.cpp index de42626..2110d0a 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.cpp +++ b/JuceLibraryCode/modules/juce_events/juce_events.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_EVENTS_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_EVENTS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -63,8 +62,6 @@ namespace juce { -// START_AUTOINCLUDE messages/*.cpp, broadcasters/*.cpp, -// timers/*.cpp, interprocess/*.cpp #include "messages/juce_ApplicationBase.cpp" #include "messages/juce_DeletedAtShutdown.cpp" #include "messages/juce_MessageListener.cpp" @@ -76,12 +73,11 @@ namespace juce #include "timers/juce_Timer.cpp" #include "interprocess/juce_InterprocessConnection.cpp" #include "interprocess/juce_InterprocessConnectionServer.cpp" -// END_AUTOINCLUDE +#include "interprocess/juce_ConnectedChildProcess.cpp" //============================================================================== #if JUCE_MAC #include "../juce_core/native/juce_osx_ObjCHelpers.h" - #include "../juce_core/native/juce_mac_ObjCSuffix.h" #include "native/juce_osx_MessageQueue.h" #include "native/juce_mac_MessageManager.mm" diff --git a/JuceLibraryCode/modules/juce_events/juce_events.h b/JuceLibraryCode/modules/juce_events/juce_events.h index 778ccf2..02f7c63 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.h +++ b/JuceLibraryCode/modules/juce_events/juce_events.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_EVENTS_JUCEHEADER__ -#define __JUCE_EVENTS_JUCEHEADER__ +#ifndef JUCE_EVENTS_H_INCLUDED +#define JUCE_EVENTS_H_INCLUDED //============================================================================= #include "../juce_core/juce_core.h" @@ -32,61 +31,27 @@ namespace juce { -// START_AUTOINCLUDE messages, broadcasters, timers, -// interprocess, native/juce_ScopedXLock* -#ifndef __JUCE_APPLICATIONBASE_JUCEHEADER__ - #include "messages/juce_ApplicationBase.h" -#endif -#ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__ - #include "messages/juce_CallbackMessage.h" -#endif -#ifndef __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ - #include "messages/juce_DeletedAtShutdown.h" -#endif -#ifndef __JUCE_MESSAGE_JUCEHEADER__ - #include "messages/juce_Message.h" -#endif -#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__ - #include "messages/juce_MessageListener.h" -#endif -#ifndef __JUCE_MESSAGEMANAGER_JUCEHEADER__ - #include "messages/juce_MessageManager.h" -#endif -#ifndef __JUCE_ACTIONBROADCASTER_JUCEHEADER__ - #include "broadcasters/juce_ActionBroadcaster.h" -#endif -#ifndef __JUCE_ACTIONLISTENER_JUCEHEADER__ - #include "broadcasters/juce_ActionListener.h" -#endif -#ifndef __JUCE_ASYNCUPDATER_JUCEHEADER__ - #include "broadcasters/juce_AsyncUpdater.h" -#endif -#ifndef __JUCE_CHANGEBROADCASTER_JUCEHEADER__ - #include "broadcasters/juce_ChangeBroadcaster.h" -#endif -#ifndef __JUCE_CHANGELISTENER_JUCEHEADER__ - #include "broadcasters/juce_ChangeListener.h" -#endif -#ifndef __JUCE_LISTENERLIST_JUCEHEADER__ - #include "broadcasters/juce_ListenerList.h" -#endif -#ifndef __JUCE_MULTITIMER_JUCEHEADER__ - #include "timers/juce_MultiTimer.h" -#endif -#ifndef __JUCE_TIMER_JUCEHEADER__ - #include "timers/juce_Timer.h" -#endif -#ifndef __JUCE_INTERPROCESSCONNECTION_JUCEHEADER__ - #include "interprocess/juce_InterprocessConnection.h" -#endif -#ifndef __JUCE_INTERPROCESSCONNECTIONSERVER_JUCEHEADER__ - #include "interprocess/juce_InterprocessConnectionServer.h" -#endif -#ifndef __JUCE_SCOPEDXLOCK_JUCEHEADER__ - #include "native/juce_ScopedXLock.h" -#endif -// END_AUTOINCLUDE +#include "messages/juce_MessageManager.h" +#include "messages/juce_Message.h" +#include "messages/juce_MessageListener.h" +#include "messages/juce_CallbackMessage.h" +#include "messages/juce_DeletedAtShutdown.h" +#include "messages/juce_NotificationType.h" +#include "messages/juce_ApplicationBase.h" +#include "messages/juce_Initialisation.h" +#include "broadcasters/juce_ListenerList.h" +#include "broadcasters/juce_ActionBroadcaster.h" +#include "broadcasters/juce_ActionListener.h" +#include "broadcasters/juce_AsyncUpdater.h" +#include "broadcasters/juce_ChangeListener.h" +#include "broadcasters/juce_ChangeBroadcaster.h" +#include "timers/juce_Timer.h" +#include "timers/juce_MultiTimer.h" +#include "interprocess/juce_InterprocessConnection.h" +#include "interprocess/juce_InterprocessConnectionServer.h" +#include "interprocess/juce_ConnectedChildProcess.h" +#include "native/juce_ScopedXLock.h" } -#endif // __JUCE_EVENTS_JUCEHEADER__ +#endif // JUCE_EVENTS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/juce_events.mm b/JuceLibraryCode/modules/juce_events/juce_events.mm index 37db549..45263f1 100644 --- a/JuceLibraryCode/modules/juce_events/juce_events.mm +++ b/JuceLibraryCode/modules/juce_events/juce_events.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_events/juce_module_info b/JuceLibraryCode/modules/juce_events/juce_module_info index 2557a95..534fe89 100644 --- a/JuceLibraryCode/modules/juce_events/juce_module_info +++ b/JuceLibraryCode/modules/juce_events/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_events", "name": "JUCE message and event handling classes", - "version": "2.0.21", + "version": "3.0.6", "description": "Classes for running an application's main event loop and sending/receiving messages, timers, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", @@ -17,5 +17,7 @@ "timers/*", "broadcasters/*", "interprocess/*", - "native/*" ] + "native/*" ], + + "LinuxLibs": "X11" } diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp index 045e604..507823d 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -27,6 +26,8 @@ JUCEApplicationBase::CreateInstanceFunction JUCEApplicationBase::createInstance JUCEApplicationBase* JUCEApplicationBase::appInstance = nullptr; JUCEApplicationBase::JUCEApplicationBase() + : appReturnValue (0), + stillInitialising (true) { jassert (isStandaloneApp() && appInstance == nullptr); appInstance = this; @@ -38,18 +39,251 @@ JUCEApplicationBase::~JUCEApplicationBase() appInstance = nullptr; } +void JUCEApplicationBase::setApplicationReturnValue (const int newReturnValue) noexcept +{ + appReturnValue = newReturnValue; +} + // This is called on the Mac and iOS where the OS doesn't allow the stack to unwind on shutdown.. void JUCEApplicationBase::appWillTerminateByForce() { JUCE_AUTORELEASEPOOL - { - const ScopedPointer app (appInstance); + { + const ScopedPointer app (appInstance); - if (app != nullptr) - app->shutdownApp(); + if (app != nullptr) + app->shutdownApp(); + } + + DeletedAtShutdown::deleteAll(); + MessageManager::deleteInstance(); + } +} + +void JUCEApplicationBase::quit() +{ + MessageManager::getInstance()->stopDispatchLoop(); +} + +void JUCEApplicationBase::sendUnhandledException (const std::exception* const e, + const char* const sourceFile, + const int lineNumber) +{ + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + app->unhandledException (e, sourceFile, lineNumber); +} + +//============================================================================== +#if ! (JUCE_IOS || JUCE_ANDROID) + #define JUCE_HANDLE_MULTIPLE_INSTANCES 1 +#endif + +#if JUCE_HANDLE_MULTIPLE_INSTANCES +struct JUCEApplicationBase::MultipleInstanceHandler : public ActionListener +{ +public: + MultipleInstanceHandler (const String& appName) + : appLock ("juceAppLock_" + appName) + { } - DeletedAtShutdown::deleteAll(); - MessageManager::deleteInstance(); + bool sendCommandLineToPreexistingInstance() + { + if (appLock.enter (0)) + return false; + + JUCEApplicationBase* const app = JUCEApplicationBase::getInstance(); + jassert (app != nullptr); + + MessageManager::broadcastMessage (app->getApplicationName() + + "/" + app->getCommandLineParameters()); + return true; + } + + void actionListenerCallback (const String& message) override + { + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + { + const String appName (app->getApplicationName()); + + if (message.startsWith (appName + "/")) + app->anotherInstanceStarted (message.substring (appName.length() + 1)); + } + } + +private: + InterProcessLock appLock; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultipleInstanceHandler) +}; + +bool JUCEApplicationBase::sendCommandLineToPreexistingInstance() +{ + jassert (multipleInstanceHandler == nullptr); // this must only be called once! + + multipleInstanceHandler = new MultipleInstanceHandler (getApplicationName()); + return multipleInstanceHandler->sendCommandLineToPreexistingInstance(); +} + +#else +struct JUCEApplicationBase::MultipleInstanceHandler {}; +#endif + +//============================================================================== +#if JUCE_ANDROID + +StringArray JUCEApplicationBase::getCommandLineParameterArray() { return StringArray(); } +String JUCEApplicationBase::getCommandLineParameters() { return String(); } + +#else + +#if JUCE_WINDOWS + +String JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameters() +{ + return CharacterFunctions::findEndOfToken (CharPointer_UTF16 (GetCommandLineW()), + CharPointer_UTF16 (L" "), + CharPointer_UTF16 (L"\"")).findEndOfWhitespace(); +} + +StringArray JUCE_CALLTYPE JUCEApplicationBase::getCommandLineParameterArray() +{ + StringArray s; + + int argc = 0; + if (LPWSTR* const argv = CommandLineToArgvW (GetCommandLineW(), &argc)) + { + s = StringArray (argv + 1, argc - 1); + LocalFree (argv); + } + + return s; +} + +#else + +#if JUCE_IOS + extern int juce_iOSMain (int argc, const char* argv[]); +#endif + +#if JUCE_MAC + extern void initialiseNSApplication(); +#endif + +extern const char* const* juce_argv; // declared in juce_core +extern int juce_argc; + +String JUCEApplicationBase::getCommandLineParameters() +{ + String argString; + + for (int i = 1; i < juce_argc; ++i) + { + String arg (juce_argv[i]); + + if (arg.containsChar (' ') && ! arg.isQuotedString()) + arg = arg.quoted ('"'); + + argString << arg << ' '; + } + + return argString.trim(); +} + +StringArray JUCEApplicationBase::getCommandLineParameterArray() +{ + return StringArray (juce_argv + 1, juce_argc - 1); +} + +int JUCEApplicationBase::main (int argc, const char* argv[]) +{ + JUCE_AUTORELEASEPOOL + { + juce_argc = argc; + juce_argv = argv; + + #if JUCE_MAC + initialiseNSApplication(); + #endif + + #if JUCE_IOS + return juce_iOSMain (argc, argv); + #else + return JUCEApplicationBase::main(); + #endif + } +} + +#endif + +//============================================================================== +int JUCEApplicationBase::main() +{ + ScopedJuceInitialiser_GUI libraryInitialiser; + jassert (createInstance != nullptr); + + const ScopedPointer app (createInstance()); + jassert (app != nullptr); + + if (! app->initialiseApp()) + return 0; + + JUCE_TRY + { + // loop until a quit message is received.. + MessageManager::getInstance()->runDispatchLoop(); + } + JUCE_CATCH_EXCEPTION + + return app->shutdownApp(); +} + +#endif + +//============================================================================== +bool JUCEApplicationBase::initialiseApp() +{ + #if JUCE_HANDLE_MULTIPLE_INSTANCES + if ((! moreThanOneInstanceAllowed()) && sendCommandLineToPreexistingInstance()) + { + DBG ("Another instance is running - quitting..."); + return false; + } + #endif + + // let the app do its setting-up.. + initialise (getCommandLineParameters()); + + stillInitialising = false; + + if (MessageManager::getInstance()->hasStopMessageBeenSent()) + return false; + + #if JUCE_HANDLE_MULTIPLE_INSTANCES + if (multipleInstanceHandler != nullptr) + MessageManager::getInstance()->registerBroadcastListener (multipleInstanceHandler); + #endif + + return true; +} + +int JUCEApplicationBase::shutdownApp() +{ + jassert (JUCEApplicationBase::getInstance() == this); + + #if JUCE_HANDLE_MULTIPLE_INSTANCES + if (multipleInstanceHandler != nullptr) + MessageManager::getInstance()->deregisterBroadcastListener (multipleInstanceHandler); + #endif + + JUCE_TRY + { + // give the app a chance to clean up.. + shutdown(); + } + JUCE_CATCH_EXCEPTION + + multipleInstanceHandler = nullptr; + return getApplicationReturnValue(); } diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h index aa8fe11..51f098c 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.h @@ -1,41 +1,86 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_APPLICATIONBASE_JUCEHEADER__ -#define __JUCE_APPLICATIONBASE_JUCEHEADER__ +#ifndef JUCE_APPLICATIONBASE_H_INCLUDED +#define JUCE_APPLICATIONBASE_H_INCLUDED //============================================================================== /** Abstract base class for application classes. - This class shouldn't be used directly - you'll normally use JUCEApplication as - the base for your app, and that inherits from this, adding some more functionality - to it. + Note that in the juce_gui_basics module, there's a utility class JUCEApplication + which derives from JUCEApplicationBase, and takes care of a few chores. Most + of the time you'll want to derive your class from JUCEApplication rather than + using JUCEApplicationBase directly, but if you're not using the juce_gui_basics + module then you might need to go straight to this base class. - @see JUCEApplication + Any application that wants to run an event loop must declare a subclass of + JUCEApplicationBase, 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 suitable platform-specific + boilerplate code to launch the app. + + e.g. @code + class MyJUCEApp : public JUCEApplication + { + public: + MyJUCEApp() {} + ~MyJUCEApp() {} + + void initialise (const String& commandLine) + { + myMainWindow = new MyApplicationWindow(); + myMainWindow->setBounds (100, 100, 400, 500); + myMainWindow->setVisible (true); + } + + void shutdown() + { + myMainWindow = nullptr; + } + + const String getApplicationName() + { + return "Super JUCE-o-matic"; + } + + const String getApplicationVersion() + { + return "1.0"; + } + + private: + ScopedPointer myMainWindow; + }; + + // this generates boilerplate code to launch our app class: + START_JUCE_APPLICATION (MyJUCEApp) + @endcode + + @see JUCEApplication, START_JUCE_APPLICATION */ class JUCE_API JUCEApplicationBase { @@ -52,13 +97,10 @@ public: static JUCEApplicationBase* getInstance() noexcept { return appInstance; } //============================================================================== - /** Returns the application's name. - An application must implement this to name itself. - */ + /** Returns the application's name. */ virtual const String getApplicationName() = 0; - /** Returns the application's version number. - */ + /** Returns the application's version number. */ virtual const String getApplicationVersion() = 0; /** Checks whether multiple instances of the app are allowed. @@ -85,8 +127,10 @@ public: 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. + @param commandLineParameters the line passed in does not include the name of + the executable, just the parameter list. To get the + parameters as an array, you can call + JUCEApplication::getCommandLineParameters() @see shutdown, quit */ virtual void initialise (const String& commandLineParameters) = 0; @@ -125,6 +169,16 @@ public: */ virtual void systemRequestedQuit() = 0; + /** This method is called when the application is being put into background mode + by the operating system. + */ + virtual void suspended() = 0; + + /** This method is called when the application is being woken from background mode + by the operating system. + */ + virtual void resumed() = 0; + /** 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. @@ -137,27 +191,93 @@ public: const String& sourceFilename, int lineNumber) = 0; + //============================================================================== + /** 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 + */ + static void quit(); + + //============================================================================== + /** Returns the application's command line parameters as a set of strings. + @see getCommandLineParameters + */ + static StringArray JUCE_CALLTYPE getCommandLineParameterArray(); + + /** Returns the application's command line parameters as a single string. + @see getCommandLineParameterArray + */ + static String JUCE_CALLTYPE getCommandLineParameters(); + + //============================================================================== + /** 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) noexcept; + + /** Returns the value that has been set as the application's exit code. + @see setApplicationReturnValue + */ + int getApplicationReturnValue() const noexcept { return appReturnValue; } + //============================================================================== /** Returns true if this executable is running as an app (as opposed to being a plugin or other kind of shared library. */ - static inline bool isStandaloneApp() noexcept { return createInstance != nullptr; } + static bool isStandaloneApp() noexcept { return createInstance != nullptr; } + + /** 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 noexcept { return stillInitialising; } + //============================================================================== #ifndef DOXYGEN + // The following methods are for internal use only... + static int main(); + static int main (int argc, const char* argv[]); + static void appWillTerminateByForce(); typedef JUCEApplicationBase* (*CreateInstanceFunction)(); static CreateInstanceFunction createInstance; -protected: - virtual int shutdownApp() = 0; + virtual bool initialiseApp(); + static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); + bool sendCommandLineToPreexistingInstance(); #endif private: //============================================================================== static JUCEApplicationBase* appInstance; + int appReturnValue; + bool stillInitialising; - JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase); + struct MultipleInstanceHandler; + friend struct MultipleInstanceHandler; + friend struct ContainerDeletePolicy; + ScopedPointer multipleInstanceHandler; + + int shutdownApp(); + + JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase) }; -#endif // __JUCE_APPLICATIONBASE_JUCEHEADER__ +#endif // JUCE_APPLICATIONBASE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h b/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h index bd77ea0..799defd 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_CallbackMessage.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_CALLBACKMESSAGE_JUCEHEADER__ -#define __JUCE_CALLBACKMESSAGE_JUCEHEADER__ - -#include "juce_MessageManager.h" +#ifndef JUCE_CALLBACKMESSAGE_H_INCLUDED +#define JUCE_CALLBACKMESSAGE_H_INCLUDED //============================================================================== @@ -69,8 +66,8 @@ public: private: // Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered // messages still in the system event queue. These aren't harmful, but can cause annoying assertions. - JUCE_DECLARE_NON_COPYABLE (CallbackMessage); + JUCE_DECLARE_NON_COPYABLE (CallbackMessage) }; -#endif // __JUCE_CALLBACKMESSAGE_JUCEHEADER__ +#endif // JUCE_CALLBACKMESSAGE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp index 2d0c42e..d3c6e97 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -34,7 +33,7 @@ DeletedAtShutdown::DeletedAtShutdown() DeletedAtShutdown::~DeletedAtShutdown() { const SpinLock::ScopedLockType sl (deletedAtShutdownLock); - getObjects().removeValue (this); + getObjects().removeFirstMatchingValue (this); } void DeletedAtShutdown::deleteAll() diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h index bf5ea0c..ce1f10e 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_DeletedAtShutdown.h @@ -1,37 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ -#define __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ +#ifndef JUCE_DELETEDATSHUTDOWN_H_INCLUDED +#define JUCE_DELETEDATSHUTDOWN_H_INCLUDED //============================================================================== /** Classes derived from this will be automatically deleted when the application exits. - After JUCEApplication::shutdown() has been called, any objects derived from + After JUCEApplicationBase::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. @@ -56,14 +55,14 @@ 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. + in the shutdown code of the JUCEApplicationBase class. */ static void deleteAll(); private: static Array & getObjects(); - JUCE_DECLARE_NON_COPYABLE (DeletedAtShutdown); + JUCE_DECLARE_NON_COPYABLE (DeletedAtShutdown) }; -#endif // __JUCE_DELETEDATSHUTDOWN_JUCEHEADER__ +#endif // JUCE_DELETEDATSHUTDOWN_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h b/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h new file mode 100644 index 0000000..84f5e82 --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/messages/juce_Initialisation.h @@ -0,0 +1,118 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_INITIALISATION_H_INCLUDED +#define JUCE_INITIALISATION_H_INCLUDED + + +//============================================================================== +/** 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 + Process::setCurrentModuleInstanceHandle() method. + + @see shutdownJuce_GUI() +*/ +JUCE_API void JUCE_CALLTYPE 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() +*/ +JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI(); + + +//============================================================================== +/** A utility object that helps you initialise and shutdown Juce correctly + using an RAII pattern. + + When the first instance of this class is created, it calls initialiseJuce_GUI(), + and when the last instance is deleted, it calls shutdownJuce_GUI(), so that you + can easily be sure that as long as at least one instance of the class exists, the + library will be initialised. + + 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. + + Be careful with your threading though - to be safe, you should always make sure + that these objects are created and deleted on the message thread. +*/ +class JUCE_API ScopedJuceInitialiser_GUI +{ +public: + /** The constructor simply calls initialiseJuce_GUI(). */ + ScopedJuceInitialiser_GUI(); + + /** The destructor simply calls shutdownJuce_GUI(). */ + ~ScopedJuceInitialiser_GUI(); +}; + + +//============================================================================== +/** + To start a JUCE app, use this macro: START_JUCE_APPLICATION (AppSubClass) where + AppSubClass is the name of a class derived from JUCEApplication or JUCEApplicationBase. + + See the JUCEApplication and JUCEApplicationBase class documentation for more details. +*/ +#ifdef DOXYGEN + #define START_JUCE_APPLICATION(AppClass) +#elif JUCE_ANDROID + #define START_JUCE_APPLICATION(AppClass) \ + juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } + +#else + #if JUCE_WINDOWS + #if defined (WINAPI) || defined (_WINDOWS_) + #define JUCE_MAIN_FUNCTION int __stdcall WinMain (HINSTANCE, HINSTANCE, const LPSTR, int) + #elif defined (_UNICODE) + #define JUCE_MAIN_FUNCTION int __stdcall WinMain (void*, void*, const wchar_t*, int) + #else + #define JUCE_MAIN_FUNCTION int __stdcall WinMain (void*, void*, const char*, int) + #endif + #define JUCE_MAIN_FUNCTION_ARGS + #else + #define JUCE_MAIN_FUNCTION int main (int argc, char* argv[]) + #define JUCE_MAIN_FUNCTION_ARGS argc, (const char**) argv + #endif + + #define START_JUCE_APPLICATION(AppClass) \ + static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ + extern "C" JUCE_MAIN_FUNCTION \ + { \ + juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; \ + return juce::JUCEApplicationBase::main (JUCE_MAIN_FUNCTION_ARGS); \ + } +#endif + +#endif // JUCE_INITIALISATION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_Message.h b/JuceLibraryCode/modules/juce_events/messages/juce_Message.h index 0946f4a..4ae55f6 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_Message.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_Message.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MESSAGE_JUCEHEADER__ -#define __JUCE_MESSAGE_JUCEHEADER__ +#ifndef JUCE_MESSAGE_H_INCLUDED +#define JUCE_MESSAGE_H_INCLUDED class MessageListener; @@ -55,12 +54,12 @@ public: private: friend class MessageListener; WeakReference recipient; - void messageCallback(); + void messageCallback() override; // Avoid the leak-detector because for plugins, the host can unload our DLL with undelivered // messages still in the system event queue. These aren't harmful, but can cause annoying assertions. - JUCE_DECLARE_NON_COPYABLE (Message); + JUCE_DECLARE_NON_COPYABLE (Message) }; -#endif // __JUCE_MESSAGE_JUCEHEADER__ +#endif // JUCE_MESSAGE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp index 5c21e42..b398ffa 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -28,8 +27,7 @@ Message::~Message() {} void Message::messageCallback() { - MessageListener* const r = recipient; - if (r != nullptr) + if (MessageListener* const r = recipient) r->handleMessage (*this); } diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h index abd1ae0..ddd51e2 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageListener.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MESSAGELISTENER_JUCEHEADER__ -#define __JUCE_MESSAGELISTENER_JUCEHEADER__ - -#include "juce_MessageManager.h" +#ifndef JUCE_MESSAGELISTENER_H_INCLUDED +#define JUCE_MESSAGELISTENER_H_INCLUDED //============================================================================== @@ -72,4 +69,4 @@ private: }; -#endif // __JUCE_MESSAGELISTENER_JUCEHEADER__ +#endif // JUCE_MESSAGELISTENER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp index 74c4163..9383d50 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp @@ -1,44 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -class MessageManager::QuitMessage : public MessageManager::MessageBase -{ -public: - QuitMessage() {} - - void messageCallback() - { - MessageManager* const mm = MessageManager::instance; - if (mm != nullptr) - mm->quitMessageReceived = true; - } - - JUCE_DECLARE_NON_COPYABLE (QuitMessage); -}; - -//============================================================================== MessageManager::MessageManager() noexcept : quitMessagePosted (false), quitMessageReceived (false), @@ -72,7 +55,7 @@ MessageManager* MessageManager::getInstance() return instance; } -inline MessageManager* MessageManager::getInstanceWithoutCreating() noexcept +MessageManager* MessageManager::getInstanceWithoutCreating() noexcept { return instance; } @@ -95,42 +78,51 @@ void MessageManager::MessageBase::post() #if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS) void MessageManager::runDispatchLoop() { - jassert (isThisTheMessageThread()); // must only be called by the message thread - runDispatchLoopUntil (-1); } -void MessageManager::stopDispatchLoop() -{ - (new QuitMessage())->post(); - quitMessagePosted = true; -} - bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { jassert (isThisTheMessageThread()); // must only be called by the message thread const int64 endTime = Time::currentTimeMillis() + millisecondsToRunFor; - while ((millisecondsToRunFor < 0 || endTime > Time::currentTimeMillis()) - && ! quitMessageReceived) + while (! quitMessageReceived) { JUCE_TRY { if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0)) - { - const int msToWait = (int) (endTime - Time::currentTimeMillis()); - - if (msToWait > 0) - Thread::sleep (jmin (5, msToWait)); - } + Thread::sleep (1); } JUCE_CATCH_EXCEPTION + + if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime) + break; } return ! quitMessageReceived; } +class MessageManager::QuitMessage : public MessageManager::MessageBase +{ +public: + QuitMessage() {} + + void messageCallback() override + { + if (MessageManager* const mm = MessageManager::instance) + mm->quitMessageReceived = true; + } + + JUCE_DECLARE_NON_COPYABLE (QuitMessage) +}; + +void MessageManager::stopDispatchLoop() +{ + (new QuitMessage())->post(); + quitMessagePosted = true; +} + #endif //============================================================================== @@ -141,7 +133,7 @@ public: : result (nullptr), func (f), parameter (param) {} - void messageCallback() + void messageCallback() override { result = (*func) (parameter); finished.signal(); @@ -154,7 +146,7 @@ private: MessageCallbackFunction* const func; void* const parameter; - JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback); + JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback) }; void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* const func, void* const parameter) @@ -234,7 +226,7 @@ class MessageManagerLock::BlockingMessage : public MessageManager::MessageBase public: BlockingMessage() noexcept {} - void messageCallback() + void messageCallback() override { lockedEvent.signal(); releaseEvent.wait(); @@ -242,7 +234,7 @@ public: WaitableEvent lockedEvent, releaseEvent; - JUCE_DECLARE_NON_COPYABLE (BlockingMessage); + JUCE_DECLARE_NON_COPYABLE (BlockingMessage) }; //============================================================================== @@ -327,13 +319,22 @@ JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI(); JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI() { JUCE_AUTORELEASEPOOL - MessageManager::getInstance(); + { + MessageManager::getInstance(); + } } JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI(); JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI() { JUCE_AUTORELEASEPOOL - DeletedAtShutdown::deleteAll(); - MessageManager::deleteInstance(); + { + DeletedAtShutdown::deleteAll(); + MessageManager::deleteInstance(); + } } + +static int numScopedInitInstances = 0; + +ScopedJuceInitialiser_GUI::ScopedJuceInitialiser_GUI() { if (numScopedInitInstances++ == 0) initialiseJuce_GUI(); } +ScopedJuceInitialiser_GUI::~ScopedJuceInitialiser_GUI() { if (--numScopedInitInstances == 0) shutdownJuce_GUI(); } diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h index b66e8bd..d50c35c 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MESSAGEMANAGER_JUCEHEADER__ -#define __JUCE_MESSAGEMANAGER_JUCEHEADER__ +#ifndef JUCE_MESSAGEMANAGER_H_INCLUDED +#define JUCE_MESSAGEMANAGER_H_INCLUDED class MessageManagerLock; class ThreadPoolJob; @@ -42,7 +41,7 @@ typedef void* (MessageCallbackFunction) (void* userData); /** This class is in charge of the application's event-dispatch loop. - @see Message, CallbackMessage, MessageManagerLock, JUCEApplication + @see Message, CallbackMessage, MessageManagerLock, JUCEApplication, JUCEApplicationBase */ class JUCE_API MessageManager { @@ -82,7 +81,7 @@ public: */ bool hasStopMessageBeenSent() const noexcept { return quitMessagePosted; } - #if JUCE_MODAL_LOOPS_PERMITTED + #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN /** Synchronously dispatches messages until a given time has elapsed. Returns false if a quit message has been posted by a call to stopDispatchLoop(), @@ -129,7 +128,7 @@ public: */ Thread::ThreadID getCurrentMessageThread() const noexcept { return messageThreadId; } - /** Returns true if the caller thread has currenltly got the message manager locked. + /** Returns true if the caller thread has currently got the message manager locked. see the MessageManagerLock class for more info about this. @@ -175,7 +174,7 @@ public: typedef ReferenceCountedObjectPtr Ptr; - JUCE_DECLARE_NON_COPYABLE (MessageBase); + JUCE_DECLARE_NON_COPYABLE (MessageBase) }; //============================================================================== @@ -208,7 +207,7 @@ private: static void doPlatformSpecificShutdown(); static bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager) }; @@ -322,8 +321,8 @@ private: bool attemptLock (Thread*, ThreadPoolJob*); - JUCE_DECLARE_NON_COPYABLE (MessageManagerLock); + JUCE_DECLARE_NON_COPYABLE (MessageManagerLock) }; -#endif // __JUCE_MESSAGEMANAGER_JUCEHEADER__ +#endif // JUCE_MESSAGEMANAGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h b/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h new file mode 100644 index 0000000..c3ecccc --- /dev/null +++ b/JuceLibraryCode/modules/juce_events/messages/juce_NotificationType.h @@ -0,0 +1,42 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_NOTIFICATIONTYPE_H_INCLUDED +#define JUCE_NOTIFICATIONTYPE_H_INCLUDED + +//============================================================================== +/** + These enums are used in various classes to indicate whether a notification + event should be sent out. +*/ +enum NotificationType +{ + dontSendNotification = 0, /**< No notification message should be sent. */ + sendNotification = 1, /**< Requests a notification message, either synchronous or not. */ + sendNotificationSync, /**< Requests a synchronous notification. */ + sendNotificationAsync, /**< Requests an asynchronous notification. */ +}; + + +#endif // JUCE_NOTIFICATIONTYPE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/native/juce_ScopedXLock.h b/JuceLibraryCode/modules/juce_events/native/juce_ScopedXLock.h index 6df27e3..d2136c7 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_ScopedXLock.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_ScopedXLock.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_SCOPEDXLOCK_JUCEHEADER__ -#define __JUCE_SCOPEDXLOCK_JUCEHEADER__ +#ifndef JUCE_SCOPEDXLOCK_H_INCLUDED +#define JUCE_SCOPEDXLOCK_H_INCLUDED //============================================================================== @@ -48,4 +47,4 @@ public: }; #endif -#endif // __JUCE_SCOPEDXLOCK_JUCEHEADER__ +#endif // JUCE_SCOPEDXLOCK_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp index b370e1e..733994a 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_android_Messaging.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -69,7 +68,7 @@ void MessageManager::stopDispatchLoop() { QuitCallback() {} - void messageCallback() + void messageCallback() override { android.activity.callVoidMethod (JuceAppActivity.finish); } diff --git a/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm b/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm index a6476ab..bdfed65 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm +++ b/JuceLibraryCode/modules/juce_events/native/juce_ios_MessageManager.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -32,29 +31,33 @@ void MessageManager::runDispatchLoop() void MessageManager::stopDispatchLoop() { [[[UIApplication sharedApplication] delegate] applicationWillTerminate: [UIApplication sharedApplication]]; - exit (0); // iPhone apps get no mercy.. + exit (0); // iOS apps get no mercy.. } bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { JUCE_AUTORELEASEPOOL - jassert (isThisTheMessageThread()); // must only be called by the message thread - - uint32 endTime = Time::getMillisecondCounter() + millisecondsToRunFor; - NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001]; - - while (! quitMessagePosted) { - JUCE_AUTORELEASEPOOL + jassert (isThisTheMessageThread()); // must only be called by the message thread - [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode - beforeDate: endDate]; + uint32 startTime = Time::getMillisecondCounter(); + NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001]; - if (millisecondsToRunFor >= 0 && Time::getMillisecondCounter() >= endTime) - break; + while (! quitMessagePosted) + { + JUCE_AUTORELEASEPOOL + { + [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode + beforeDate: endDate]; + + if (millisecondsToRunFor >= 0 + && Time::getMillisecondCounter() >= startTime + (uint32) millisecondsToRunFor) + break; + } + } + + return ! quitMessagePosted; } - - return ! quitMessagePosted; } //============================================================================== @@ -81,4 +84,5 @@ bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* cons void MessageManager::broadcastMessage (const String&) { + // N/A on current iOS } diff --git a/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp index fccc359..48e28a7 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_linux_Messaging.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -31,8 +30,11 @@ Display* display = nullptr; Window juce_messageWindowHandle = None; XContext windowHandleXContext; // This is referenced from Windowing.cpp -extern void juce_windowMessageReceive (XEvent* event); // Defined in Windowing.cpp -extern void juce_handleSelectionRequest (XSelectionRequestEvent &evt); // Defined in Clipboard.cpp +typedef bool (*WindowMessageReceiveCallback) (XEvent&); +WindowMessageReceiveCallback dispatchWindowMessage = nullptr; + +typedef void (*SelectionRequestCallback) (XSelectionRequestEvent&); +SelectionRequestCallback handleSelectionRequest = nullptr; //============================================================================== ScopedXLock::ScopedXLock() { XLockDisplay (display); } @@ -89,8 +91,8 @@ public: // to keep everything running smoothly.. if ((++totalEventCount & 1) != 0) return dispatchNextXEvent() || dispatchNextInternalMessage(); - else - return dispatchNextInternalMessage() || dispatchNextXEvent(); + + return dispatchNextInternalMessage() || dispatchNextXEvent(); } // Wait for an event (either XEvent, or an internal Message) @@ -165,10 +167,11 @@ private: XNextEvent (display, &evt); } - if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle) - juce_handleSelectionRequest (evt.xselectionrequest); - else if (evt.xany.window != juce_messageWindowHandle) - juce_windowMessageReceive (&evt); + if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle + && handleSelectionRequest != nullptr) + handleSelectionRequest (evt.xselectionrequest); + else if (evt.xany.window != juce_messageWindowHandle && dispatchWindowMessage != nullptr) + dispatchWindowMessage (evt); return true; } @@ -192,18 +195,17 @@ private: bool dispatchNextInternalMessage() { - const MessageManager::MessageBase::Ptr msg (popNextMessage()); - - if (msg == nullptr) - return false; - - JUCE_TRY + if (const MessageManager::MessageBase::Ptr msg = popNextMessage()) { - msg->messageCallback(); + JUCE_TRY + { + msg->messageCallback(); + return true; + } + JUCE_CATCH_EXCEPTION } - JUCE_CATCH_EXCEPTION - return true; + return false; } }; @@ -220,7 +222,7 @@ namespace LinuxErrorHandling //============================================================================== // Usually happens when client-server connection is broken - int ioErrorHandler (Display* display) + int ioErrorHandler (Display*) { DBG ("ERROR: connection to X server broken.. terminating."); @@ -360,7 +362,7 @@ bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* cons return true; } -void MessageManager::broadcastMessage (const String& value) +void MessageManager::broadcastMessage (const String& /* value */) { /* TODO */ } diff --git a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm index 92219c0..3aed8a2 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -29,76 +28,62 @@ AppFocusChangeCallback appFocusChangeCallback = nullptr; typedef bool (*CheckEventBlockedByModalComps) (NSEvent*); CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr; -//============================================================================== -/* 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. +typedef void (*MenuTrackingChangedCallback)(bool); +MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr; - 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 +//============================================================================== +struct AppDelegate { public: - AppDelegateRedirector() {} - virtual ~AppDelegateRedirector() {} - - virtual NSApplicationTerminateReply shouldTerminate() + AppDelegate() { - if (JUCEApplicationBase::getInstance() != nullptr) - { - JUCEApplicationBase::getInstance()->systemRequestedQuit(); + static AppDelegateClass cls; + delegate = [cls.createInstance() init]; - if (! MessageManager::getInstance()->hasStopMessageBeenSent()) - return NSTerminateCancel; + NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; + + [center addObserver: delegate selector: @selector (mainMenuTrackingBegan:) + name: NSMenuDidBeginTrackingNotification object: nil]; + [center addObserver: delegate selector: @selector (mainMenuTrackingEnded:) + name: NSMenuDidEndTrackingNotification object: nil]; + + if (JUCEApplicationBase::isStandaloneApp()) + { + [NSApp setDelegate: delegate]; + + [[NSDistributedNotificationCenter defaultCenter] addObserver: delegate + selector: @selector (broadcastMessageCallback:) + name: getBroacastEventName() + object: nil]; + } + else + { + [center addObserver: delegate selector: @selector (applicationDidResignActive:) + name: NSApplicationDidResignActiveNotification object: NSApp]; + + [center addObserver: delegate selector: @selector (applicationDidBecomeActive:) + name: NSApplicationDidBecomeActiveNotification object: NSApp]; + + [center addObserver: delegate selector: @selector (applicationWillUnhide:) + name: NSApplicationWillUnhideNotification object: NSApp]; + } + } + + ~AppDelegate() + { + [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: delegate]; + [[NSNotificationCenter defaultCenter] removeObserver: delegate]; + + if (JUCEApplicationBase::isStandaloneApp()) + { + [NSApp setDelegate: nil]; + + [[NSDistributedNotificationCenter defaultCenter] removeObserver: delegate + name: getBroacastEventName() + object: nil]; } - return NSTerminateNow; - } - - virtual void willTerminate() - { - JUCEApplicationBase::appWillTerminateByForce(); - } - - virtual BOOL openFile (NSString* filename) - { - if (JUCEApplicationBase::getInstance() != nullptr) - { - JUCEApplicationBase::getInstance()->anotherInstanceStarted (quotedIfContainsSpaces (filename)); - return YES; - } - - return NO; - } - - virtual void openFiles (NSArray* filenames) - { - StringArray files; - for (unsigned int i = 0; i < [filenames count]; ++i) - files.add (quotedIfContainsSpaces ((NSString*) [filenames objectAtIndex: i])); - - if (files.size() > 0 && JUCEApplicationBase::getInstance() != nullptr) - JUCEApplicationBase::getInstance()->anotherInstanceStarted (files.joinIntoString (" ")); - } - - virtual void focusChanged() - { - if (appFocusChangeCallback != nullptr) - (*appFocusChangeCallback)(); - } - - virtual void deleteSelf() - { - delete this; - } - - void postMessage (MessageManager::MessageBase* const m) - { - messageQueue.post (m); + [delegate release]; } static NSString* getBroacastEventName() @@ -106,198 +91,179 @@ public: return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64())); } -private: - CFRunLoopRef runLoop; - CFRunLoopSourceRef runLoopSource; MessageQueue messageQueue; + id delegate; - static String quotedIfContainsSpaces (NSString* file) +private: + //============================================================================== + struct AppDelegateClass : public ObjCClass { - String s (nsStringToJuce (file)); - if (s.containsChar (' ')) - s = s.quoted ('"'); + AppDelegateClass() : ObjCClass ("JUCEAppDelegate_") + { + addMethod (@selector (applicationShouldTerminate:), applicationShouldTerminate, "I@:@"); + addMethod (@selector (applicationWillTerminate:), applicationWillTerminate, "v@:@"); + addMethod (@selector (application:openFile:), application_openFile, "c@:@@"); + addMethod (@selector (application:openFiles:), application_openFiles, "v@:@@"); + addMethod (@selector (applicationDidBecomeActive:), applicationDidBecomeActive, "v@:@"); + addMethod (@selector (applicationDidResignActive:), applicationDidResignActive, "v@:@"); + addMethod (@selector (applicationWillUnhide:), applicationWillUnhide, "v@:@"); + addMethod (@selector (broadcastMessageCallback:), broadcastMessageCallback, "v@:@"); + addMethod (@selector (mainMenuTrackingBegan:), mainMenuTrackingBegan, "v@:@"); + addMethod (@selector (mainMenuTrackingEnded:), mainMenuTrackingEnded, "v@:@"); + addMethod (@selector (dummyMethod), dummyMethod, "v@:"); - return s; - } + registerClass(); + } + + private: + static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*) + { + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + { + app->systemRequestedQuit(); + + if (! MessageManager::getInstance()->hasStopMessageBeenSent()) + return NSTerminateCancel; + } + + return NSTerminateNow; + } + + static void applicationWillTerminate (id /*self*/, SEL, NSNotification*) + { + JUCEApplicationBase::appWillTerminateByForce(); + } + + static BOOL application_openFile (id /*self*/, SEL, NSApplication*, NSString* filename) + { + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + { + app->anotherInstanceStarted (quotedIfContainsSpaces (filename)); + return YES; + } + + return NO; + } + + static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames) + { + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) + { + StringArray files; + + for (NSString* f in filenames) + files.add (quotedIfContainsSpaces (f)); + + if (files.size() > 0) + app->anotherInstanceStarted (files.joinIntoString (" ")); + } + } + + static void applicationDidBecomeActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } + static void applicationDidResignActive (id /*self*/, SEL, NSNotification*) { focusChanged(); } + static void applicationWillUnhide (id /*self*/, SEL, NSNotification*) { focusChanged(); } + + static void broadcastMessageCallback (id /*self*/, SEL, NSNotification* n) + { + NSDictionary* dict = (NSDictionary*) [n userInfo]; + const String messageString (nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")])); + MessageManager::getInstance()->deliverBroadcastMessage (messageString); + } + + static void mainMenuTrackingBegan (id /*self*/, SEL, NSNotification*) + { + if (menuTrackingChangedCallback != nullptr) + (*menuTrackingChangedCallback) (true); + } + + static void mainMenuTrackingEnded (id /*self*/, SEL, NSNotification*) + { + if (menuTrackingChangedCallback != nullptr) + (*menuTrackingChangedCallback) (false); + } + + static void dummyMethod (id /*self*/, SEL) {} // (used as a way of running a dummy thread) + + private: + static void focusChanged() + { + if (appFocusChangeCallback != nullptr) + (*appFocusChangeCallback)(); + } + + static String quotedIfContainsSpaces (NSString* file) + { + String s (nsStringToJuce (file)); + if (s.containsChar (' ')) + s = s.quoted ('"'); + + return s; + } + }; }; - -} // (juce namespace) - -using namespace juce; - -#define JuceAppDelegate MakeObjCClassName(JuceAppDelegate) - //============================================================================== -@interface JuceAppDelegate : NSObject -{ -@public - AppDelegateRedirector* redirector; -} - -- (JuceAppDelegate*) init; -- (void) dealloc; -- (void) unregisterObservers; -- (BOOL) application: (NSApplication*) theApplication openFile: (NSString*) filename; -- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames; -- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app; -- (void) applicationWillTerminate: (NSNotification*) aNotification; -- (void) applicationDidBecomeActive: (NSNotification*) aNotification; -- (void) applicationDidResignActive: (NSNotification*) aNotification; -- (void) applicationWillUnhide: (NSNotification*) aNotification; -- (void) broadcastMessageCallback: (NSNotification*) info; -- (void) dummyMethod; -@end - -@implementation JuceAppDelegate - -- (JuceAppDelegate*) init -{ - [super init]; - - redirector = new AppDelegateRedirector(); - - if (JUCEApplicationBase::isStandaloneApp()) - { - [NSApp setDelegate: self]; - - [[NSDistributedNotificationCenter defaultCenter] addObserver: self - selector: @selector (broadcastMessageCallback:) - name: AppDelegateRedirector::getBroacastEventName() - object: nil]; - } - else - { - NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - - [center addObserver: self selector: @selector (applicationDidResignActive:) - name: NSApplicationDidResignActiveNotification object: NSApp]; - - [center addObserver: self selector: @selector (applicationDidBecomeActive:) - name: NSApplicationDidBecomeActiveNotification object: NSApp]; - - [center addObserver: self selector: @selector (applicationWillUnhide:) - name: NSApplicationWillUnhideNotification object: NSApp]; - } - - return self; -} - -- (void) dealloc -{ - redirector->deleteSelf(); - [super dealloc]; -} - -- (void) unregisterObservers -{ - [[NSRunLoop currentRunLoop] cancelPerformSelectorsWithTarget: self]; - [[NSNotificationCenter defaultCenter] removeObserver: self]; - - if (JUCEApplicationBase::isStandaloneApp()) - { - [NSApp setDelegate: nil]; - - [[NSDistributedNotificationCenter defaultCenter] removeObserver: self - name: AppDelegateRedirector::getBroacastEventName() - object: nil]; - } -} - -- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication*) app -{ - (void) app; - return redirector->shouldTerminate(); -} - -- (void) applicationWillTerminate: (NSNotification*) aNotification -{ - (void) aNotification; - redirector->willTerminate(); -} - -- (BOOL) application: (NSApplication*) app openFile: (NSString*) filename -{ - (void) app; - return redirector->openFile (filename); -} - -- (void) application: (NSApplication*) sender openFiles: (NSArray*) filenames -{ - (void) sender; - return redirector->openFiles (filenames); -} - -- (void) applicationDidBecomeActive: (NSNotification*) notification -{ - (void) notification; - redirector->focusChanged(); -} - -- (void) applicationDidResignActive: (NSNotification*) notification -{ - (void) notification; - redirector->focusChanged(); -} - -- (void) applicationWillUnhide: (NSNotification*) notification -{ - (void) notification; - redirector->focusChanged(); -} - -- (void) broadcastMessageCallback: (NSNotification*) n -{ - NSDictionary* dict = (NSDictionary*) [n userInfo]; - const String messageString (nsStringToJuce ((NSString*) [dict valueForKey: nsStringLiteral ("message")])); - MessageManager::getInstance()->deliverBroadcastMessage (messageString); -} - -- (void) dummyMethod {} // (used as a way of running a dummy thread) - -@end - -//============================================================================== -namespace juce -{ - -static JuceAppDelegate* juceAppDelegate = nil; - void MessageManager::runDispatchLoop() { if (! quitMessagePosted) // check that the quit message wasn't already posted.. { JUCE_AUTORELEASEPOOL - - // must only be called by the message thread! - jassert (isThisTheMessageThread()); - - #if JUCE_CATCH_UNHANDLED_EXCEPTIONS - @try { + // must only be called by the message thread! + jassert (isThisTheMessageThread()); + + #if JUCE_PROJUCER_LIVE_BUILD + runDispatchLoopUntil (std::numeric_limits::max()); + #else + #if JUCE_CATCH_UNHANDLED_EXCEPTIONS + @try + { + [NSApp run]; + } + @catch (NSException* e) + { + // An AppKit exception will kill the app, but at least this provides a chance to log it., + std::runtime_error ex (std::string ("NSException: ") + [[e name] UTF8String] + ", Reason:" + [[e reason] UTF8String]); + JUCEApplication::sendUnhandledException (&ex, __FILE__, __LINE__); + } + @finally + { + } + #else [NSApp run]; + #endif + #endif } - @catch (NSException* e) - { - // An AppKit exception will kill the app, but at least this provides a chance to log it., - std::runtime_error ex (std::string ("NSException: ") + [[e name] UTF8String] + ", Reason:" + [[e reason] UTF8String]); - JUCEApplication::sendUnhandledException (&ex, __FILE__, __LINE__); - } - @finally - { - } - #else - [NSApp run]; - #endif } } +static void shutdownNSApp() +{ + [NSApp stop: nil]; + [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated) + [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; +} + void MessageManager::stopDispatchLoop() { quitMessagePosted = true; - [NSApp stop: nil]; - [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated) - [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; + + #if ! JUCE_PROJUCER_LIVE_BUILD + if (isThisTheMessageThread()) + { + shutdownNSApp(); + } + else + { + struct QuitCallback : public CallbackMessage + { + QuitCallback() {} + void messageCallback() override { shutdownNSApp(); } + }; + + (new QuitCallback())->post(); + } + #endif } #if JUCE_MODAL_LOOPS_PERMITTED @@ -311,19 +277,20 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) while (! quitMessagePosted) { JUCE_AUTORELEASEPOOL + { + CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true); - CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true); + NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask + untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001] + inMode: NSDefaultRunLoopMode + dequeue: YES]; - NSEvent* e = [NSApp nextEventMatchingMask: NSAnyEventMask - untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001] - inMode: NSDefaultRunLoopMode - dequeue: YES]; + if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e))) + [NSApp sendEvent: e]; - if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e))) - [NSApp sendEvent: e]; - - if (Time::getMillisecondCounter() >= endTime) - break; + if (Time::getMillisecondCounter() >= endTime) + break; + } } return ! quitMessagePosted; @@ -335,36 +302,37 @@ void initialiseNSApplication(); void initialiseNSApplication() { JUCE_AUTORELEASEPOOL - [NSApplication sharedApplication]; + { + [NSApplication sharedApplication]; + } } +static AppDelegate* appDelegate = nullptr; + void MessageManager::doPlatformSpecificInitialisation() { - if (juceAppDelegate == nil) - juceAppDelegate = [[JuceAppDelegate alloc] init]; + if (appDelegate == nil) + appDelegate = new AppDelegate(); #if ! (defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) // This launches a dummy thread, which forces Cocoa to initialise NSThreads correctly (needed prior to 10.5) if (! [NSThread isMultiThreaded]) [NSThread detachNewThreadSelector: @selector (dummyMethod) - toTarget: juceAppDelegate + toTarget: appDelegate->delegate withObject: nil]; #endif } void MessageManager::doPlatformSpecificShutdown() { - if (juceAppDelegate != nil) - { - [juceAppDelegate unregisterObservers]; - [juceAppDelegate release]; - juceAppDelegate = nil; - } + delete appDelegate; + appDelegate = nullptr; } bool MessageManager::postMessageToSystemQueue (MessageBase* message) { - juceAppDelegate->redirector->postMessage (message); + jassert (appDelegate != nil); + appDelegate->messageQueue.post (message); return true; } @@ -373,7 +341,27 @@ void MessageManager::broadcastMessage (const String& message) NSDictionary* info = [NSDictionary dictionaryWithObject: juceStringToNS (message) forKey: nsStringLiteral ("message")]; - [[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegateRedirector::getBroacastEventName() + [[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegate::getBroacastEventName() object: nil userInfo: info]; } + +// Special function used by some plugin classes to re-post carbon events +void repostCurrentNSEvent(); +void repostCurrentNSEvent() +{ + struct EventReposter : public CallbackMessage + { + EventReposter() : e ([[NSApp currentEvent] retain]) {} + ~EventReposter() { [e release]; } + + void messageCallback() override + { + [NSApp postEvent: e atStart: YES]; + } + + NSEvent* e; + }; + + (new EventReposter())->post(); +} diff --git a/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h b/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h index d478424..6ffbefe 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_osx_MessageQueue.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_OSX_MESSAGEQUEUE_JUCEHEADER__ -#define __JUCE_OSX_MESSAGEQUEUE_JUCEHEADER__ +#ifndef JUCE_OSX_MESSAGEQUEUE_H_INCLUDED +#define JUCE_OSX_MESSAGEQUEUE_H_INCLUDED //============================================================================== /* An internal message pump class used in OSX and iOS. */ @@ -39,7 +38,8 @@ public: runLoop = CFRunLoopGetCurrent(); #endif - CFRunLoopSourceContext sourceContext = { 0 }; + CFRunLoopSourceContext sourceContext; + zerostruct (sourceContext); // (can't use "= { 0 }" on this object because it's typedef'ed as a C struct) sourceContext.info = this; sourceContext.perform = runLoopSourceCallback; runLoopSource = CFRunLoopSourceCreate (kCFAllocatorDefault, 1, &sourceContext); @@ -62,7 +62,6 @@ public: private: ReferenceCountedArray messages; - CriticalSection lock; CFRunLoopRef runLoop; CFRunLoopSourceRef runLoopSource; @@ -74,12 +73,13 @@ private: return false; JUCE_AUTORELEASEPOOL - - JUCE_TRY { - nextMessage->messageCallback(); + JUCE_TRY + { + nextMessage->messageCallback(); + } + JUCE_CATCH_EXCEPTION } - JUCE_CATCH_EXCEPTION return true; } @@ -100,4 +100,4 @@ private: } }; -#endif // __JUCE_OSX_MESSAGEQUEUE_JUCEHEADER__ +#endif // JUCE_OSX_MESSAGEQUEUE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h b/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h index b0b634f..c4747ec 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h +++ b/JuceLibraryCode/modules/juce_events/native/juce_win32_HiddenMessageWindow.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_WIN32_HIDDENMESSAGEWINDOW_JUCEHEADER__ -#define __JUCE_WIN32_HIDDENMESSAGEWINDOW_JUCEHEADER__ +#ifndef JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED +#define JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED //============================================================================== class HiddenMessageWindow @@ -104,6 +103,13 @@ public: protected: virtual void systemDeviceChanged() = 0; + void triggerAsyncDeviceChangeCallback() + { + // We'll pause before sending a message, because on device removal, the OS hasn't always updated + // its device lists correctly at this point. This also helps avoid repeated callbacks. + startTimer (500); + } + private: HiddenMessageWindow messageWindow; @@ -115,18 +121,18 @@ private: || wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/ || wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/)) { - // We'll pause before sending a message, because on device removal, the OS hasn't always updated - // its device lists correctly at this point. This also helps avoid repeated callbacks. - ((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA))->startTimer (500); + ((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA)) + ->triggerAsyncDeviceChangeCallback(); } return DefWindowProc (h, message, wParam, lParam); } - void timerCallback() + void timerCallback() override { + stopTimer(); systemDeviceChanged(); } }; -#endif // __JUCE_WIN32_HIDDENMESSAGEWINDOW_JUCEHEADER__ +#endif // JUCE_WIN32_HIDDENMESSAGEWINDOW_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp b/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp index 2e8ecb7..0bc0f78 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp +++ b/JuceLibraryCode/modules/juce_events/native/juce_win32_Messaging.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -101,7 +100,7 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPend using namespace WindowsMessageHelpers; MSG m; - if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, 0)) + if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, PM_NOREMOVE)) return false; if (GetMessage (&m, (HWND) 0, 0, 0) >= 0) @@ -112,8 +111,7 @@ bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPend } else if (m.message == WM_QUIT) { - JUCEApplicationBase* const app = JUCEApplicationBase::getInstance(); - if (app != nullptr) + if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) app->systemRequestedQuit(); } else if (isEventBlockedByModalComps == nullptr || ! isEventBlockedByModalComps (m)) diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp index 753d560..4885e7b 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp +++ b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.cpp @@ -1,56 +1,48 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -class MultiTimer::MultiTimerCallback : public Timer +struct MultiTimerCallback : public Timer { -public: - MultiTimerCallback (const int timerId_, MultiTimer& owner_) - : timerId (timerId_), - owner (owner_) + MultiTimerCallback (const int tid, MultiTimer& mt) noexcept + : owner (mt), timerID (tid) { } - void timerCallback() + void timerCallback() override { - owner.timerCallback (timerId); + owner.timerCallback (timerID); } - const int timerId; - -private: MultiTimer& owner; + const int timerID; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiTimerCallback) }; //============================================================================== -MultiTimer::MultiTimer() noexcept -{ -} - -MultiTimer::MultiTimer (const MultiTimer&) noexcept -{ -} +MultiTimer::MultiTimer() noexcept {} +MultiTimer::MultiTimer (const MultiTimer&) noexcept {} MultiTimer::~MultiTimer() { @@ -59,63 +51,55 @@ MultiTimer::~MultiTimer() } //============================================================================== -void MultiTimer::startTimer (const int timerId, const int intervalInMilliseconds) noexcept +Timer* MultiTimer::getCallback (int timerID) const noexcept { - const SpinLock::ScopedLockType sl (timerListLock); - for (int i = timers.size(); --i >= 0;) { - MultiTimerCallback* const t = timers.getUnchecked(i); + MultiTimerCallback* const t = static_cast (timers.getUnchecked(i)); - if (t->timerId == timerId) - { - t->startTimer (intervalInMilliseconds); - return; - } + if (t->timerID == timerID) + return t; } - MultiTimerCallback* const newTimer = new MultiTimerCallback (timerId, *this); - timers.add (newTimer); - newTimer->startTimer (intervalInMilliseconds); + return nullptr; } -void MultiTimer::stopTimer (const int timerId) noexcept +void MultiTimer::startTimer (const int timerID, const int intervalInMilliseconds) noexcept { const SpinLock::ScopedLockType sl (timerListLock); - for (int i = timers.size(); --i >= 0;) - { - MultiTimerCallback* const t = timers.getUnchecked(i); + Timer* timer = getCallback (timerID); - if (t->timerId == timerId) - t->stopTimer(); - } + if (timer == nullptr) + timers.add (timer = new MultiTimerCallback (timerID, *this)); + + timer->startTimer (intervalInMilliseconds); } -bool MultiTimer::isTimerRunning (const int timerId) const noexcept +void MultiTimer::stopTimer (const int timerID) noexcept { const SpinLock::ScopedLockType sl (timerListLock); - for (int i = timers.size(); --i >= 0;) - { - const MultiTimerCallback* const t = timers.getUnchecked(i); - if (t->timerId == timerId) - return t->isTimerRunning(); - } + if (Timer* const t = getCallback (timerID)) + t->stopTimer(); +} + +bool MultiTimer::isTimerRunning (const int timerID) const noexcept +{ + const SpinLock::ScopedLockType sl (timerListLock); + + if (Timer* const t = getCallback (timerID)) + return t->isTimerRunning(); return false; } -int MultiTimer::getTimerInterval (const int timerId) const noexcept +int MultiTimer::getTimerInterval (const int timerID) const noexcept { const SpinLock::ScopedLockType sl (timerListLock); - for (int i = timers.size(); --i >= 0;) - { - const MultiTimerCallback* const t = timers.getUnchecked(i); - if (t->timerId == timerId) - return t->getTimerInterval(); - } + if (Timer* const t = getCallback (timerID)) + return t->getTimerInterval(); return 0; } diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h index 7907318..c7fe0e4 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h +++ b/JuceLibraryCode/modules/juce_events/timers/juce_MultiTimer.h @@ -1,32 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MULTITIMER_JUCEHEADER__ -#define __JUCE_MULTITIMER_JUCEHEADER__ +#ifndef JUCE_MULTITIMER_H_INCLUDED +#define JUCE_MULTITIMER_H_INCLUDED -#include "juce_Timer.h" //============================================================================== /** @@ -60,7 +58,7 @@ protected: Note that this timer will not contain any running timers, even if the one you're copying from was running. */ - MultiTimer (const MultiTimer& other) noexcept; + MultiTimer (const MultiTimer&) noexcept; public: //============================================================================== @@ -74,7 +72,7 @@ public: It's perfectly ok to call startTimer() or stopTimer() from within this callback to change the subsequent intervals. */ - virtual void timerCallback (int timerId) = 0; + virtual void timerCallback (int timerID) = 0; //============================================================================== /** Starts a timer and sets the length of interval required. @@ -83,14 +81,14 @@ public: 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 + @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) noexcept; + void startTimer (int timerID, int intervalInMilliseconds) noexcept; /** Stops a timer. @@ -101,30 +99,28 @@ public: be currently executing may be allowed to finish before the method returns. */ - void stopTimer (int timerId) noexcept; + void stopTimer (int timerID) noexcept; //============================================================================== /** 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 noexcept; + bool isTimerRunning (int timerID) const noexcept; /** 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. + @returns the timer's interval in milliseconds if it's running, or 0 if no + timer was running for the ID number specified. */ - int getTimerInterval (int timerId) const noexcept; + int getTimerInterval (int timerID) const noexcept; //============================================================================== private: - class MultiTimerCallback; SpinLock timerListLock; - OwnedArray timers; + OwnedArray timers; + Timer* getCallback (int) const noexcept; MultiTimer& operator= (const MultiTimer&); }; -#endif // __JUCE_MULTITIMER_JUCEHEADER__ +#endif // JUCE_MULTITIMER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp index 1c99c75..6b7343a 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp +++ b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -47,7 +46,7 @@ public: instance = nullptr; } - void run() + void run() override { uint32 lastTime = Time::getMillisecondCounter(); MessageManager::MessageBase::Ptr messageToSend (new CallTimersMessage()); @@ -83,7 +82,7 @@ public: 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; + const uint32 messageDeliveryTimeout = now + 300; while (callbackNeeded.get() != 0) { @@ -93,7 +92,10 @@ public: return; if (Time::getMillisecondCounter() > messageDeliveryTimeout) + { + messageToSend->post(); break; + } } } } @@ -190,7 +192,7 @@ private: { CallTimersMessage() {} - void messageCallback() + void messageCallback() override { if (instance != nullptr) instance->callTimers(); @@ -201,18 +203,9 @@ private: void addTimer (Timer* const t) noexcept { #if JUCE_DEBUG - Timer* tt = firstTimer; - - while (tt != nullptr) - { - // trying to add a timer that's already here - shouldn't get to this point, - // so if you get this assertion, let me know! - jassert (tt != t); - - tt = tt->next; - } - - jassert (t->previous == nullptr && t->next == nullptr); + // trying to add a timer that's already here - shouldn't get to this point, + // so if you get this assertion, let me know! + jassert (! timerExists (t)); #endif Timer* i = firstTimer; @@ -246,23 +239,9 @@ private: void removeTimer (Timer* const t) noexcept { #if JUCE_DEBUG - Timer* tt = firstTimer; - bool found = false; - - while (tt != nullptr) - { - if (tt == t) - { - found = true; - break; - } - - tt = tt->next; - } - // trying to remove a timer that's not here - shouldn't get to this point, // so if you get this assertion, let me know! - jassert (found); + jassert (timerExists (t)); #endif if (t->previous != nullptr) @@ -293,32 +272,35 @@ private: return firstTimer != nullptr ? firstTimer->countdownMs : 1000; } - void handleAsyncUpdate() + void handleAsyncUpdate() override { startThread (7); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread); + #if JUCE_DEBUG + bool timerExists (Timer* const t) const noexcept + { + for (Timer* tt = firstTimer; tt != nullptr; tt = tt->next) + if (tt == t) + return true; + + return false; + } + #endif + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TimerThread) }; Timer::TimerThread* Timer::TimerThread::instance = nullptr; Timer::TimerThread::LockType Timer::TimerThread::lock; //============================================================================== -#if JUCE_DEBUG -static SortedSet activeTimers; -#endif - Timer::Timer() noexcept : countdownMs (0), periodMs (0), previous (nullptr), next (nullptr) { - #if JUCE_DEBUG - const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - activeTimers.add (this); - #endif } Timer::Timer (const Timer&) noexcept @@ -327,30 +309,17 @@ Timer::Timer (const Timer&) noexcept previous (nullptr), next (nullptr) { - #if JUCE_DEBUG - const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - activeTimers.add (this); - #endif } Timer::~Timer() { stopTimer(); - - #if JUCE_DEBUG - activeTimers.removeValue (this); - #endif } void Timer::startTimer (const int interval) noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - #if JUCE_DEBUG - // this isn't a valid object! Your timer might be a dangling pointer or something.. - jassert (activeTimers.contains (this)); - #endif - if (periodMs == 0) { countdownMs = interval; @@ -367,11 +336,6 @@ void Timer::stopTimer() noexcept { const TimerThread::LockType::ScopedLockType sl (TimerThread::lock); - #if JUCE_DEBUG - // this isn't a valid object! Your timer might be a dangling pointer or something.. - jassert (activeTimers.contains (this)); - #endif - if (periodMs > 0) { TimerThread::remove (this); diff --git a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h index cde616b..a2f823a 100644 --- a/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h +++ b/JuceLibraryCode/modules/juce_events/timers/juce_Timer.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TIMER_JUCEHEADER__ -#define __JUCE_TIMER_JUCEHEADER__ +#ifndef JUCE_TIMER_H_INCLUDED +#define JUCE_TIMER_H_INCLUDED //============================================================================== @@ -48,7 +47,7 @@ structure is very similar to the Timer class, but contains multiple timers internally, each one identified by an ID number. - @see MultiTimer + @see HighResolutionTimer, MultiTimer */ class JUCE_API Timer { @@ -132,4 +131,4 @@ private: Timer& operator= (const Timer&); }; -#endif // __JUCE_TIMER_JUCEHEADER__ +#endif // JUCE_TIMER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp index b496250..aaf9468 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -27,96 +26,113 @@ namespace ColourHelpers { static uint8 floatToUInt8 (const float n) noexcept { - return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : (uint8) (n * 255.0f)); + return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : static_cast (n * 255.996f)); } - // This is an adjusted brightness value, based on the way the human - // eye responds to different colour channels.. - static float getPerceivedBrightness (float r, float g, float b) noexcept + //============================================================================== + struct HSB { - return std::sqrt (r * r * 0.241f - + g * g * 0.691f - + b * b * 0.068f); - } -} - -//============================================================================== -struct HSB -{ - HSB (const Colour& col) noexcept - { - const int r = col.getRed(); - const int g = col.getGreen(); - const int b = col.getBlue(); - - const int hi = jmax (r, g, b); - const int lo = jmin (r, g, b); - - if (hi != 0) + HSB (Colour col) noexcept { - saturation = (hi - lo) / (float) hi; + const int r = col.getRed(); + const int g = col.getGreen(); + const int b = col.getBlue(); - if (saturation > 0) + const int hi = jmax (r, g, b); + const int lo = jmin (r, g, b); + + if (hi != 0) { - const float invDiff = 1.0f / (hi - lo); + saturation = (hi - lo) / (float) hi; - const float red = (hi - r) * invDiff; - const float green = (hi - g) * invDiff; - const float blue = (hi - b) * invDiff; + if (saturation > 0) + { + const float invDiff = 1.0f / (hi - lo); - if (r == hi) - hue = blue - green; - else if (g == hi) - hue = 2.0f + red - blue; + const float red = (hi - r) * invDiff; + const float green = (hi - g) * invDiff; + const float blue = (hi - b) * invDiff; + + if (r == hi) + hue = blue - green; + else if (g == hi) + hue = 2.0f + red - blue; + else + hue = 4.0f + green - red; + + hue *= 1.0f / 6.0f; + + if (hue < 0) + ++hue; + } else - hue = 4.0f + green - red; - - hue *= 1.0f / 6.0f; - - if (hue < 0) - ++hue; + { + hue = 0; + } } else { - hue = 0; + saturation = hue = 0; } + + brightness = hi / 255.0f; } - else + + Colour toColour (Colour original) const noexcept { - saturation = hue = 0; + return Colour (hue, saturation, brightness, original.getAlpha()); } - brightness = hi / 255.0f; - } + static PixelARGB toRGB (float h, float s, float v, const uint8 alpha) noexcept + { + v = jlimit (0.0f, 255.0f, v * 255.0f); + const uint8 intV = (uint8) roundToInt (v); - Colour toColour (const Colour& original) const noexcept + if (s <= 0) + return PixelARGB (alpha, intV, intV, intV); + + s = jmin (1.0f, s); + h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors + const float f = h - std::floor (h); + const uint8 x = (uint8) roundToInt (v * (1.0f - s)); + + if (h < 1.0f) return PixelARGB (alpha, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x); + if (h < 2.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - s * f)), intV, x); + if (h < 3.0f) return PixelARGB (alpha, x, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f))))); + if (h < 4.0f) return PixelARGB (alpha, x, (uint8) roundToInt (v * (1.0f - s * f)), intV); + if (h < 5.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x, intV); + return PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f))); + } + + float hue, saturation, brightness; + }; + + //============================================================================== + struct YIQ { - return Colour (hue, saturation, brightness, original.getAlpha()); - } + YIQ (Colour c) noexcept + { + const float r = c.getFloatRed(); + const float g = c.getFloatGreen(); + const float b = c.getFloatBlue(); - static PixelARGB toRGB (float h, float s, float v, const uint8 alpha) noexcept - { - v = jlimit (0.0f, 255.0f, v * 255.0f); - const uint8 intV = (uint8) roundToInt (v); + y = 0.2999f * r + 0.5870f * g + 0.1140f * b; + i = 0.5957f * r - 0.2744f * g - 0.3212f * b; + q = 0.2114f * r - 0.5225f * g - 0.3113f * b; + alpha = c.getFloatAlpha(); + } - if (s <= 0) - return PixelARGB (alpha, intV, intV, intV); + Colour toColour() const noexcept + { + return Colour::fromFloatRGBA (y + 0.9563f * i + 0.6210f * q, + y - 0.2721f * i - 0.6474f * q, + y - 1.1070f * i + 1.7046f * q, + alpha); + } - s = jmin (1.0f, s); - h = (h - std::floor (h)) * 6.0f + 0.00001f; // need a small adjustment to compensate for rounding errors - const float f = h - std::floor (h); - const uint8 x = (uint8) roundToInt (v * (1.0f - s)); - - if (h < 1.0f) return PixelARGB (alpha, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x); - if (h < 2.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - s * f)), intV, x); - if (h < 3.0f) return PixelARGB (alpha, x, intV, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f))))); - if (h < 4.0f) return PixelARGB (alpha, x, (uint8) roundToInt (v * (1.0f - s * f)), intV); - if (h < 5.0f) return PixelARGB (alpha, (uint8) roundToInt (v * (1.0f - (s * (1.0f - f)))), x, intV); - else return PixelARGB (alpha, intV, x, (uint8) roundToInt (v * (1.0f - s * f))); - } - - float hue, saturation, brightness; -}; + float y, i, q, alpha; + }; +} //============================================================================== Colour::Colour() noexcept @@ -139,8 +155,7 @@ bool Colour::operator== (const Colour& other) const noexcept { return argb.ge bool Colour::operator!= (const Colour& other) const noexcept { return argb.getARGB() != other.argb.getARGB(); } //============================================================================== -Colour::Colour (const uint32 argb_) noexcept - : argb (argb_) +Colour::Colour (const uint32 col) noexcept : argb (col) { } @@ -171,11 +186,13 @@ Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const floa Colour Colour::fromFloatRGBA (const float red, const float green, const float blue, const float alpha) noexcept { - return Colour (ColourHelpers::floatToUInt8 (red), ColourHelpers::floatToUInt8 (green), ColourHelpers::floatToUInt8 (blue), alpha); + return Colour (ColourHelpers::floatToUInt8 (red), + ColourHelpers::floatToUInt8 (green), + ColourHelpers::floatToUInt8 (blue), alpha); } Colour::Colour (const float hue, const float saturation, const float brightness, const float alpha) noexcept - : argb (HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha))) + : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha))) { } @@ -185,7 +202,7 @@ Colour Colour::fromHSV (const float hue, const float saturation, const float bri } Colour::Colour (const float hue, const float saturation, const float brightness, const uint8 alpha) noexcept - : argb (HSB::toRGB (hue, saturation, brightness, alpha)) + : argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, alpha)) { } @@ -243,7 +260,7 @@ Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept } //============================================================================== -Colour Colour::overlaidWith (const Colour& src) const noexcept +Colour Colour::overlaidWith (Colour src) const noexcept { const int destAlpha = getAlpha(); @@ -264,7 +281,7 @@ Colour Colour::overlaidWith (const Colour& src) const noexcept (uint8) resA); } -Colour Colour::interpolatedWith (const Colour& other, float proportionOfOther) const noexcept +Colour Colour::interpolatedWith (Colour other, float proportionOfOther) const noexcept { if (proportionOfOther <= 0) return *this; @@ -289,38 +306,45 @@ float Colour::getFloatAlpha() const noexcept { return getAlpha() / 255.0f; } //============================================================================== void Colour::getHSB (float& h, float& s, float& v) const noexcept { - const HSB hsb (*this); + const ColourHelpers::HSB hsb (*this); h = hsb.hue; s = hsb.saturation; v = hsb.brightness; } -float Colour::getHue() const noexcept { return HSB (*this).hue; } -float Colour::getSaturation() const noexcept { return HSB (*this).saturation; } -float Colour::getBrightness() const noexcept { return HSB (*this).brightness; } +float Colour::getHue() const noexcept { return ColourHelpers::HSB (*this).hue; } +float Colour::getSaturation() const noexcept { return ColourHelpers::HSB (*this).saturation; } +float Colour::getBrightness() const noexcept { return ColourHelpers::HSB (*this).brightness; } -Colour Colour::withHue (float h) const noexcept { HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); } -Colour Colour::withSaturation (float s) const noexcept { HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); } -Colour Colour::withBrightness (float v) const noexcept { HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); } +Colour Colour::withHue (float h) const noexcept { ColourHelpers::HSB hsb (*this); hsb.hue = h; return hsb.toColour (*this); } +Colour Colour::withSaturation (float s) const noexcept { ColourHelpers::HSB hsb (*this); hsb.saturation = s; return hsb.toColour (*this); } +Colour Colour::withBrightness (float v) const noexcept { ColourHelpers::HSB hsb (*this); hsb.brightness = v; return hsb.toColour (*this); } + +float Colour::getPerceivedBrightness() const noexcept +{ + return std::sqrt (0.241f * square (getFloatRed()) + + 0.691f * square (getFloatGreen()) + + 0.068f * square (getFloatBlue())); +} //============================================================================== Colour Colour::withRotatedHue (const float amountToRotate) const noexcept { - HSB hsb (*this); + ColourHelpers::HSB hsb (*this); hsb.hue += amountToRotate; return hsb.toColour (*this); } Colour Colour::withMultipliedSaturation (const float amount) const noexcept { - HSB hsb (*this); + ColourHelpers::HSB hsb (*this); hsb.saturation = jmin (1.0f, hsb.saturation * amount); return hsb.toColour (*this); } Colour Colour::withMultipliedBrightness (const float amount) const noexcept { - HSB hsb (*this); + ColourHelpers::HSB hsb (*this); hsb.brightness = jmin (1.0f, hsb.brightness * amount); return hsb.toColour (*this); } @@ -356,16 +380,31 @@ Colour Colour::greyLevel (const float brightness) noexcept //============================================================================== Colour Colour::contrasting (const float amount) const noexcept { - return overlaidWith ((ColourHelpers::getPerceivedBrightness (getFloatRed(), getFloatGreen(), getFloatBlue()) >= 0.5f - ? Colours::black - : Colours::white).withAlpha (amount)); + return overlaidWith ((getPerceivedBrightness() >= 0.5f + ? Colours::black + : Colours::white).withAlpha (amount)); } -Colour Colour::contrasting (const Colour& colour1, - const Colour& colour2) noexcept +Colour Colour::contrasting (Colour target, float minContrast) const noexcept { - const float b1 = colour1.getBrightness(); - const float b2 = colour2.getBrightness(); + const ColourHelpers::YIQ bg (*this); + ColourHelpers::YIQ fg (target); + + if (std::abs (bg.y - fg.y) >= minContrast) + return target; + + const float y1 = jmax (0.0f, bg.y - minContrast); + const float y2 = jmin (1.0f, bg.y + minContrast); + fg.y = (std::abs (y1 - bg.y) > std::abs (y2 - bg.y)) ? y1 : y2; + + return fg.toColour(); +} + +Colour Colour::contrasting (Colour colour1, + Colour colour2) noexcept +{ + const float b1 = colour1.getPerceivedBrightness(); + const float b2 = colour2.getPerceivedBrightness(); float best = 0.0f; float bestDist = 0.0f; @@ -392,9 +431,9 @@ String Colour::toString() const return String::toHexString ((int) argb.getARGB()); } -Colour Colour::fromString (const String& encodedColourString) +Colour Colour::fromString (StringRef encodedColourString) { - return Colour ((uint32) encodedColourString.getHexValue32()); + return Colour ((uint32) CharacterFunctions::HexParser::parse (encodedColourString.text)); } String Colour::toDisplayString (const bool includeAlphaValue) const diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h index a0251dd..4d84504 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colour.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_COLOUR_JUCEHEADER__ -#define __JUCE_COLOUR_JUCEHEADER__ - -#include "../colour/juce_PixelFormats.h" +#ifndef JUCE_COLOUR_H_INCLUDED +#define JUCE_COLOUR_H_INCLUDED //============================================================================== @@ -142,37 +139,31 @@ public: //============================================================================== /** Returns the red component of this colour. - @returns a value between 0x00 and 0xff. */ uint8 getRed() const noexcept { return argb.getRed(); } /** Returns the green component of this colour. - @returns a value between 0x00 and 0xff. */ uint8 getGreen() const noexcept { return argb.getGreen(); } /** Returns the blue component of this colour. - @returns a value between 0x00 and 0xff. */ uint8 getBlue() const noexcept { 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 noexcept; /** Returns the green component of this colour as a floating point value. - @returns a value between 0.0 and 1.0 */ float getFloatGreen() const noexcept; /** Returns the blue component of this colour as a floating point value. - @returns a value between 0.0 and 1.0 */ float getFloatBlue() const noexcept; @@ -220,25 +211,21 @@ public: Colour withAlpha (float newAlpha) const noexcept; /** 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. */ Colour withMultipliedAlpha (float alphaMultiplier) const noexcept; //============================================================================== /** 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. + If the foreground colour is semi-transparent, it is blended onto this colour accordingly. */ - Colour overlaidWith (const Colour& foregroundColour) const noexcept; + Colour overlaidWith (Colour foregroundColour) const noexcept; /** 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. */ - Colour interpolatedWith (const Colour& other, float proportionOfOther) const noexcept; + Colour interpolatedWith (Colour other, float proportionOfOther) const noexcept; //============================================================================== /** Returns the colour's hue component. @@ -256,6 +243,12 @@ public: */ float getBrightness() const noexcept; + /** Returns a skewed brightness value, adjusted to better reflect the way the human + eye responds to different colour channels. This makes it better than getBrightness() + for comparing differences in brightness. + */ + float getPerceivedBrightness() const noexcept; + /** Returns the colour's hue, saturation and brightness components all at once. The values returned are in the range 0.0 to 1.0 */ @@ -324,32 +317,36 @@ public: */ Colour contrasting (float amount = 1.0f) const noexcept; + /** Returns a colour that is as close as possible to a target colour whilst + still being in contrast to this one. + + The colour that is returned will be the targetColour, but with its luminosity + nudged up or down so that it differs from the luminosity of this colour + by at least the amount specified by minLuminosityDiff. + */ + Colour contrasting (Colour targetColour, float minLuminosityDiff) const noexcept; + /** 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 Colour contrasting (const Colour& colour1, - const Colour& colour2) noexcept; + static Colour contrasting (Colour colour1, + Colour colour2) noexcept; //============================================================================== /** Returns an opaque shade of grey. - @param brightness the level of grey to return - 0 is black, 1.0 is white */ static Colour greyLevel (float brightness) noexcept; //============================================================================== /** Returns a stringified version of this colour. - The string can be turned back into a colour using the fromString() method. */ String toString() const; - /** Reads the colour from a string that was created with toString(). - */ - static Colour fromString (const String& encodedColourString); + /** Reads the colour from a string that was created with toString(). */ + static Colour fromString (StringRef encodedColourString); /** Returns the colour as a hex string in the form RRGGBB or AARRGGBB. */ String toDisplayString (bool includeAlphaValue) const; @@ -360,4 +357,4 @@ private: }; -#endif // __JUCE_COLOUR_JUCEHEADER__ +#endif // JUCE_COLOUR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp index 9a2ba69..75d1712 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -33,8 +32,8 @@ ColourGradient::ColourGradient() noexcept #endif } -ColourGradient::ColourGradient (const Colour& colour1, const float x1_, const float y1_, - const Colour& colour2, const float x2_, const float y2_, +ColourGradient::ColourGradient (Colour colour1, const float x1_, const float y1_, + Colour colour2, const float x2_, const float y2_, const bool isRadial_) : point1 (x1_, y1_), point2 (x2_, y2_), @@ -66,7 +65,7 @@ void ColourGradient::clearColours() colours.clear(); } -int ColourGradient::addColour (const double proportionAlongGradient, const Colour& colour) +int ColourGradient::addColour (const double proportionAlongGradient, Colour colour) { // must be within the two end-points jassert (proportionAlongGradient >= 0 && proportionAlongGradient <= 1.0); @@ -119,7 +118,7 @@ Colour ColourGradient::getColour (const int index) const noexcept return Colour(); } -void ColourGradient::setColour (int index, const Colour& newColour) noexcept +void ColourGradient::setColour (int index, Colour newColour) noexcept { if (isPositiveAndBelow (index, colours.size())) colours.getReference (index).colour = newColour; @@ -149,7 +148,7 @@ Colour ColourGradient::getColourAtPosition (const double position) const noexcep //============================================================================== void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int numEntries) const noexcept { - JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its co-ordinates? + JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its coordinates? jassert (colours.size() >= 2); jassert (numEntries > 0); jassert (colours.getReference(0).position == 0); // The first colour specified has to go at position 0 @@ -181,7 +180,7 @@ void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock & lookupTable) const { - JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its co-ordinates? + JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED // Trying to use this object without setting its coordinates? jassert (colours.size() >= 2); const int numEntries = jlimit (1, jmax (1, (colours.size() - 1) << 8), diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h index dd4caca..73e6631 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_COLOURGRADIENT_JUCEHEADER__ -#define __JUCE_COLOURGRADIENT_JUCEHEADER__ - -#include "juce_Colour.h" -#include "../geometry/juce_Point.h" +#ifndef JUCE_COLOURGRADIENT_H_INCLUDED +#define JUCE_COLOURGRADIENT_H_INCLUDED //============================================================================== @@ -57,8 +53,8 @@ public: @see ColourGradient */ - ColourGradient (const Colour& colour1, float x1, float y1, - const Colour& colour2, float x2, float y2, + ColourGradient (Colour colour1, float x1, float y1, + Colour colour2, float x2, float y2, bool isRadial); /** Creates an uninitialised gradient. @@ -91,7 +87,7 @@ public: @returns the index at which the new point was added */ int addColour (double proportionAlongGradient, - const Colour& colour); + Colour colour); /** Removes one of the colours from the gradient. */ void removeColour (int index); @@ -117,7 +113,7 @@ public: /** Changes the colour at a given index. The index is from 0 to getNumColours() - 1. */ - void setColour (int index, const Colour& newColour) noexcept; + void setColour (int index, Colour newColour) noexcept; /** Returns the an interpolated colour at any position along the gradient. @param position the position along the gradient, between 0 and 1 @@ -155,8 +151,8 @@ public: */ bool isRadial; - bool operator== (const ColourGradient& other) const noexcept; - bool operator!= (const ColourGradient& other) const noexcept; + bool operator== (const ColourGradient&) const noexcept; + bool operator!= (const ColourGradient&) const noexcept; private: @@ -165,21 +161,21 @@ private: { ColourPoint() noexcept {} - ColourPoint (const double position_, const Colour& colour_) noexcept - : position (position_), colour (colour_) + ColourPoint (const double pos, Colour col) noexcept + : position (pos), colour (col) {} - bool operator== (const ColourPoint& other) const noexcept; - bool operator!= (const ColourPoint& other) const noexcept; + bool operator== (const ColourPoint&) const noexcept; + bool operator!= (const ColourPoint&) const noexcept; double position; Colour colour; }; - Array colours; + Array colours; - JUCE_LEAK_DETECTOR (ColourGradient); + JUCE_LEAK_DETECTOR (ColourGradient) }; -#endif // __JUCE_COLOURGRADIENT_JUCEHEADER__ +#endif // JUCE_COLOURGRADIENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.cpp b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.cpp index 65d4c36..fbde0ba 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.cpp +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -166,7 +165,7 @@ const Colour Colours::yellowgreen (0xff9acd32); //============================================================================== Colour Colours::findColourForName (const String& colourName, - const Colour& defaultColour) + Colour defaultColour) { static const uint32 presets[] = { diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.h b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.h index 5f3ee81..90e4aa5 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.h +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_Colours.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_COLOURS_JUCEHEADER__ -#define __JUCE_COLOURS_JUCEHEADER__ - -#include "juce_Colour.h" +#ifndef JUCE_COLOURS_H_INCLUDED +#define JUCE_COLOURS_H_INCLUDED //============================================================================== @@ -96,7 +93,7 @@ public: colour passed in as the defaultColour parameter is returned. */ static JUCE_API Colour findColourForName (const String& colourName, - const Colour& defaultColour); + Colour defaultColour); private: //============================================================================== @@ -104,7 +101,7 @@ private: // static values in it. Colours(); - JUCE_DECLARE_NON_COPYABLE (Colours); + JUCE_DECLARE_NON_COPYABLE (Colours) }; -#endif // __JUCE_COLOURS_JUCEHEADER__ +#endif // JUCE_COLOURS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.cpp b/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.cpp index 9e2806c..5e5db56 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.cpp +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -28,8 +27,8 @@ FillType::FillType() noexcept { } -FillType::FillType (const Colour& colour_) noexcept - : colour (colour_) +FillType::FillType (Colour c) noexcept + : colour (c) { } @@ -102,7 +101,7 @@ bool FillType::operator!= (const FillType& other) const return ! operator== (other); } -void FillType::setColour (const Colour& newColour) noexcept +void FillType::setColour (Colour newColour) noexcept { gradient = nullptr; image = Image::null; diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.h b/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.h index 3e5bef4..19401d6 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.h +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILLTYPE_JUCEHEADER__ -#define __JUCE_FILLTYPE_JUCEHEADER__ - -#include "../colour/juce_ColourGradient.h" -#include "../images/juce_Image.h" +#ifndef JUCE_FILLTYPE_H_INCLUDED +#define JUCE_FILLTYPE_H_INCLUDED //============================================================================== @@ -49,7 +45,7 @@ public: /** Creates a fill type of a solid colour. @see setColour */ - FillType (const Colour& colour) noexcept; + FillType (Colour colour) noexcept; /** Creates a gradient fill type. @see setGradient @@ -63,14 +59,14 @@ public: FillType (const Image& image, const AffineTransform& transform) noexcept; /** Creates a copy of another FillType. */ - FillType (const FillType& other); + FillType (const FillType&); /** Makes a copy of another FillType. */ - FillType& operator= (const FillType& other); + FillType& operator= (const FillType&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - FillType (FillType&& other) noexcept; - FillType& operator= (FillType&& other) noexcept; + FillType (FillType&&) noexcept; + FillType& operator= (FillType&&) noexcept; #endif /** Destructor. */ @@ -88,7 +84,7 @@ public: /** 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) noexcept; + void setColour (Colour newColour) noexcept; /** Turns this object into a gradient fill. */ void setGradient (const ColourGradient& newGradient); @@ -142,12 +138,12 @@ public: AffineTransform transform; //============================================================================== - bool operator== (const FillType& other) const; - bool operator!= (const FillType& other) const; + bool operator== (const FillType&) const; + bool operator!= (const FillType&) const; private: - JUCE_LEAK_DETECTOR (FillType); + JUCE_LEAK_DETECTOR (FillType) }; -#endif // __JUCE_FILLTYPE_JUCEHEADER__ +#endif // JUCE_FILLTYPE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/colour/juce_PixelFormats.h b/JuceLibraryCode/modules/juce_graphics/colour/juce_PixelFormats.h index 6cf54d8..2715cb2 100644 --- a/JuceLibraryCode/modules/juce_graphics/colour/juce_PixelFormats.h +++ b/JuceLibraryCode/modules/juce_graphics/colour/juce_PixelFormats.h @@ -1,47 +1,49 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ -#define __JUCE_PIXELFORMATS_JUCEHEADER__ +#ifndef JUCE_PIXELFORMATS_H_INCLUDED +#define JUCE_PIXELFORMATS_H_INCLUDED //============================================================================== -#ifndef DOXYGEN - #if JUCE_MSVC - #pragma pack (push, 1) - #define PACKED - #elif JUCE_GCC - #define PACKED __attribute__((packed)) - #else - #define PACKED - #endif +#if JUCE_MSVC + #pragma pack (push, 1) #endif class PixelRGB; class PixelAlpha; +inline uint32 maskPixelComponents (uint32 x) noexcept +{ + return (x >> 8) & 0x00ff00ff; +} + +inline uint32 clampPixelComponents (uint32 x) noexcept +{ + return (x | (0x01000100 - maskPixelComponents (x))) & 0x00ff00ff; +} + //============================================================================== /** Represents a 32-bit ARGB pixel with premultiplied alpha, and can perform compositing @@ -60,8 +62,8 @@ public: /** Creates a pixel from a 32-bit argb value. */ - PixelARGB (const uint32 argb_) noexcept - : argb (argb_) + PixelARGB (const uint32 argbValue) noexcept + : argb (argbValue) { } @@ -84,6 +86,19 @@ public: forcedinline uint8 getGreen() const noexcept { return components.g; } forcedinline uint8 getBlue() const noexcept { return components.b; } + #if JUCE_GCC && ! JUCE_CLANG + // NB these are here as a workaround because GCC refuses to bind to packed values. + forcedinline uint8& getAlpha() noexcept { return comps [indexA]; } + forcedinline uint8& getRed() noexcept { return comps [indexR]; } + forcedinline uint8& getGreen() noexcept { return comps [indexG]; } + forcedinline uint8& getBlue() noexcept { return comps [indexB]; } + #else + forcedinline uint8& getAlpha() noexcept { return components.a; } + forcedinline uint8& getRed() noexcept { return components.r; } + forcedinline uint8& getGreen() noexcept { return components.g; } + forcedinline uint8& getBlue() noexcept { return components.b; } + #endif + /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends @@ -92,13 +107,10 @@ public: template forcedinline void blend (const Pixel& src) noexcept { - uint32 sargb = src.getARGB(); - const uint32 alpha = 0x100 - (sargb >> 24); - - sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); - sargb += 0xff00ff00 & (getAG() * alpha); - - argb = sargb; + const uint32 alpha = 0x100 - src.getAlpha(); + uint32 rb = src.getRB() + maskPixelComponents (getRB() * alpha); + uint32 ag = src.getAG() + maskPixelComponents (getAG() * alpha); + argb = clampPixelComponents (rb) + (clampPixelComponents (ag) << 8); } /** Blends another pixel onto this one. @@ -106,7 +118,7 @@ public: This takes into account the opacity of the pixel being overlaid, and blends it accordingly. */ - forcedinline void blend (const PixelRGB& src) noexcept; + forcedinline void blend (const PixelRGB src) noexcept; /** Blends another pixel onto this one, applying an extra multiplier to its opacity. @@ -117,17 +129,14 @@ public: template forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept { - ++extraAlpha; + uint32 ag = maskPixelComponents (extraAlpha * src.getAG()); + const uint32 alpha = 0x100 - (ag >> 16); + ag += maskPixelComponents (getAG() * alpha); - uint32 sargb = ((extraAlpha * src.getAG()) & 0xff00ff00) - | (((extraAlpha * src.getRB()) >> 8) & 0x00ff00ff); + uint32 rb = maskPixelComponents (extraAlpha * src.getRB()) + + maskPixelComponents (getRB() * alpha); - const uint32 alpha = 0x100 - (sargb >> 24); - - sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); - sargb += 0xff00ff00 & (getAG() * alpha); - - argb = sargb; + argb = clampPixelComponents(rb) + (clampPixelComponents (ag) << 8); } /** Blends another pixel with this one, creating a colour that is somewhere @@ -271,20 +280,23 @@ private: struct Components { #if JUCE_BIG_ENDIAN - uint8 a : 8, r : 8, g : 8, b : 8; + uint8 a, r, g, b; #else uint8 b, g, r, a; #endif - } PACKED; + } JUCE_PACKED; union { uint32 argb; Components components; + #if JUCE_GCC + uint8 comps[4]; + #endif }; } #ifndef DOXYGEN - PACKED + JUCE_PACKED #endif ; @@ -326,6 +338,10 @@ public: forcedinline uint8 getGreen() const noexcept { return g; } forcedinline uint8 getBlue() const noexcept { return b; } + forcedinline uint8& getRed() noexcept { return r; } + forcedinline uint8& getGreen() noexcept { return g; } + forcedinline uint8& getBlue() noexcept { return b; } + /** Blends another pixel onto this one. This takes into account the opacity of the pixel being overlaid, and blends @@ -334,18 +350,17 @@ public: template forcedinline void blend (const Pixel& src) noexcept { - uint32 sargb = src.getARGB(); - const uint32 alpha = 0x100 - (sargb >> 24); + const uint32 alpha = 0x100 - src.getAlpha(); - sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); - sargb += 0x0000ff00 & (g * alpha); + uint32 rb = clampPixelComponents (src.getRB() + maskPixelComponents (getRB() * alpha)); + uint32 ag = src.getAG() + (g * alpha >> 8); - r = (uint8) (sargb >> 16); - g = (uint8) (sargb >> 8); - b = (uint8) sargb; + r = (uint8) (rb >> 16); + g = (uint8) clampPixelComponents (ag); + b = (uint8) rb; } - forcedinline void blend (const PixelRGB& src) noexcept + forcedinline void blend (const PixelRGB src) noexcept { set (src); } @@ -358,19 +373,16 @@ public: template forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept { - ++extraAlpha; - const uint32 srb = (extraAlpha * src.getRB()) >> 8; - const uint32 sag = extraAlpha * src.getAG(); - uint32 sargb = (sag & 0xff00ff00) | (srb & 0x00ff00ff); + uint32 ag = maskPixelComponents (extraAlpha * src.getAG()); + const uint32 alpha = 0x100 - (ag >> 16); + ag += g * alpha >> 8; - const uint32 alpha = 0x100 - (sargb >> 24); + uint32 rb = clampPixelComponents (maskPixelComponents (extraAlpha * src.getRB()) + + maskPixelComponents (getRB() * alpha)); - sargb += 0x00ff00ff & ((getRB() * alpha) >> 8); - sargb += 0x0000ff00 & (g * alpha); - - b = (uint8) sargb; - g = (uint8) (sargb >> 8); - r = (uint8) (sargb >> 16); + b = (uint8) rb; + g = (uint8) clampPixelComponents (ag); + r = (uint8) (rb >> 16); } /** Blends another pixel with this one, creating a colour that is somewhere @@ -414,11 +426,11 @@ public: forcedinline void multiplyAlpha (float) noexcept {} /** Sets the pixel's colour from individual components. */ - void setARGB (const uint8, const uint8 r_, const uint8 g_, const uint8 b_) noexcept + void setARGB (const uint8, const uint8 red, const uint8 green, const uint8 blue) noexcept { - r = r_; - g = g_; - b = b_; + r = red; + g = green; + b = blue; } /** Premultiplies the pixel's RGB values by its alpha. */ @@ -450,11 +462,11 @@ private: } #ifndef DOXYGEN - PACKED + JUCE_PACKED #endif ; -forcedinline void PixelARGB::blend (const PixelRGB& src) noexcept +forcedinline void PixelARGB::blend (const PixelRGB src) noexcept { set (src); } @@ -490,6 +502,8 @@ public: forcedinline uint32 getAG() const noexcept { return (((uint32) a) << 16) | a; } forcedinline uint8 getAlpha() const noexcept { return a; } + forcedinline uint8& getAlpha() noexcept { return a; } + forcedinline uint8 getRed() const noexcept { return 0; } forcedinline uint8 getGreen() const noexcept { return 0; } forcedinline uint8 getBlue() const noexcept { return 0; } @@ -576,10 +590,10 @@ public: private: //============================================================================== - uint8 a : 8; + uint8 a; } #ifndef DOXYGEN - PACKED + JUCE_PACKED #endif ; @@ -587,6 +601,4 @@ private: #pragma pack (pop) #endif -#undef PACKED - -#endif // __JUCE_PIXELFORMATS_JUCEHEADER__ +#endif // JUCE_PIXELFORMATS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index 5686e4e..a0dcd80 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,35 +25,35 @@ namespace { template - bool areCoordsSensibleNumbers (Type x, Type y, Type w, Type h) + Rectangle coordsToRectangle (Type x, Type y, Type w, Type h) { + #if JUCE_DEBUG const int maxVal = 0x3fffffff; - return (int) x >= -maxVal && (int) x <= maxVal - && (int) y >= -maxVal && (int) y <= maxVal - && (int) w >= -maxVal && (int) w <= maxVal - && (int) h >= -maxVal && (int) h <= maxVal; + jassert ((int) x >= -maxVal && (int) x <= maxVal + && (int) y >= -maxVal && (int) y <= maxVal + && (int) w >= -maxVal && (int) w <= maxVal + && (int) h >= -maxVal && (int) h <= maxVal); + #endif + + return Rectangle (x, y, w, h); } } //============================================================================== -LowLevelGraphicsContext::LowLevelGraphicsContext() -{ -} - -LowLevelGraphicsContext::~LowLevelGraphicsContext() -{ -} +LowLevelGraphicsContext::LowLevelGraphicsContext() {} +LowLevelGraphicsContext::~LowLevelGraphicsContext() {} //============================================================================== Graphics::Graphics (const Image& imageToDrawOnto) - : context (imageToDrawOnto.createLowLevelContext()), - contextToDelete (context), + : context (*imageToDrawOnto.createLowLevelContext()), + contextToDelete (&context), saveStatePending (false) { + jassert (imageToDrawOnto.isValid()); // Can't draw into a null image! } -Graphics::Graphics (LowLevelGraphicsContext* const internalContext) noexcept +Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept : context (internalContext), saveStatePending (false) { @@ -68,20 +67,20 @@ Graphics::~Graphics() void Graphics::resetToDefaultState() { saveStateIfPending(); - context->setFill (FillType()); - context->setFont (Font()); - context->setInterpolationQuality (Graphics::mediumResamplingQuality); + context.setFill (FillType()); + context.setFont (Font()); + context.setInterpolationQuality (Graphics::mediumResamplingQuality); } bool Graphics::isVectorDevice() const { - return context->isVectorDevice(); + return context.isVectorDevice(); } bool Graphics::reduceClipRegion (const Rectangle& area) { saveStateIfPending(); - return context->clipToRectangle (area); + return context.clipToRectangle (area); } bool Graphics::reduceClipRegion (const int x, const int y, const int w, const int h) @@ -89,40 +88,40 @@ bool Graphics::reduceClipRegion (const int x, const int y, const int w, const in return reduceClipRegion (Rectangle (x, y, w, h)); } -bool Graphics::reduceClipRegion (const RectangleList& clipRegion) +bool Graphics::reduceClipRegion (const RectangleList& clipRegion) { saveStateIfPending(); - return context->clipToRectangleList (clipRegion); + return context.clipToRectangleList (clipRegion); } bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform) { saveStateIfPending(); - context->clipToPath (path, transform); - return ! context->isClipEmpty(); + context.clipToPath (path, transform); + return ! context.isClipEmpty(); } bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform) { saveStateIfPending(); - context->clipToImageAlpha (image, transform); - return ! context->isClipEmpty(); + context.clipToImageAlpha (image, transform); + return ! context.isClipEmpty(); } void Graphics::excludeClipRegion (const Rectangle& rectangleToExclude) { saveStateIfPending(); - context->excludeClipRectangle (rectangleToExclude); + context.excludeClipRectangle (rectangleToExclude); } bool Graphics::isClipEmpty() const { - return context->isClipEmpty(); + return context.isClipEmpty(); } Rectangle Graphics::getClipBounds() const { - return context->getClipBounds(); + return context.getClipBounds(); } void Graphics::saveState() @@ -136,7 +135,7 @@ void Graphics::restoreState() if (saveStatePending) saveStatePending = false; else - context->restoreState(); + context.restoreState(); } void Graphics::saveStateIfPending() @@ -144,49 +143,54 @@ void Graphics::saveStateIfPending() if (saveStatePending) { saveStatePending = false; - context->saveState(); + context.saveState(); } } -void Graphics::setOrigin (const int newOriginX, const int newOriginY) +void Graphics::setOrigin (Point newOrigin) { saveStateIfPending(); - context->setOrigin (newOriginX, newOriginY); + context.setOrigin (newOrigin); +} + +void Graphics::setOrigin (int x, int y) +{ + setOrigin (Point (x, y)); } void Graphics::addTransform (const AffineTransform& transform) { saveStateIfPending(); - context->addTransform (transform); + context.addTransform (transform); } bool Graphics::clipRegionIntersects (const Rectangle& area) const { - return context->clipRegionIntersects (area); + return context.clipRegionIntersects (area); } void Graphics::beginTransparencyLayer (float layerOpacity) { saveStateIfPending(); - context->beginTransparencyLayer (layerOpacity); + context.beginTransparencyLayer (layerOpacity); } void Graphics::endTransparencyLayer() { - context->endTransparencyLayer(); + context.endTransparencyLayer(); } //============================================================================== -void Graphics::setColour (const Colour& newColour) +void Graphics::setColour (Colour newColour) { saveStateIfPending(); - context->setFill (newColour); + context.setFill (newColour); } void Graphics::setOpacity (const float newOpacity) { saveStateIfPending(); - context->setOpacity (newOpacity); + context.setOpacity (newOpacity); } void Graphics::setGradientFill (const ColourGradient& gradient) @@ -197,51 +201,56 @@ void Graphics::setGradientFill (const ColourGradient& gradient) void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity) { saveStateIfPending(); - context->setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY))); - context->setOpacity (opacity); + context.setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY))); + context.setOpacity (opacity); } void Graphics::setFillType (const FillType& newFill) { saveStateIfPending(); - context->setFill (newFill); + context.setFill (newFill); } //============================================================================== void Graphics::setFont (const Font& newFont) { saveStateIfPending(); - context->setFont (newFont); + context.setFont (newFont); } -void Graphics::setFont (const float newFontHeight, const int newFontStyleFlags) +void Graphics::setFont (const float newFontHeight) { - saveStateIfPending(); - Font f (context->getFont()); - f.setSizeAndStyle (newFontHeight, newFontStyleFlags, 1.0f, 0); - context->setFont (f); + setFont (context.getFont().withHeight (newFontHeight)); } Font Graphics::getCurrentFont() const { - return context->getFont(); + return context.getFont(); } //============================================================================== void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY, - const Justification& justification) const + Justification justification) const { - if (text.isNotEmpty() - && startX < context->getClipBounds().getRight()) + if (text.isNotEmpty()) { - GlyphArrangement arr; - arr.addLineOfText (context->getFont(), text, (float) startX, (float) baselineY); - // Don't pass any vertical placement flags to this method - they'll be ignored. jassert (justification.getOnlyVerticalFlags() == 0); const int flags = justification.getOnlyHorizontalFlags(); + if (flags == Justification::right) + { + if (startX < context.getClipBounds().getX()) + return; + } + else if (flags == Justification::left) + if (startX > context.getClipBounds().getRight()) + return; + + GlyphArrangement arr; + arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY); + if (flags != Justification::left) { float w = arr.getBoundingBox (0, -1, true).getWidth(); @@ -258,63 +267,59 @@ void Graphics::drawSingleLineText (const String& text, const int startX, const i } } -void Graphics::drawTextAsPath (const String& text, const AffineTransform& transform) const -{ - if (text.isNotEmpty()) - { - GlyphArrangement arr; - arr.addLineOfText (context->getFont(), text, 0.0f, 0.0f); - arr.draw (*this, transform); - } -} - -void Graphics::drawMultiLineText (const String& text, const int startX, const int baselineY, const int maximumLineWidth) const +void Graphics::drawMultiLineText (const String& text, const int startX, + const int baselineY, const int maximumLineWidth) const { if (text.isNotEmpty() - && startX < context->getClipBounds().getRight()) + && startX < context.getClipBounds().getRight()) { GlyphArrangement arr; - arr.addJustifiedText (context->getFont(), text, + arr.addJustifiedText (context.getFont(), text, (float) startX, (float) baselineY, (float) maximumLineWidth, Justification::left); arr.draw (*this); } } -void Graphics::drawText (const String& text, - const int x, const int y, const int width, const int height, - const Justification& justificationType, - const bool useEllipsesIfTooBig) const +void Graphics::drawText (const String& text, const Rectangle& area, + Justification justificationType, bool useEllipsesIfTooBig) const { - if (text.isNotEmpty() && context->clipRegionIntersects (Rectangle (x, y, width, height))) + if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer())) { GlyphArrangement arr; - - arr.addCurtailedLineOfText (context->getFont(), text, - 0.0f, 0.0f, (float) width, - useEllipsesIfTooBig); + arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f, + area.getWidth(), useEllipsesIfTooBig); arr.justifyGlyphs (0, arr.getNumGlyphs(), - (float) x, (float) y, (float) width, (float) height, + area.getX(), area.getY(), area.getWidth(), area.getHeight(), justificationType); arr.draw (*this); } } -void Graphics::drawFittedText (const String& text, - const int x, const int y, const int width, const int height, - const Justification& justification, +void Graphics::drawText (const String& text, const Rectangle& area, + Justification justificationType, bool useEllipsesIfTooBig) const +{ + drawText (text, area.toFloat(), justificationType, useEllipsesIfTooBig); +} + +void Graphics::drawText (const String& text, const int x, const int y, const int width, const int height, + Justification justificationType, const bool useEllipsesIfTooBig) const +{ + drawText (text, Rectangle (x, y, width, height), justificationType, useEllipsesIfTooBig); +} + +void Graphics::drawFittedText (const String& text, const Rectangle& area, + Justification justification, const int maximumNumberOfLines, const float minimumHorizontalScale) const { - if (text.isNotEmpty() - && width > 0 && height > 0 - && context->clipRegionIntersects (Rectangle (x, y, width, height))) + if (text.isNotEmpty() && (! area.isEmpty()) && context.clipRegionIntersects (area)) { GlyphArrangement arr; - - arr.addFittedText (context->getFont(), text, - (float) x, (float) y, (float) width, (float) height, + arr.addFittedText (context.getFont(), text, + (float) area.getX(), (float) area.getY(), + (float) area.getWidth(), (float) area.getHeight(), justification, maximumNumberOfLines, minimumHorizontalScale); @@ -323,50 +328,67 @@ void Graphics::drawFittedText (const String& text, } } -//============================================================================== -void Graphics::fillRect (int x, int y, int width, int height) const +void Graphics::drawFittedText (const String& text, const int x, const int y, const int width, const int height, + Justification justification, + const int maximumNumberOfLines, + const float minimumHorizontalScale) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); - - context->fillRect (Rectangle (x, y, width, height), false); + drawFittedText (text, coordsToRectangle (x, y, width, height), + justification, maximumNumberOfLines, minimumHorizontalScale); } +//============================================================================== void Graphics::fillRect (const Rectangle& r) const { - context->fillRect (r, false); + context.fillRect (r, false); } -void Graphics::fillRect (const float x, const float y, const float width, const float height) const +void Graphics::fillRect (const Rectangle& r) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); + context.fillRect (r); +} - Path p; - p.addRectangle (x, y, width, height); - fillPath (p); +void Graphics::fillRect (int x, int y, int width, int height) const +{ + context.fillRect (coordsToRectangle (x, y, width, height), false); +} + +void Graphics::fillRect (float x, float y, float width, float height) const +{ + fillRect (coordsToRectangle (x, y, width, height)); +} + +void Graphics::fillRectList (const RectangleList& rectangles) const +{ + context.fillRectList (rectangles); +} + +void Graphics::fillRectList (const RectangleList& rects) const +{ + for (const Rectangle* r = rects.begin(), * const e = rects.end(); r != e; ++r) + context.fillRect (*r, false); } void Graphics::setPixel (int x, int y) const { - context->fillRect (Rectangle (x, y, 1, 1), false); + context.fillRect (Rectangle (x, y, 1, 1), false); } void Graphics::fillAll() const { - fillRect (context->getClipBounds()); + fillRect (context.getClipBounds()); } -void Graphics::fillAll (const Colour& colourToUse) const +void Graphics::fillAll (Colour colourToUse) const { if (! colourToUse.isTransparent()) { - const Rectangle clip (context->getClipBounds()); + const Rectangle clip (context.getClipBounds()); - context->saveState(); - context->setFill (colourToUse); - context->fillRect (clip, false); - context->restoreState(); + context.saveState(); + context.setFill (colourToUse); + context.fillRect (clip, false); + context.restoreState(); } } @@ -374,8 +396,8 @@ void Graphics::fillAll (const Colour& colourToUse) const //============================================================================== void Graphics::fillPath (const Path& path, const AffineTransform& transform) const { - if ((! context->isClipEmpty()) && ! path.isEmpty()) - context->fillPath (path, transform); + if ((! context.isClipEmpty()) && ! path.isEmpty()) + context.fillPath (path, transform); } void Graphics::strokePath (const Path& path, @@ -383,125 +405,87 @@ void Graphics::strokePath (const Path& path, const AffineTransform& transform) const { Path stroke; - strokeType.createStrokedPath (stroke, path, transform, context->getScaleFactor()); + strokeType.createStrokedPath (stroke, path, transform, context.getPhysicalPixelScaleFactor()); fillPath (stroke); } //============================================================================== -void Graphics::drawRect (const int x, const int y, const int width, const int height, - const int lineThickness) const +void Graphics::drawRect (float x, float y, float width, float height, float lineThickness) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); - - context->fillRect (Rectangle (x, y, width, lineThickness), false); - context->fillRect (Rectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2), false); - context->fillRect (Rectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2), false); - context->fillRect (Rectangle (x, y + height - lineThickness, width, lineThickness), false); + drawRect (coordsToRectangle (x, y, width, height), lineThickness); } -void Graphics::drawRect (const float x, const float y, const float width, const float height, const float lineThickness) const +void Graphics::drawRect (int x, int y, int width, int height, int lineThickness) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); - - Path p; - p.addRectangle (x, y, width, lineThickness); - p.addRectangle (x, y + lineThickness, lineThickness, height - lineThickness * 2.0f); - p.addRectangle (x + width - lineThickness, y + lineThickness, lineThickness, height - lineThickness * 2.0f); - p.addRectangle (x, y + height - lineThickness, width, lineThickness); - fillPath (p); + drawRect (coordsToRectangle (x, y, width, height), lineThickness); } -void Graphics::drawRect (const Rectangle& r, const int lineThickness) const +void Graphics::drawRect (const Rectangle& r, int lineThickness) const { - drawRect (r.getX(), r.getY(), r.getWidth(), r.getHeight(), lineThickness); + drawRect (r.toFloat(), (float) lineThickness); } -void Graphics::drawBevel (const int x, const int y, const int width, const int height, - const int bevelThickness, const Colour& topLeftColour, const Colour& bottomRightColour, - const bool useGradient, const bool sharpEdgeOnOutside) const +void Graphics::drawRect (Rectangle r, const float lineThickness) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); - - if (clipRegionIntersects (Rectangle (x, y, width, height))) - { - context->saveState(); - - for (int i = bevelThickness; --i >= 0;) - { - const float op = useGradient ? (sharpEdgeOnOutside ? bevelThickness - i : i) / (float) bevelThickness - : 1.0f; - - context->setFill (topLeftColour.withMultipliedAlpha (op)); - context->fillRect (Rectangle (x + i, y + i, width - i * 2, 1), false); - context->setFill (topLeftColour.withMultipliedAlpha (op * 0.75f)); - context->fillRect (Rectangle (x + i, y + i + 1, 1, height - i * 2 - 2), false); - context->setFill (bottomRightColour.withMultipliedAlpha (op)); - context->fillRect (Rectangle (x + i, y + height - i - 1, width - i * 2, 1), false); - context->setFill (bottomRightColour.withMultipliedAlpha (op * 0.75f)); - context->fillRect (Rectangle (x + width - i - 1, y + i + 1, 1, height - i * 2 - 2), false); - } - - context->restoreState(); - } + RectangleList rects; + rects.addWithoutMerging (r.removeFromTop (lineThickness)); + rects.addWithoutMerging (r.removeFromBottom (lineThickness)); + rects.addWithoutMerging (r.removeFromLeft (lineThickness)); + rects.addWithoutMerging (r.removeFromRight (lineThickness)); + context.fillRectList (rects); } //============================================================================== -void Graphics::fillEllipse (const float x, const float y, const float width, const float height) const +void Graphics::fillEllipse (const Rectangle& area) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); - Path p; - p.addEllipse (x, y, width, height); + p.addEllipse (area); fillPath (p); } -void Graphics::drawEllipse (const float x, const float y, const float width, const float height, - const float lineThickness) const +void Graphics::fillEllipse (float x, float y, float w, float h) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); + fillEllipse (Rectangle (x, y, w, h)); +} +void Graphics::drawEllipse (float x, float y, float width, float height, float lineThickness) const +{ Path p; p.addEllipse (x, y, width, height); strokePath (p, PathStrokeType (lineThickness)); } -void Graphics::fillRoundedRectangle (const float x, const float y, const float width, const float height, const float cornerSize) const +void Graphics::drawEllipse (const Rectangle& area, float lineThickness) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); + drawEllipse (area.getX(), area.getY(), area.getWidth(), area.getHeight(), lineThickness); +} - Path p; - p.addRoundedRectangle (x, y, width, height, cornerSize); - fillPath (p); +void Graphics::fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const +{ + fillRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize); } void Graphics::fillRoundedRectangle (const Rectangle& r, const float cornerSize) const { - fillRoundedRectangle (r.getX(), r.getY(), r.getWidth(), r.getHeight(), cornerSize); + Path p; + p.addRoundedRectangle (r, cornerSize); + fillPath (p); } -void Graphics::drawRoundedRectangle (const float x, const float y, const float width, const float height, - const float cornerSize, const float lineThickness) const +void Graphics::drawRoundedRectangle (float x, float y, float width, float height, + float cornerSize, float lineThickness) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (x, y, width, height)); + drawRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize, lineThickness); +} +void Graphics::drawRoundedRectangle (const Rectangle& r, float cornerSize, float lineThickness) const +{ Path p; - p.addRoundedRectangle (x, y, width, height, cornerSize); + p.addRoundedRectangle (r, cornerSize); strokePath (p, PathStrokeType (lineThickness)); } -void Graphics::drawRoundedRectangle (const Rectangle& r, const float cornerSize, const float lineThickness) const -{ - drawRoundedRectangle (r.getX(), r.getY(), r.getWidth(), r.getHeight(), cornerSize, lineThickness); -} - -void Graphics::drawArrow (const Line& line, const float lineThickness, const float arrowheadWidth, const float arrowheadLength) const +void Graphics::drawArrow (const Line& line, float lineThickness, float arrowheadWidth, float arrowheadLength) const { Path p; p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength); @@ -510,26 +494,26 @@ void Graphics::drawArrow (const Line& line, const float lineThickness, co void Graphics::fillCheckerBoard (const Rectangle& area, const int checkWidth, const int checkHeight, - const Colour& colour1, const Colour& colour2) const + Colour colour1, Colour colour2) const { jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less! if (checkWidth > 0 && checkHeight > 0) { - context->saveState(); + context.saveState(); if (colour1 == colour2) { - context->setFill (colour1); - context->fillRect (area, false); + context.setFill (colour1); + context.fillRect (area, false); } else { - const Rectangle clipped (context->getClipBounds().getIntersection (area)); + const Rectangle clipped (context.getClipBounds().getIntersection (area)); if (! clipped.isEmpty()) { - context->clipToRectangle (clipped); + context.clipToRectangle (clipped); const int checkNumX = (clipped.getX() - area.getX()) / checkWidth; const int checkNumY = (clipped.getY() - area.getY()) / checkHeight; @@ -540,42 +524,44 @@ void Graphics::fillCheckerBoard (const Rectangle& area, for (int i = 0; i < 2; ++i) { - context->setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2); + context.setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2); int cy = i; for (int y = startY; y < bottom; y += checkHeight) for (int x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2) - context->fillRect (Rectangle (x, y, checkWidth, checkHeight), false); + context.fillRect (Rectangle (x, y, checkWidth, checkHeight), false); } } } - context->restoreState(); + context.restoreState(); } } //============================================================================== void Graphics::drawVerticalLine (const int x, float top, float bottom) const { - context->drawVerticalLine (x, top, bottom); + if (top < bottom) + context.fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); } void Graphics::drawHorizontalLine (const int y, float left, float right) const { - context->drawHorizontalLine (y, left, right); -} - -void Graphics::drawLine (const float x1, const float y1, const float x2, const float y2) const -{ - context->drawLine (Line (x1, y1, x2, y2)); + if (left < right) + context.fillRect (Rectangle (left, (float) y, right - left, 1.0f)); } void Graphics::drawLine (const Line& line) const { - context->drawLine (line); + context.drawLine (line); } -void Graphics::drawLine (const float x1, const float y1, const float x2, const float y2, const float lineThickness) const +void Graphics::drawLine (float x1, float y1, float x2, float y2) const +{ + context.drawLine (Line (x1, y1, x2, y2)); +} + +void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThickness) const { drawLine (Line (x1, y1, x2, y2), lineThickness); } @@ -615,7 +601,7 @@ void Graphics::drawDashedLine (const Line& line, const float* const dashL if (lineThickness != 1.0f) drawLine (segment, lineThickness); else - context->drawLine (segment); + context.drawLine (segment); } } } @@ -625,56 +611,27 @@ void Graphics::drawDashedLine (const Line& line, const float* const dashL void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality) { saveStateIfPending(); - context->setInterpolationQuality (newQuality); + context.setInterpolationQuality (newQuality); } //============================================================================== -void Graphics::drawImageAt (const Image& imageToDraw, - const int topLeftX, const int topLeftY, - const bool fillAlphaChannelWithCurrentBrush) const +void Graphics::drawImageAt (const Image& imageToDraw, int x, int y, bool fillAlphaChannel) const { - const int imageW = imageToDraw.getWidth(); - const int imageH = imageToDraw.getHeight(); - - drawImage (imageToDraw, - topLeftX, topLeftY, imageW, imageH, - 0, 0, imageW, imageH, - fillAlphaChannelWithCurrentBrush); + drawImageTransformed (imageToDraw, + AffineTransform::translation ((float) x, (float) y), + fillAlphaChannel); } void Graphics::drawImageWithin (const Image& imageToDraw, - const int destX, const int destY, - const int destW, const int destH, - const RectanglePlacement& placementWithinTarget, + int dx, int dy, int dw, int dh, + RectanglePlacement placementWithinTarget, const bool fillAlphaChannelWithCurrentBrush) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (destX, destY, destW, destH)); - if (imageToDraw.isValid()) - { - const int imageW = imageToDraw.getWidth(); - const int imageH = imageToDraw.getHeight(); - - if (imageW > 0 && imageH > 0) - { - double newX = 0.0, newY = 0.0; - double newW = imageW; - double newH = imageH; - - placementWithinTarget.applyTo (newX, newY, newW, newH, - destX, destY, destW, destH); - - if (newW > 0 && newH > 0) - { - drawImage (imageToDraw, - roundToInt (newX), roundToInt (newY), - roundToInt (newW), roundToInt (newH), - 0, 0, imageW, imageH, - fillAlphaChannelWithCurrentBrush); - } - } - } + drawImageTransformed (imageToDraw, + placementWithinTarget.getTransformToFit (imageToDraw.getBounds().toFloat(), + coordsToRectangle (dx, dy, dw, dh).toFloat()), + fillAlphaChannelWithCurrentBrush); } void Graphics::drawImage (const Image& imageToDraw, @@ -682,42 +639,35 @@ void Graphics::drawImage (const Image& imageToDraw, int sx, int sy, int sw, int sh, const bool fillAlphaChannelWithCurrentBrush) const { - // passing in a silly number can cause maths problems in rendering! - jassert (areCoordsSensibleNumbers (dx, dy, dw, dh)); - jassert (areCoordsSensibleNumbers (sx, sy, sw, sh)); - - if (imageToDraw.isValid() && context->clipRegionIntersects (Rectangle (dx, dy, dw, dh))) - { - drawImageTransformed (imageToDraw.getClippedImage (Rectangle (sx, sy, sw, sh)), + if (imageToDraw.isValid() && context.clipRegionIntersects (coordsToRectangle (dx, dy, dw, dh))) + drawImageTransformed (imageToDraw.getClippedImage (coordsToRectangle (sx, sy, sw, sh)), AffineTransform::scale (dw / (float) sw, dh / (float) sh) .translated ((float) dx, (float) dy), fillAlphaChannelWithCurrentBrush); - } } void Graphics::drawImageTransformed (const Image& imageToDraw, const AffineTransform& transform, const bool fillAlphaChannelWithCurrentBrush) const { - if (imageToDraw.isValid() && ! context->isClipEmpty()) + if (imageToDraw.isValid() && ! context.isClipEmpty()) { if (fillAlphaChannelWithCurrentBrush) { - context->saveState(); - context->clipToImageAlpha (imageToDraw, transform); + context.saveState(); + context.clipToImageAlpha (imageToDraw, transform); fillAll(); - context->restoreState(); + context.restoreState(); } else { - context->drawImage (imageToDraw, transform); + context.drawImage (imageToDraw, transform); } } } //============================================================================== -Graphics::ScopedSaveState::ScopedSaveState (Graphics& g) - : context (g) +Graphics::ScopedSaveState::ScopedSaveState (Graphics& g) : context (g) { context.saveState(); } diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.h b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.h index b75db81..d59bcd2 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.h +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.h @@ -1,42 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_GRAPHICSCONTEXT_JUCEHEADER__ -#define __JUCE_GRAPHICSCONTEXT_JUCEHEADER__ - -#include "../fonts/juce_Font.h" -#include "../geometry/juce_Rectangle.h" -#include "../geometry/juce_PathStrokeType.h" -#include "../geometry/juce_Line.h" -#include "../colour/juce_Colours.h" -#include "../colour/juce_ColourGradient.h" -#include "../placement/juce_RectanglePlacement.h" -class LowLevelGraphicsContext; -class Image; -class FillType; -class RectangleList; +#ifndef JUCE_GRAPHICSCONTEXT_H_INCLUDED +#define JUCE_GRAPHICSCONTEXT_H_INCLUDED //============================================================================== @@ -81,7 +68,7 @@ public: @see setOpacity */ - void setColour (const Colour& newColour); + void setColour (Colour newColour); /** Changes the opacity to use with the current colour. @@ -117,18 +104,16 @@ public: 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 + @see drawSingleLineText, drawMultiLineText, drawText, drawFittedText */ void setFont (const Font& newFont); - /** Changes the size and style of the currently-selected font. - + /** Changes the size 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. - + different size. The typeface won't be changed. @see Font */ - void setFont (float newFontHeight, int fontStyleFlags = Font::plain); + void setFont (float newFontHeight); /** Returns the currently selected font. */ Font getCurrentFont() const; @@ -147,7 +132,7 @@ public: */ void drawSingleLineText (const String& text, int startX, int baselineY, - const Justification& justification = Justification::left) const; + Justification justification = Justification::left) const; /** Draws text across multiple lines. @@ -161,17 +146,6 @@ public: 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 @@ -183,7 +157,35 @@ public: */ void drawText (const String& text, int x, int y, int width, int height, - const Justification& justificationType, + Justification justificationType, + bool useEllipsesIfTooBig) 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, + const Rectangle& area, + Justification justificationType, + bool useEllipsesIfTooBig) 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, + const Rectangle& area, + Justification justificationType, bool useEllipsesIfTooBig) const; /** Tries to draw a text string inside a given space. @@ -207,14 +209,39 @@ public: */ void drawFittedText (const String& text, int x, int y, int width, int height, - const Justification& justificationFlags, + Justification justificationFlags, + int maximumNumberOfLines, + float minimumHorizontalScale = 0.7f) 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, + const Rectangle& area, + 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 + (See also the fillAll (Colour) method which is a quick way of filling it with a given colour). */ void fillAll() const; @@ -224,152 +251,143 @@ public: This leaves the context's current colour and brush unchanged, it just uses the specified colour temporarily. */ - void fillAll (const Colour& colourToUse) const; + void fillAll (Colour colourToUse) const; //============================================================================== /** Fills a rectangle with the current colour or brush. + @see drawRect, fillRoundedRectangle + */ + void fillRect (const Rectangle& rectangle) const; + /** Fills a rectangle with the current colour or brush. + @see drawRect, fillRoundedRectangle + */ + void fillRect (const Rectangle& rectangle) 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. + @see drawRect, fillRoundedRectangle */ void fillRect (float x, float y, float width, float height) const; - /** Uses the current colour or brush to fill a rectangle with rounded corners. + /** Fills a set of rectangles using the current colour or brush. + If you have a lot of rectangles to draw, it may be more efficient + to create a RectangleList and use this method than to call fillRect() + multiple times. + */ + void fillRectList (const RectangleList& rectangles) const; + /** Fills a set of rectangles using the current colour or brush. + If you have a lot of rectangles to draw, it may be more efficient + to create a RectangleList and use this method than to call fillRect() + multiple times. + */ + void fillRectList (const RectangleList& rectangles) 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. - */ + /** Fills a rectangle with a checkerboard pattern, alternating between two colours. */ void fillCheckerBoard (const Rectangle& area, 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. + Colour colour1, Colour colour2) const; + /** Draws 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. + void drawRect (int x, int y, int width, int height, int lineThickness = 1) const; + /** Draws 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. + void drawRect (float x, float y, float width, float height, float lineThickness = 1.0f) const; + /** Draws 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; + void drawRect (const Rectangle& rectangle, int lineThickness = 1) const; + + /** Draws 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 (Rectangle rectangle, float lineThickness = 1.0f) 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, - const Colour& bottomRightColour = Colours::black, - bool useGradient = true, - bool sharpEdgeOnOutside = true) const; - - /** Draws a pixel using the current colour or brush. + /** Fills a 1x1 pixel using the current colour or brush. + Note that because the context may be transformed, this is effectively the same as + calling fillRect (x, y, 1, 1), and the actual result may involve multiple pixels. */ 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. + /** Fills an ellipse with the current colour or brush. + The ellipse is drawn to fit inside the given rectangle. + @see drawEllipse, Path::addEllipse + */ + void fillEllipse (const Rectangle& area) 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 an elliptical stroke using the current colour or brush. + @see fillEllipse, Path::addEllipse + */ + void drawEllipse (const Rectangle& area, 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; + 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; @@ -409,13 +427,11 @@ public: void drawHorizontalLine (int y, float left, float right) const; //============================================================================== - /** Fills a path using the currently selected colour or brush. - */ + /** 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. - */ + /** 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; @@ -446,9 +462,7 @@ public: }; /** 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); @@ -456,7 +470,7 @@ public: /** 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 + given coordinates, 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. @@ -541,13 +555,12 @@ public: */ void drawImageWithin (const Image& imageToDraw, int destX, int destY, int destWidth, int destHeight, - const RectanglePlacement& placementWithinTarget, + RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush = false) const; //============================================================================== /** Returns the position of the bounding box for the current clipping region. - @see getClipRegion, clipRegionIntersects */ Rectangle getClipBounds() const; @@ -579,7 +592,7 @@ public: @returns true if the resulting clipping region is non-zero in size @see setOrigin, clipRegionIntersects */ - bool reduceClipRegion (const RectangleList& clipRegion); + bool reduceClipRegion (const RectangleList& clipRegion); /** Intersects the current clipping region with a path. @@ -626,13 +639,12 @@ public: class ScopedSaveState { public: - ScopedSaveState (Graphics& g); + ScopedSaveState (Graphics&); ~ScopedSaveState(); private: Graphics& context; - - JUCE_DECLARE_NON_COPYABLE (ScopedSaveState); + JUCE_DECLARE_NON_COPYABLE (ScopedSaveState) }; //============================================================================== @@ -653,6 +665,18 @@ public: */ void endTransparencyLayer(); + /** 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 with (100, 100), then the position that was previously + referred to as (100, 100) will subsequently be considered to be (0, 0). + + @see reduceClipRegion, addTransform + */ + void setOrigin (Point newOrigin); + /** Moves the position of the context's origin. This changes the position that the context considers to be (0, 0) to @@ -682,25 +706,25 @@ public: bool isVectorDevice() const; //============================================================================== - /** 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. + /** Create a graphics that draws with a given low-level renderer. + This method is intended for use only by people who know what they're doing. + Note that the LowLevelGraphicsContext will NOT be deleted by this object. */ - Graphics (LowLevelGraphicsContext* internalContext) noexcept; + Graphics (LowLevelGraphicsContext&) noexcept; /** @internal */ - LowLevelGraphicsContext* getInternalContext() const noexcept { return context; } + LowLevelGraphicsContext& getInternalContext() const noexcept { return context; } private: //============================================================================== - LowLevelGraphicsContext* const context; - ScopedPointer contextToDelete; + LowLevelGraphicsContext& context; + ScopedPointer contextToDelete; bool saveStatePending; void saveStateIfPending(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Graphics); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Graphics) }; -#endif // __JUCE_GRAPHICSCONTEXT_JUCEHEADER__ +#endif // JUCE_GRAPHICSCONTEXT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h index 3aace56..8a0176f 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h @@ -1,37 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ -#define __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ +#ifndef JUCE_LOWLEVELGRAPHICSCONTEXT_H_INCLUDED +#define JUCE_LOWLEVELGRAPHICSCONTEXT_H_INCLUDED -#include "../images/juce_Image.h" -#include "../geometry/juce_Path.h" -#include "../geometry/juce_RectangleList.h" -#include "../colour/juce_ColourGradient.h" -#include "../colour/juce_FillType.h" -class AttributedString; //============================================================================== /** @@ -62,20 +55,20 @@ public: //============================================================================== /** Moves the origin to a new position. - The co-ords are relative to the current origin, and indicate the new position + The coordinates are relative to the current origin, and indicate the new position of (0, 0). */ - virtual void setOrigin (int x, int y) = 0; - virtual void addTransform (const AffineTransform& transform) = 0; - virtual float getScaleFactor() = 0; + virtual void setOrigin (Point) = 0; + virtual void addTransform (const AffineTransform&) = 0; + virtual float getPhysicalPixelScaleFactor() = 0; - virtual bool clipToRectangle (const Rectangle& r) = 0; - virtual bool clipToRectangleList (const RectangleList& clipRegion) = 0; - virtual void excludeClipRectangle (const Rectangle& r) = 0; - virtual void clipToPath (const Path& path, const AffineTransform& transform) = 0; - virtual void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) = 0; + virtual bool clipToRectangle (const Rectangle&) = 0; + virtual bool clipToRectangleList (const RectangleList&) = 0; + virtual void excludeClipRectangle (const Rectangle&) = 0; + virtual void clipToPath (const Path&, const AffineTransform&) = 0; + virtual void clipToImageAlpha (const Image&, const AffineTransform&) = 0; - virtual bool clipRegionIntersects (const Rectangle& r) = 0; + virtual bool clipRegionIntersects (const Rectangle&) = 0; virtual Rectangle getClipBounds() const = 0; virtual bool isClipEmpty() const = 0; @@ -86,25 +79,23 @@ public: virtual void endTransparencyLayer() = 0; //============================================================================== - virtual void setFill (const FillType& fillType) = 0; - virtual void setOpacity (float newOpacity) = 0; - virtual void setInterpolationQuality (Graphics::ResamplingQuality quality) = 0; + virtual void setFill (const FillType&) = 0; + virtual void setOpacity (float) = 0; + virtual void setInterpolationQuality (Graphics::ResamplingQuality) = 0; //============================================================================== - virtual void fillRect (const Rectangle& r, bool replaceExistingContents) = 0; - virtual void fillPath (const Path& path, const AffineTransform& transform) = 0; + virtual void fillRect (const Rectangle&, bool replaceExistingContents) = 0; + virtual void fillRect (const Rectangle&) = 0; + virtual void fillRectList (const RectangleList&) = 0; + virtual void fillPath (const Path&, const AffineTransform&) = 0; + virtual void drawImage (const Image&, const AffineTransform&) = 0; + virtual void drawLine (const Line&) = 0; - virtual void drawImage (const Image& sourceImage, const AffineTransform& transform) = 0; - - virtual void drawLine (const Line & line) = 0; - virtual void drawVerticalLine (int x, float top, float bottom) = 0; - virtual void drawHorizontalLine (int y, float left, float right) = 0; - - virtual void setFont (const Font& newFont) = 0; + virtual void setFont (const Font&) = 0; virtual const Font& getFont() = 0; - virtual void drawGlyph (int glyphNumber, const AffineTransform& transform) = 0; + virtual void drawGlyph (int glyphNumber, const AffineTransform&) = 0; virtual bool drawTextLayout (const AttributedString&, const Rectangle&) { return false; } }; -#endif // __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ +#endif // JUCE_LOWLEVELGRAPHICSCONTEXT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp index faec1c8..be4329c 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -90,12 +89,12 @@ bool LowLevelGraphicsPostScriptRenderer::isVectorDevice() const return true; } -void LowLevelGraphicsPostScriptRenderer::setOrigin (int x, int y) +void LowLevelGraphicsPostScriptRenderer::setOrigin (Point o) { - if (x != 0 || y != 0) + if (! o.isOrigin()) { - stateStack.getLast()->xOffset += x; - stateStack.getLast()->yOffset += y; + stateStack.getLast()->xOffset += o.x; + stateStack.getLast()->yOffset += o.y; needToClip = true; } } @@ -106,11 +105,7 @@ void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /* jassertfalse; } -float LowLevelGraphicsPostScriptRenderer::getScaleFactor() -{ - jassertfalse; //xxx - return 1.0f; -} +float LowLevelGraphicsPostScriptRenderer::getPhysicalPixelScaleFactor() { return 1.0f; } bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle& r) { @@ -118,7 +113,7 @@ bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle& return stateStack.getLast()->clip.clipTo (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset)); } -bool LowLevelGraphicsPostScriptRenderer::clipToRectangleList (const RectangleList& clipRegion) +bool LowLevelGraphicsPostScriptRenderer::clipToRectangleList (const RectangleList& clipRegion) { needToClip = true; return stateStack.getLast()->clip.clipTo (clipRegion); @@ -205,7 +200,7 @@ void LowLevelGraphicsPostScriptRenderer::writeClip() int itemsOnLine = 0; - for (RectangleList::Iterator i (stateStack.getLast()->clip); i.next();) + for (const Rectangle* i = stateStack.getLast()->clip.begin(), * const e = stateStack.getLast()->clip.end(); i != e; ++i) { if (++itemsOnLine == 6) { @@ -213,17 +208,15 @@ void LowLevelGraphicsPostScriptRenderer::writeClip() out << '\n'; } - const Rectangle& r = *i.getRectangle(); - - out << r.getX() << ' ' << -r.getY() << ' ' - << r.getWidth() << ' ' << -r.getHeight() << " pr "; + out << i->getX() << ' ' << -i->getY() << ' ' + << i->getWidth() << ' ' << -i->getHeight() << " pr "; } out << "endclip\n"; } } -void LowLevelGraphicsPostScriptRenderer::writeColour (const Colour& colour) +void LowLevelGraphicsPostScriptRenderer::writeColour (Colour colour) { Colour c (Colours::white.overlaidWith (colour)); @@ -342,13 +335,19 @@ void LowLevelGraphicsPostScriptRenderer::setInterpolationQuality (Graphics::Resa //============================================================================== void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle& r, const bool /*replaceExistingContents*/) +{ + fillRect (r.toFloat()); +} + +void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle& r) { if (stateStack.getLast()->fillType.isColour()) { writeClip(); writeColour (stateStack.getLast()->fillType.colour); - Rectangle r2 (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset)); + Rectangle r2 (r.translated ((float) stateStack.getLast()->xOffset, + (float) stateStack.getLast()->yOffset)); out << r2.getX() << ' ' << -r2.getBottom() << ' ' << r2.getWidth() << ' ' << r2.getHeight() << " rectfill\n"; } @@ -358,7 +357,11 @@ void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle& r, cons p.addRectangle (r); fillPath (p, AffineTransform::identity); } +} +void LowLevelGraphicsPostScriptRenderer::fillRectList (const RectangleList& list) +{ + fillPath (list.toPath(), AffineTransform::identity); } //============================================================================== @@ -473,13 +476,13 @@ void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, co writeTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset) .scaled (1.0f, -1.0f)); - RectangleList imageClip; + RectangleList imageClip; sourceImage.createSolidAreaMask (imageClip, 0.5f); out << "newpath "; int itemsOnLine = 0; - for (RectangleList::Iterator i (imageClip); i.next();) + for (const Rectangle* i = imageClip.begin(), * const e = imageClip.end(); i != e; ++i) { if (++itemsOnLine == 6) { @@ -487,9 +490,7 @@ void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, co itemsOnLine = 0; } - const Rectangle& r = *i.getRectangle(); - - out << r.getX() << ' ' << r.getY() << ' ' << r.getWidth() << ' ' << r.getHeight() << " pr "; + out << i->getX() << ' ' << i->getY() << ' ' << i->getWidth() << ' ' << i->getHeight() << " pr "; } out << " clip newpath\n"; @@ -512,16 +513,6 @@ void LowLevelGraphicsPostScriptRenderer::drawLine (const Line & line) fillPath (p, AffineTransform::identity); } -void LowLevelGraphicsPostScriptRenderer::drawVerticalLine (const int x, float top, float bottom) -{ - drawLine (Line ((float) x, top, (float) x, bottom)); -} - -void LowLevelGraphicsPostScriptRenderer::drawHorizontalLine (const int y, float left, float right) -{ - drawLine (Line (left, (float) y, right, (float) y)); -} - //============================================================================== void LowLevelGraphicsPostScriptRenderer::setFont (const Font& newFont) { diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h index 0f01938..2b893a3 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ -#define __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ - -#include "juce_LowLevelGraphicsContext.h" +#ifndef JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_H_INCLUDED +#define JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_H_INCLUDED //============================================================================== @@ -47,47 +44,44 @@ public: ~LowLevelGraphicsPostScriptRenderer(); //============================================================================== - bool isVectorDevice() const; - void setOrigin (int x, int y); - void addTransform (const AffineTransform& transform); - float getScaleFactor(); + bool isVectorDevice() const override; + void setOrigin (Point) override; + void addTransform (const AffineTransform&) override; + float getPhysicalPixelScaleFactor() override; - bool clipToRectangle (const Rectangle& r); - bool clipToRectangleList (const RectangleList& clipRegion); - void excludeClipRectangle (const Rectangle& r); - void clipToPath (const Path& path, const AffineTransform& transform); - void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform); + bool clipToRectangle (const Rectangle&) override; + bool clipToRectangleList (const RectangleList&) override; + void excludeClipRectangle (const Rectangle&) override; + void clipToPath (const Path&, const AffineTransform&) override; + void clipToImageAlpha (const Image&, const AffineTransform&) override; - void saveState(); - void restoreState(); + void saveState() override; + void restoreState() override; - void beginTransparencyLayer (float opacity); - void endTransparencyLayer(); + void beginTransparencyLayer (float) override; + void endTransparencyLayer() override; - bool clipRegionIntersects (const Rectangle& r); - Rectangle getClipBounds() const; - bool isClipEmpty() const; + bool clipRegionIntersects (const Rectangle&) override; + Rectangle getClipBounds() const override; + bool isClipEmpty() const override; //============================================================================== - void setFill (const FillType& fillType); - void setOpacity (float opacity); - void setInterpolationQuality (Graphics::ResamplingQuality quality); + void setFill (const FillType&) override; + void setOpacity (float) override; + void setInterpolationQuality (Graphics::ResamplingQuality) override; //============================================================================== - void fillRect (const Rectangle& r, bool replaceExistingContents); - void fillPath (const Path& path, const AffineTransform& transform); - - void drawImage (const Image& sourceImage, const AffineTransform& transform); - - void drawLine (const Line & line); - - void drawVerticalLine (int x, float top, float bottom); - void drawHorizontalLine (int x, float top, float bottom); + void fillRect (const Rectangle&, bool replaceExistingContents) override; + void fillRect (const Rectangle&) override; + void fillRectList (const RectangleList&) override; + void fillPath (const Path&, const AffineTransform&) override; + void drawImage (const Image&, const AffineTransform&) override; + void drawLine (const Line &) override; //============================================================================== - const Font& getFont(); - void setFont (const Font& newFont); - void drawGlyph (int glyphNumber, const AffineTransform& transform); + const Font& getFont() override; + void setFont (const Font&) override; + void drawGlyph (int glyphNumber, const AffineTransform&) override; protected: //============================================================================== @@ -101,7 +95,7 @@ protected: SavedState(); ~SavedState(); - RectangleList clip; + RectangleList clip; int xOffset, yOffset; FillType fillType; Font font; @@ -113,15 +107,15 @@ protected: OwnedArray stateStack; void writeClip(); - void writeColour (const Colour& colour); - void writePath (const Path& path) const; + void writeColour (Colour colour); + void writePath (const Path&) const; void writeXY (float x, float y) const; - void writeTransform (const AffineTransform& trans) const; - void writeImage (const Image& im, int sx, int sy, int maxW, int maxH) const; + void writeTransform (const AffineTransform&) const; + void writeImage (const Image&, int sx, int sy, int maxW, int maxH) const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsPostScriptRenderer); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsPostScriptRenderer) }; -#endif // __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ +#endif // JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index f540222..a57e4d6 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -1,2237 +1,38 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4127) // "expression is constant" warning - - #if JUCE_DEBUG - #pragma optimize ("t", on) // optimise just this file, to avoid sluggish graphics when debugging - #pragma warning (disable: 4714) // warning about forcedinline methods not being inlined - #endif -#endif - -namespace SoftwareRendererClasses -{ - -//============================================================================== -template -class SolidColourEdgeTableRenderer -{ -public: - SolidColourEdgeTableRenderer (const Image::BitmapData& data_, const PixelARGB& colour) - : data (data_), - sourceColour (colour) - { - if (sizeof (PixelType) == 3) - { - areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen() - && sourceColour.getGreen() == sourceColour.getBlue(); - filler[0].set (sourceColour); - filler[1].set (sourceColour); - filler[2].set (sourceColour); - filler[3].set (sourceColour); - } - } - - forcedinline void setEdgeTableYPos (const int y) noexcept - { - linePixels = (PixelType*) data.getLinePointer (y); - } - - forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept - { - if (replaceExisting) - linePixels[x].set (sourceColour); - else - linePixels[x].blend (sourceColour, (uint32) alphaLevel); - } - - forcedinline void handleEdgeTablePixelFull (const int x) const noexcept - { - if (replaceExisting) - linePixels[x].set (sourceColour); - else - linePixels[x].blend (sourceColour); - } - - forcedinline void handleEdgeTableLine (const int x, const int width, const int alphaLevel) const noexcept - { - PixelARGB p (sourceColour); - p.multiplyAlpha (alphaLevel); - - PixelType* dest = linePixels + x; - - if (replaceExisting || p.getAlpha() >= 0xff) - replaceLine (dest, p, width); - else - blendLine (dest, p, width); - } - - forcedinline void handleEdgeTableLineFull (const int x, const int width) const noexcept - { - PixelType* dest = linePixels + x; - - if (replaceExisting || sourceColour.getAlpha() >= 0xff) - replaceLine (dest, sourceColour, width); - else - blendLine (dest, sourceColour, width); - } - -private: - const Image::BitmapData& data; - PixelType* linePixels; - PixelARGB sourceColour; - PixelRGB filler [4]; - bool areRGBComponentsEqual; - - inline void blendLine (PixelType* dest, const PixelARGB& colour, int width) const noexcept - { - do - { - dest->blend (colour); - ++dest; - } while (--width > 0); - } - - forcedinline void replaceLine (PixelRGB* dest, const PixelARGB& colour, int width) const noexcept - { - if (areRGBComponentsEqual) // if all the component values are the same, we can cheat.. - { - memset (dest, colour.getRed(), (size_t) width * 3); - } - else - { - if (width >> 5) - { - const int* const intFiller = reinterpret_cast (filler); - - while (width > 8 && (((pointer_sized_int) dest) & 7) != 0) - { - dest->set (colour); - ++dest; - --width; - } - - while (width > 4) - { - int* d = reinterpret_cast (dest); - *d++ = intFiller[0]; - *d++ = intFiller[1]; - *d++ = intFiller[2]; - dest = reinterpret_cast (d); - width -= 4; - } - } - - while (--width >= 0) - { - dest->set (colour); - ++dest; - } - } - } - - forcedinline void replaceLine (PixelAlpha* const dest, const PixelARGB& colour, int const width) const noexcept - { - memset (dest, colour.getAlpha(), (size_t) width); - } - - forcedinline void replaceLine (PixelARGB* dest, const PixelARGB& colour, int width) const noexcept - { - do - { - dest->set (colour); - ++dest; - - } while (--width > 0); - } - - JUCE_DECLARE_NON_COPYABLE (SolidColourEdgeTableRenderer); -}; - -//============================================================================== -class LinearGradientPixelGenerator -{ -public: - LinearGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform& transform, - const PixelARGB* const lookupTable_, const int numEntries_) - : lookupTable (lookupTable_), - numEntries (numEntries_) - { - jassert (numEntries_ >= 0); - Point p1 (gradient.point1); - Point p2 (gradient.point2); - - if (! transform.isIdentity()) - { - const Line l (p2, p1); - Point p3 = l.getPointAlongLine (0.0f, 100.0f); - - p1.applyTransform (transform); - p2.applyTransform (transform); - p3.applyTransform (transform); - - p2 = Line (p2, p3).findNearestPointTo (p1); - } - - vertical = std::abs (p1.x - p2.x) < 0.001f; - horizontal = std::abs (p1.y - p2.y) < 0.001f; - - if (vertical) - { - scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.y - p1.y)); - start = roundToInt (p1.y * scale); - } - else if (horizontal) - { - scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.x - p1.x)); - start = roundToInt (p1.x * scale); - } - else - { - grad = (p2.getY() - p1.y) / (double) (p1.x - p2.x); - yTerm = p1.getY() - p1.x / grad; - scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (p2.y * grad - p2.x))); - grad *= scale; - } - } - - forcedinline void setY (const int y) noexcept - { - if (vertical) - linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)]; - else if (! horizontal) - start = roundToInt ((y - yTerm) * grad); - } - - inline PixelARGB getPixel (const int x) const noexcept - { - return vertical ? linePix - : lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; - } - -private: - const PixelARGB* const lookupTable; - const int numEntries; - PixelARGB linePix; - int start, scale; - double grad, yTerm; - bool vertical, horizontal; - enum { numScaleBits = 12 }; - - JUCE_DECLARE_NON_COPYABLE (LinearGradientPixelGenerator); -}; - -//============================================================================== -class RadialGradientPixelGenerator -{ -public: - RadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform&, - const PixelARGB* const lookupTable_, const int numEntries_) - : lookupTable (lookupTable_), - numEntries (numEntries_), - gx1 (gradient.point1.x), - gy1 (gradient.point1.y) - { - jassert (numEntries_ >= 0); - const Point diff (gradient.point1 - gradient.point2); - maxDist = diff.x * diff.x + diff.y * diff.y; - invScale = numEntries / std::sqrt (maxDist); - jassert (roundToInt (std::sqrt (maxDist) * invScale) <= numEntries); - } - - forcedinline void setY (const int y) noexcept - { - dy = y - gy1; - dy *= dy; - } - - inline PixelARGB getPixel (const int px) const noexcept - { - double x = px - gx1; - x *= x; - x += dy; - - return lookupTable [x >= maxDist ? numEntries : roundToInt (std::sqrt (x) * invScale)]; - } - -protected: - const PixelARGB* const lookupTable; - const int numEntries; - const double gx1, gy1; - double maxDist, invScale, dy; - - JUCE_DECLARE_NON_COPYABLE (RadialGradientPixelGenerator); -}; - -//============================================================================== -class TransformedRadialGradientPixelGenerator : public RadialGradientPixelGenerator -{ -public: - TransformedRadialGradientPixelGenerator (const ColourGradient& gradient, const AffineTransform& transform, - const PixelARGB* const lookupTable_, const int numEntries_) - : RadialGradientPixelGenerator (gradient, transform, lookupTable_, numEntries_), - inverseTransform (transform.inverted()) - { - tM10 = inverseTransform.mat10; - tM00 = inverseTransform.mat00; - } - - forcedinline void setY (const int y) noexcept - { - lineYM01 = inverseTransform.mat01 * y + inverseTransform.mat02 - gx1; - lineYM11 = inverseTransform.mat11 * y + inverseTransform.mat12 - gy1; - } - - inline PixelARGB getPixel (const int px) const noexcept - { - double x = px; - const double y = tM10 * x + lineYM11; - x = tM00 * x + lineYM01; - x *= x; - x += y * y; - - if (x >= maxDist) - return lookupTable [numEntries]; - else - return lookupTable [jmin (numEntries, roundToInt (std::sqrt (x) * invScale))]; - } - -private: - double tM10, tM00, lineYM01, lineYM11; - const AffineTransform inverseTransform; - - JUCE_DECLARE_NON_COPYABLE (TransformedRadialGradientPixelGenerator); -}; - -//============================================================================== -template -class GradientEdgeTableRenderer : public GradientType -{ -public: - GradientEdgeTableRenderer (const Image::BitmapData& destData_, const ColourGradient& gradient, const AffineTransform& transform, - const PixelARGB* const lookupTable_, const int numEntries_) - : GradientType (gradient, transform, lookupTable_, numEntries_ - 1), - destData (destData_) - { - } - - forcedinline void setEdgeTableYPos (const int y) noexcept - { - linePixels = (PixelType*) destData.getLinePointer (y); - GradientType::setY (y); - } - - forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept - { - linePixels[x].blend (GradientType::getPixel (x), (uint32) alphaLevel); - } - - forcedinline void handleEdgeTablePixelFull (const int x) const noexcept - { - linePixels[x].blend (GradientType::getPixel (x)); - } - - void handleEdgeTableLine (int x, int width, const int alphaLevel) const noexcept - { - PixelType* dest = linePixels + x; - - if (alphaLevel < 0xff) - { - do - { - (dest++)->blend (GradientType::getPixel (x++), (uint32) alphaLevel); - } while (--width > 0); - } - else - { - do - { - (dest++)->blend (GradientType::getPixel (x++)); - } while (--width > 0); - } - } - - void handleEdgeTableLineFull (int x, int width) const noexcept - { - PixelType* dest = linePixels + x; - - do - { - (dest++)->blend (GradientType::getPixel (x++)); - } while (--width > 0); - } - -private: - const Image::BitmapData& destData; - PixelType* linePixels; - - JUCE_DECLARE_NON_COPYABLE (GradientEdgeTableRenderer); -}; - -//============================================================================== -template -class ImageFillEdgeTableRenderer -{ -public: - ImageFillEdgeTableRenderer (const Image::BitmapData& destData_, - const Image::BitmapData& srcData_, - const int extraAlpha_, - const int x, const int y) - : destData (destData_), - srcData (srcData_), - extraAlpha (extraAlpha_ + 1), - xOffset (repeatPattern ? negativeAwareModulo (x, srcData_.width) - srcData_.width : x), - yOffset (repeatPattern ? negativeAwareModulo (y, srcData_.height) - srcData_.height : y) - { - } - - forcedinline void setEdgeTableYPos (int y) noexcept - { - linePixels = (DestPixelType*) destData.getLinePointer (y); - - y -= yOffset; - if (repeatPattern) - { - jassert (y >= 0); - y %= srcData.height; - } - - sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y); - } - - forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const noexcept - { - alphaLevel = (alphaLevel * extraAlpha) >> 8; - - linePixels[x].blend (sourceLineStart [repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)], (uint32) alphaLevel); - } - - forcedinline void handleEdgeTablePixelFull (const int x) const noexcept - { - linePixels[x].blend (sourceLineStart [repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)], (uint32) extraAlpha); - } - - void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept - { - DestPixelType* dest = linePixels + x; - alphaLevel = (alphaLevel * extraAlpha) >> 8; - x -= xOffset; - - jassert (repeatPattern || (x >= 0 && x + width <= srcData.width)); - - if (alphaLevel < 0xfe) - { - do - { - dest++ ->blend (sourceLineStart [repeatPattern ? (x++ % srcData.width) : x++], (uint32) alphaLevel); - } while (--width > 0); - } - else - { - if (repeatPattern) - { - do - { - dest++ ->blend (sourceLineStart [x++ % srcData.width]); - } while (--width > 0); - } - else - { - copyRow (dest, sourceLineStart + x, width); - } - } - } - - void handleEdgeTableLineFull (int x, int width) const noexcept - { - DestPixelType* dest = linePixels + x; - x -= xOffset; - - jassert (repeatPattern || (x >= 0 && x + width <= srcData.width)); - - if (extraAlpha < 0xfe) - { - do - { - dest++ ->blend (sourceLineStart [repeatPattern ? (x++ % srcData.width) : x++], (uint32) extraAlpha); - } while (--width > 0); - } - else - { - if (repeatPattern) - { - do - { - dest++ ->blend (sourceLineStart [x++ % srcData.width]); - } while (--width > 0); - } - else - { - copyRow (dest, sourceLineStart + x, width); - } - } - } - - void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) - { - jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width); - SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset); - uint8* mask = (uint8*) (s + x - xOffset); - - if (sizeof (SrcPixelType) == sizeof (PixelARGB)) - mask += PixelARGB::indexA; - - et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width); - } - -private: - const Image::BitmapData& destData; - const Image::BitmapData& srcData; - const int extraAlpha, xOffset, yOffset; - DestPixelType* linePixels; - SrcPixelType* sourceLineStart; - - template - static forcedinline void copyRow (PixelType1* dest, PixelType2* src, int width) noexcept - { - do - { - dest++ ->blend (*src++); - } while (--width > 0); - } - - static forcedinline void copyRow (PixelRGB* dest, PixelRGB* src, int width) noexcept - { - memcpy (dest, src, sizeof (PixelRGB) * (size_t) width); - } - - JUCE_DECLARE_NON_COPYABLE (ImageFillEdgeTableRenderer); -}; - -//============================================================================== -template -class TransformedImageFillEdgeTableRenderer -{ -public: - TransformedImageFillEdgeTableRenderer (const Image::BitmapData& destData_, - const Image::BitmapData& srcData_, - const AffineTransform& transform, - const int extraAlpha_, - const bool betterQuality_) - : interpolator (transform, - betterQuality_ ? 0.5f : 0.0f, - betterQuality_ ? -128 : 0), - destData (destData_), - srcData (srcData_), - extraAlpha (extraAlpha_ + 1), - betterQuality (betterQuality_), - maxX (srcData_.width - 1), - maxY (srcData_.height - 1), - scratchSize (2048) - { - scratchBuffer.malloc (scratchSize); - } - - forcedinline void setEdgeTableYPos (const int newY) noexcept - { - y = newY; - linePixels = (DestPixelType*) destData.getLinePointer (newY); - } - - forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept - { - SrcPixelType p; - generate (&p, x, 1); - - linePixels[x].blend (p, (uint32) (alphaLevel * extraAlpha) >> 8); - } - - forcedinline void handleEdgeTablePixelFull (const int x) noexcept - { - SrcPixelType p; - generate (&p, x, 1); - - linePixels[x].blend (p, (uint32) extraAlpha); - } - - void handleEdgeTableLine (const int x, int width, int alphaLevel) noexcept - { - if (width > (int) scratchSize) - { - scratchSize = (size_t) width; - scratchBuffer.malloc (scratchSize); - } - - SrcPixelType* span = scratchBuffer; - generate (span, x, width); - - DestPixelType* dest = linePixels + x; - alphaLevel *= extraAlpha; - alphaLevel >>= 8; - - if (alphaLevel < 0xfe) - { - do - { - dest++ ->blend (*span++, (uint32) alphaLevel); - } while (--width > 0); - } - else - { - do - { - dest++ ->blend (*span++); - } while (--width > 0); - } - } - - forcedinline void handleEdgeTableLineFull (const int x, int width) noexcept - { - handleEdgeTableLine (x, width, 255); - } - - void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) - { - if (width > (int) scratchSize) - { - scratchSize = (size_t) width; - scratchBuffer.malloc (scratchSize); - } - - y = y_; - generate (scratchBuffer.getData(), x, width); - - et.clipLineToMask (x, y_, - reinterpret_cast (scratchBuffer.getData()) + SrcPixelType::indexA, - sizeof (SrcPixelType), width); - } - -private: - //============================================================================== - template - void generate (PixelType* dest, const int x, int numPixels) noexcept - { - this->interpolator.setStartOfLine ((float) x, (float) y, numPixels); - - do - { - int hiResX, hiResY; - this->interpolator.next (hiResX, hiResY); - - int loResX = hiResX >> 8; - int loResY = hiResY >> 8; - - if (repeatPattern) - { - loResX = negativeAwareModulo (loResX, srcData.width); - loResY = negativeAwareModulo (loResY, srcData.height); - } - - if (betterQuality) - { - if (isPositiveAndBelow (loResX, maxX)) - { - if (isPositiveAndBelow (loResY, maxY)) - { - // In the centre of the image.. - render4PixelAverage (dest, this->srcData.getPixelPointer (loResX, loResY), - hiResX & 255, hiResY & 255); - ++dest; - continue; - } - else - { - // At a top or bottom edge.. - if (! repeatPattern) - { - if (loResY < 0) - render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, 0), hiResX & 255); - else - render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, maxY), hiResX & 255); - - ++dest; - continue; - } - } - } - else - { - if (isPositiveAndBelow (loResY, maxY)) - { - // At a left or right hand edge.. - if (! repeatPattern) - { - if (loResX < 0) - render2PixelAverageY (dest, this->srcData.getPixelPointer (0, loResY), hiResY & 255); - else - render2PixelAverageY (dest, this->srcData.getPixelPointer (maxX, loResY), hiResY & 255); - - ++dest; - continue; - } - } - } - } - - if (! repeatPattern) - { - if (loResX < 0) loResX = 0; - if (loResY < 0) loResY = 0; - if (loResX > maxX) loResX = maxX; - if (loResY > maxY) loResY = maxY; - } - - dest->set (*(const PixelType*) this->srcData.getPixelPointer (loResX, loResY)); - ++dest; - - } while (--numPixels > 0); - } - - //============================================================================== - void render4PixelAverage (PixelARGB* const dest, const uint8* src, const int subPixelX, const int subPixelY) noexcept - { - uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 }; - - uint32 weight = (uint32) ((256 - subPixelX) * (256 - subPixelY)); - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - c[3] += weight * src[3]; - - weight = (uint32) (subPixelX * (256 - subPixelY)); - c[0] += weight * src[4]; - c[1] += weight * src[5]; - c[2] += weight * src[6]; - c[3] += weight * src[7]; - - src += this->srcData.lineStride; - - weight = (uint32) ((256 - subPixelX) * subPixelY); - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - c[3] += weight * src[3]; - - weight = (uint32) (subPixelX * subPixelY); - c[0] += weight * src[4]; - c[1] += weight * src[5]; - c[2] += weight * src[6]; - c[3] += weight * src[7]; - - dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16), - (uint8) (c[PixelARGB::indexR] >> 16), - (uint8) (c[PixelARGB::indexG] >> 16), - (uint8) (c[PixelARGB::indexB] >> 16)); - } - - void render2PixelAverageX (PixelARGB* const dest, const uint8* src, const uint32 subPixelX) noexcept - { - uint32 c[4] = { 128, 128, 128, 128 }; - - uint32 weight = 256 - subPixelX; - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - c[3] += weight * src[3]; - - weight = subPixelX; - c[0] += weight * src[4]; - c[1] += weight * src[5]; - c[2] += weight * src[6]; - c[3] += weight * src[7]; - - dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8), - (uint8) (c[PixelARGB::indexR] >> 8), - (uint8) (c[PixelARGB::indexG] >> 8), - (uint8) (c[PixelARGB::indexB] >> 8)); - } - - void render2PixelAverageY (PixelARGB* const dest, const uint8* src, const uint32 subPixelY) noexcept - { - uint32 c[4] = { 128, 128, 128, 128 }; - - uint32 weight = 256 - subPixelY; - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - c[3] += weight * src[3]; - - src += this->srcData.lineStride; - - weight = subPixelY; - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - c[3] += weight * src[3]; - - dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8), - (uint8) (c[PixelARGB::indexR] >> 8), - (uint8) (c[PixelARGB::indexG] >> 8), - (uint8) (c[PixelARGB::indexB] >> 8)); - } - - //============================================================================== - void render4PixelAverage (PixelRGB* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept - { - uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 }; - - uint32 weight = (256 - subPixelX) * (256 - subPixelY); - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - - weight = subPixelX * (256 - subPixelY); - c[0] += weight * src[3]; - c[1] += weight * src[4]; - c[2] += weight * src[5]; - - src += this->srcData.lineStride; - - weight = (256 - subPixelX) * subPixelY; - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - - weight = subPixelX * subPixelY; - c[0] += weight * src[3]; - c[1] += weight * src[4]; - c[2] += weight * src[5]; - - dest->setARGB ((uint8) 255, - (uint8) (c[PixelRGB::indexR] >> 16), - (uint8) (c[PixelRGB::indexG] >> 16), - (uint8) (c[PixelRGB::indexB] >> 16)); - } - - void render2PixelAverageX (PixelRGB* const dest, const uint8* src, const uint32 subPixelX) noexcept - { - uint32 c[3] = { 128, 128, 128 }; - - const uint32 weight = 256 - subPixelX; - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - - c[0] += subPixelX * src[3]; - c[1] += subPixelX * src[4]; - c[2] += subPixelX * src[5]; - - dest->setARGB ((uint8) 255, - (uint8) (c[PixelRGB::indexR] >> 8), - (uint8) (c[PixelRGB::indexG] >> 8), - (uint8) (c[PixelRGB::indexB] >> 8)); - } - - void render2PixelAverageY (PixelRGB* const dest, const uint8* src, const uint32 subPixelY) noexcept - { - uint32 c[3] = { 128, 128, 128 }; - - const uint32 weight = 256 - subPixelY; - c[0] += weight * src[0]; - c[1] += weight * src[1]; - c[2] += weight * src[2]; - - src += this->srcData.lineStride; - - c[0] += subPixelY * src[0]; - c[1] += subPixelY * src[1]; - c[2] += subPixelY * src[2]; - - dest->setARGB ((uint8) 255, - (uint8) (c[PixelRGB::indexR] >> 8), - (uint8) (c[PixelRGB::indexG] >> 8), - (uint8) (c[PixelRGB::indexB] >> 8)); - } - - //============================================================================== - void render4PixelAverage (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept - { - uint32 c = 256 * 128; - c += src[0] * ((256 - subPixelX) * (256 - subPixelY)); - c += src[1] * (subPixelX * (256 - subPixelY)); - src += this->srcData.lineStride; - c += src[0] * ((256 - subPixelX) * subPixelY); - c += src[1] * (subPixelX * subPixelY); - - *((uint8*) dest) = (uint8) (c >> 16); - } - - void render2PixelAverageX (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX) noexcept - { - uint32 c = 128; - c += src[0] * (256 - subPixelX); - c += src[1] * subPixelX; - *((uint8*) dest) = (uint8) (c >> 8); - } - - void render2PixelAverageY (PixelAlpha* const dest, const uint8* src, const uint32 subPixelY) noexcept - { - uint32 c = 128; - c += src[0] * (256 - subPixelY); - src += this->srcData.lineStride; - c += src[0] * subPixelY; - *((uint8*) dest) = (uint8) (c >> 8); - } - - //============================================================================== - class TransformedImageSpanInterpolator - { - public: - TransformedImageSpanInterpolator (const AffineTransform& transform, const float pixelOffset_, const int pixelOffsetInt_) noexcept - : inverseTransform (transform.inverted()), - pixelOffset (pixelOffset_), pixelOffsetInt (pixelOffsetInt_) - {} - - void setStartOfLine (float x, float y, const int numPixels) noexcept - { - jassert (numPixels > 0); - - x += pixelOffset; - y += pixelOffset; - float x1 = x, y1 = y; - x += numPixels; - inverseTransform.transformPoints (x1, y1, x, y); - - xBresenham.set ((int) (x1 * 256.0f), (int) (x * 256.0f), numPixels, pixelOffsetInt); - yBresenham.set ((int) (y1 * 256.0f), (int) (y * 256.0f), numPixels, pixelOffsetInt); - } - - void next (int& x, int& y) noexcept - { - x = xBresenham.n; - xBresenham.stepToNext(); - y = yBresenham.n; - yBresenham.stepToNext(); - } - - private: - class BresenhamInterpolator - { - public: - BresenhamInterpolator() noexcept {} - - void set (const int n1, const int n2, const int numSteps_, const int pixelOffsetInt) noexcept - { - numSteps = numSteps_; - step = (n2 - n1) / numSteps; - remainder = modulo = (n2 - n1) % numSteps; - n = n1 + pixelOffsetInt; - - if (modulo <= 0) - { - modulo += numSteps; - remainder += numSteps; - --step; - } - - modulo -= numSteps; - } - - forcedinline void stepToNext() noexcept - { - modulo += remainder; - n += step; - - if (modulo > 0) - { - modulo -= numSteps; - ++n; - } - } - - int n; - - private: - int numSteps, step, modulo, remainder; - }; - - const AffineTransform inverseTransform; - BresenhamInterpolator xBresenham, yBresenham; - const float pixelOffset; - const int pixelOffsetInt; - - JUCE_DECLARE_NON_COPYABLE (TransformedImageSpanInterpolator); - }; - - //============================================================================== - TransformedImageSpanInterpolator interpolator; - const Image::BitmapData& destData; - const Image::BitmapData& srcData; - const int extraAlpha; - const bool betterQuality; - const int maxX, maxY; - int y; - DestPixelType* linePixels; - HeapBlock scratchBuffer; - size_t scratchSize; - - JUCE_DECLARE_NON_COPYABLE (TransformedImageFillEdgeTableRenderer); -}; - -//============================================================================== -class ClipRegionBase : public SingleThreadedReferenceCountedObject -{ -public: - ClipRegionBase() {} - virtual ~ClipRegionBase() {} - - typedef ReferenceCountedObjectPtr Ptr; - - virtual Ptr clone() const = 0; - virtual Ptr applyClipTo (const Ptr& target) const = 0; - - virtual Ptr clipToRectangle (const Rectangle& r) = 0; - virtual Ptr clipToRectangleList (const RectangleList& r) = 0; - virtual Ptr excludeClipRectangle (const Rectangle& r) = 0; - virtual Ptr clipToPath (const Path& p, const AffineTransform& transform) = 0; - virtual Ptr clipToEdgeTable (const EdgeTable& et) = 0; - virtual Ptr clipToImageAlpha (const Image& image, const AffineTransform& t, const bool betterQuality) = 0; - virtual void translate (const Point& delta) = 0; - - virtual bool clipRegionIntersects (const Rectangle& r) const = 0; - virtual Rectangle getClipBounds() const = 0; - - virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const = 0; - virtual void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const = 0; - virtual void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const = 0; - virtual void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const = 0; - virtual void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& t, bool betterQuality, bool tiledFill) const = 0; - virtual void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const = 0; - -protected: - //============================================================================== - template - static void renderImageTransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, - const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) - { - switch (destData.pixelFormat) - { - case Image::ARGB: - switch (srcData.pixelFormat) - { - case Image::ARGB: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - case Image::RGB: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - default: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - } - break; - - case Image::RGB: - switch (srcData.pixelFormat) - { - case Image::ARGB: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - case Image::RGB: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - default: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - } - break; - - default: - switch (srcData.pixelFormat) - { - case Image::ARGB: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - case Image::RGB: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - default: - if (tiledFill) { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - else { TransformedImageFillEdgeTableRenderer r (destData, srcData, transform, alpha, betterQuality); iter.iterate (r); } - break; - } - break; - } - } - - template - static void renderImageUntransformedInternal (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) - { - switch (destData.pixelFormat) - { - case Image::ARGB: - switch (srcData.pixelFormat) - { - case Image::ARGB: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - case Image::RGB: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - default: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - } - break; - - case Image::RGB: - switch (srcData.pixelFormat) - { - case Image::ARGB: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - case Image::RGB: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - default: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - } - break; - - default: - switch (srcData.pixelFormat) - { - case Image::ARGB: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - case Image::RGB: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - default: - if (tiledFill) { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - else { ImageFillEdgeTableRenderer r (destData, srcData, alpha, x, y); iter.iterate (r); } - break; - } - break; - } - } - - template - static void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, const PixelARGB& fillColour, const bool replaceContents, DestPixelType*) - { - jassert (destData.pixelStride == sizeof (DestPixelType)); - if (replaceContents) - { - SolidColourEdgeTableRenderer r (destData, fillColour); - iter.iterate (r); - } - else - { - SolidColourEdgeTableRenderer r (destData, fillColour); - iter.iterate (r); - } - } - - template - static void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform, - const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) - { - jassert (destData.pixelStride == sizeof (DestPixelType)); - - if (g.isRadial) - { - if (isIdentity) - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - iter.iterate (renderer); - } - else - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - iter.iterate (renderer); - } - } - else - { - GradientEdgeTableRenderer renderer (destData, g, transform, lookupTable, numLookupEntries); - iter.iterate (renderer); - } - } -}; - -//============================================================================== -class ClipRegion_EdgeTable : public ClipRegionBase -{ -public: - ClipRegion_EdgeTable (const EdgeTable& e) : edgeTable (e) {} - ClipRegion_EdgeTable (const Rectangle& r) : edgeTable (r) {} - ClipRegion_EdgeTable (const Rectangle& r) : edgeTable (r) {} - ClipRegion_EdgeTable (const RectangleList& r) : edgeTable (r) {} - ClipRegion_EdgeTable (const Rectangle& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {} - ClipRegion_EdgeTable (const ClipRegion_EdgeTable& other) : edgeTable (other.edgeTable) {} - - Ptr clone() const - { - return new ClipRegion_EdgeTable (*this); - } - - Ptr applyClipTo (const Ptr& target) const - { - return target->clipToEdgeTable (edgeTable); - } - - Ptr clipToRectangle (const Rectangle& r) - { - edgeTable.clipToRectangle (r); - return edgeTable.isEmpty() ? nullptr : this; - } - - Ptr clipToRectangleList (const RectangleList& r) - { - RectangleList inverse (edgeTable.getMaximumBounds()); - - if (inverse.subtract (r)) - for (RectangleList::Iterator iter (inverse); iter.next();) - edgeTable.excludeRectangle (*iter.getRectangle()); - - return edgeTable.isEmpty() ? nullptr : this; - } - - Ptr excludeClipRectangle (const Rectangle& r) - { - edgeTable.excludeRectangle (r); - return edgeTable.isEmpty() ? nullptr : this; - } - - Ptr clipToPath (const Path& p, const AffineTransform& transform) - { - EdgeTable et (edgeTable.getMaximumBounds(), p, transform); - edgeTable.clipToEdgeTable (et); - return edgeTable.isEmpty() ? nullptr : this; - } - - Ptr clipToEdgeTable (const EdgeTable& et) - { - edgeTable.clipToEdgeTable (et); - return edgeTable.isEmpty() ? nullptr : this; - } - - Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality) - { - const Image::BitmapData srcData (image, Image::BitmapData::readOnly); - - if (transform.isOnlyTranslation()) - { - // If our translation doesn't involve any distortion, just use a simple blit.. - const int tx = (int) (transform.getTranslationX() * 256.0f); - const int ty = (int) (transform.getTranslationY() * 256.0f); - - if ((! betterQuality) || ((tx | ty) & 224) == 0) - { - const int imageX = ((tx + 128) >> 8); - const int imageY = ((ty + 128) >> 8); - - if (image.getFormat() == Image::ARGB) - straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0); - else - straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0); - - return edgeTable.isEmpty() ? nullptr : this; - } - } - - if (transform.isSingularity()) - return nullptr; - - { - Path p; - p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); - EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform); - edgeTable.clipToEdgeTable (et2); - } - - if (! edgeTable.isEmpty()) - { - if (image.getFormat() == Image::ARGB) - transformedClipImage (srcData, transform, betterQuality, (PixelARGB*) 0); - else - transformedClipImage (srcData, transform, betterQuality, (PixelAlpha*) 0); - } - - return edgeTable.isEmpty() ? nullptr : this; - } - - void translate (const Point& delta) - { - edgeTable.translate ((float) delta.x, delta.y); - } - - bool clipRegionIntersects (const Rectangle& r) const - { - return edgeTable.getMaximumBounds().intersects (r); - } - - Rectangle getClipBounds() const - { - return edgeTable.getMaximumBounds(); - } - - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const - { - const Rectangle totalClip (edgeTable.getMaximumBounds()); - const Rectangle clipped (totalClip.getIntersection (area)); - - if (! clipped.isEmpty()) - { - ClipRegion_EdgeTable et (clipped); - et.edgeTable.clipToEdgeTable (edgeTable); - et.fillAllWithColour (destData, colour, replaceContents); - } - } - - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const - { - const Rectangle totalClip (edgeTable.getMaximumBounds().toFloat()); - const Rectangle clipped (totalClip.getIntersection (area)); - - if (! clipped.isEmpty()) - { - ClipRegion_EdgeTable et (clipped); - et.edgeTable.clipToEdgeTable (edgeTable); - et.fillAllWithColour (destData, colour, false); - } - } - - void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const - { - switch (destData.pixelFormat) - { - case Image::ARGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelRGB*) 0); break; - default: renderSolidFill (edgeTable, destData, colour, replaceContents, (PixelAlpha*) 0); break; - } - } - - void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const - { - HeapBlock lookupTable; - const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); - jassert (numLookupEntries > 0); - - switch (destData.pixelFormat) - { - case Image::ARGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; - case Image::RGB: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; - default: renderGradient (edgeTable, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; - } - } - - void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const - { - renderImageTransformedInternal (edgeTable, destData, srcData, alpha, transform, betterQuality, tiledFill); - } - - void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const - { - renderImageUntransformedInternal (edgeTable, destData, srcData, alpha, x, y, tiledFill); - } - - EdgeTable edgeTable; - -private: - //============================================================================== - template - void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const bool betterQuality, const SrcPixelType*) - { - TransformedImageFillEdgeTableRenderer renderer (srcData, srcData, transform, 255, betterQuality); - - for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y) - renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(), - edgeTable.getMaximumBounds().getWidth()); - } - - template - void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*) - { - Rectangle r (imageX, imageY, srcData.width, srcData.height); - edgeTable.clipToRectangle (r); - - ImageFillEdgeTableRenderer renderer (srcData, srcData, 255, imageX, imageY); - - for (int y = 0; y < r.getHeight(); ++y) - renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth()); - } - - ClipRegion_EdgeTable& operator= (const ClipRegion_EdgeTable&); -}; - - -//============================================================================== -class ClipRegion_RectangleList : public ClipRegionBase -{ -public: - ClipRegion_RectangleList (const Rectangle& r) : clip (r) {} - ClipRegion_RectangleList (const RectangleList& r) : clip (r) {} - ClipRegion_RectangleList (const ClipRegion_RectangleList& other) : clip (other.clip) {} - - Ptr clone() const - { - return new ClipRegion_RectangleList (*this); - } - - Ptr applyClipTo (const Ptr& target) const - { - return target->clipToRectangleList (clip); - } - - Ptr clipToRectangle (const Rectangle& r) - { - clip.clipTo (r); - return clip.isEmpty() ? nullptr : this; - } - - Ptr clipToRectangleList (const RectangleList& r) - { - clip.clipTo (r); - return clip.isEmpty() ? nullptr : this; - } - - Ptr excludeClipRectangle (const Rectangle& r) - { - clip.subtract (r); - return clip.isEmpty() ? nullptr : this; - } - - Ptr clipToPath (const Path& p, const AffineTransform& transform) - { - return toEdgeTable()->clipToPath (p, transform); - } - - Ptr clipToEdgeTable (const EdgeTable& et) - { - return toEdgeTable()->clipToEdgeTable (et); - } - - Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const bool betterQuality) - { - return toEdgeTable()->clipToImageAlpha (image, transform, betterQuality); - } - - void translate (const Point& delta) - { - clip.offsetAll (delta.x, delta.y); - } - - bool clipRegionIntersects (const Rectangle& r) const - { - return clip.intersects (r); - } - - Rectangle getClipBounds() const - { - return clip.getBounds(); - } - - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour, bool replaceContents) const - { - SubRectangleIterator iter (clip, area); - - switch (destData.pixelFormat) - { - case Image::ARGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break; - default: renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break; - } - } - - void fillRectWithColour (Image::BitmapData& destData, const Rectangle& area, const PixelARGB& colour) const - { - SubRectangleIteratorFloat iter (clip, area); - - switch (destData.pixelFormat) - { - case Image::ARGB: renderSolidFill (iter, destData, colour, false, (PixelARGB*) 0); break; - case Image::RGB: renderSolidFill (iter, destData, colour, false, (PixelRGB*) 0); break; - default: renderSolidFill (iter, destData, colour, false, (PixelAlpha*) 0); break; - } - } - - void fillAllWithColour (Image::BitmapData& destData, const PixelARGB& colour, bool replaceContents) const - { - switch (destData.pixelFormat) - { - case Image::ARGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelARGB*) 0); break; - case Image::RGB: renderSolidFill (*this, destData, colour, replaceContents, (PixelRGB*) 0); break; - default: renderSolidFill (*this, destData, colour, replaceContents, (PixelAlpha*) 0); break; - } - } - - void fillAllWithGradient (Image::BitmapData& destData, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const - { - HeapBlock lookupTable; - const int numLookupEntries = gradient.createLookupTable (transform, lookupTable); - jassert (numLookupEntries > 0); - - switch (destData.pixelFormat) - { - case Image::ARGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; - case Image::RGB: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; - default: renderGradient (*this, destData, gradient, transform, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; - } - } - - void renderImageTransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, const AffineTransform& transform, bool betterQuality, bool tiledFill) const - { - renderImageTransformedInternal (*this, destData, srcData, alpha, transform, betterQuality, tiledFill); - } - - void renderImageUntransformed (const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) const - { - renderImageUntransformedInternal (*this, destData, srcData, alpha, x, y, tiledFill); - } - - RectangleList clip; - - //============================================================================== - template - void iterate (Renderer& r) const noexcept - { - RectangleList::Iterator iter (clip); - - while (iter.next()) - { - const Rectangle rect (*iter.getRectangle()); - const int x = rect.getX(); - const int w = rect.getWidth(); - jassert (w > 0); - const int bottom = rect.getBottom(); - - for (int y = rect.getY(); y < bottom; ++y) - { - r.setEdgeTableYPos (y); - r.handleEdgeTableLineFull (x, w); - } - } - } - -private: - //============================================================================== - class SubRectangleIterator - { - public: - SubRectangleIterator (const RectangleList& clip_, const Rectangle& area_) - : clip (clip_), area (area_) - { - } - - template - void iterate (Renderer& r) const noexcept - { - RectangleList::Iterator iter (clip); - - while (iter.next()) - { - const Rectangle rect (iter.getRectangle()->getIntersection (area)); - - if (! rect.isEmpty()) - { - const int x = rect.getX(); - const int w = rect.getWidth(); - const int bottom = rect.getBottom(); - - for (int y = rect.getY(); y < bottom; ++y) - { - r.setEdgeTableYPos (y); - r.handleEdgeTableLineFull (x, w); - } - } - } - } - - private: - const RectangleList& clip; - const Rectangle area; - - JUCE_DECLARE_NON_COPYABLE (SubRectangleIterator); - }; - - //============================================================================== - class SubRectangleIteratorFloat - { - public: - SubRectangleIteratorFloat (const RectangleList& clip_, const Rectangle& area_) noexcept - : clip (clip_), area (area_) - { - } - - template - void iterate (Renderer& r) const noexcept - { - const RenderingHelpers::FloatRectangleRasterisingInfo f (area); - RectangleList::Iterator iter (clip); - - while (iter.next()) - { - const int clipLeft = iter.getRectangle()->getX(); - const int clipRight = iter.getRectangle()->getRight(); - const int clipTop = iter.getRectangle()->getY(); - const int clipBottom = iter.getRectangle()->getBottom(); - - if (f.totalBottom > clipTop && f.totalTop < clipBottom && f.totalRight > clipLeft && f.totalLeft < clipRight) - { - if (f.isOnePixelWide()) - { - if (f.topAlpha != 0 && f.totalTop >= clipTop) - { - r.setEdgeTableYPos (f.totalTop); - r.handleEdgeTablePixel (f.left, f.topAlpha); - } - - const int endY = jmin (f.bottom, clipBottom); - for (int y = jmax (clipTop, f.top); y < endY; ++y) - { - r.setEdgeTableYPos (y); - r.handleEdgeTablePixelFull (f.left); - } - - if (f.bottomAlpha != 0 && f.bottom < clipBottom) - { - r.setEdgeTableYPos (f.bottom); - r.handleEdgeTablePixel (f.left, f.bottomAlpha); - } - } - else - { - const int clippedLeft = jmax (f.left, clipLeft); - const int clippedWidth = jmin (f.right, clipRight) - clippedLeft; - const bool doLeftAlpha = f.leftAlpha != 0 && f.totalLeft >= clipLeft; - const bool doRightAlpha = f.rightAlpha != 0 && f.right < clipRight; - - if (f.topAlpha != 0 && f.totalTop >= clipTop) - { - r.setEdgeTableYPos (f.totalTop); - - if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getTopLeftCornerAlpha()); - if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.topAlpha); - if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getTopRightCornerAlpha()); - } - - const int endY = jmin (f.bottom, clipBottom); - for (int y = jmax (clipTop, f.top); y < endY; ++y) - { - r.setEdgeTableYPos (y); - - if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.leftAlpha); - if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth); - if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.rightAlpha); - } - - if (f.bottomAlpha != 0 && f.bottom < clipBottom) - { - r.setEdgeTableYPos (f.bottom); - - if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getBottomLeftCornerAlpha()); - if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.bottomAlpha); - if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getBottomRightCornerAlpha()); - } - } - } - } - } - - private: - const RectangleList& clip; - const Rectangle& area; - - JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat); - }; - - inline Ptr toEdgeTable() const { return new ClipRegion_EdgeTable (clip); } - - ClipRegion_RectangleList& operator= (const ClipRegion_RectangleList&); -}; - -} - -//============================================================================== -class LowLevelGraphicsSoftwareRenderer::SavedState -{ -public: - SavedState (const Image& image_, const Rectangle& clip_) - : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), - transform (0, 0), - interpolationQuality (Graphics::mediumResamplingQuality), - transparencyLayerAlpha (1.0f) - { - } - - SavedState (const Image& image_, const RectangleList& clip_, const int xOffset_, const int yOffset_) - : image (image_), clip (new SoftwareRendererClasses::ClipRegion_RectangleList (clip_)), - transform (xOffset_, yOffset_), - interpolationQuality (Graphics::mediumResamplingQuality), - transparencyLayerAlpha (1.0f) - { - } - - SavedState (const SavedState& other) - : image (other.image), clip (other.clip), transform (other.transform), - font (other.font), fillType (other.fillType), - interpolationQuality (other.interpolationQuality), - transparencyLayerAlpha (other.transparencyLayerAlpha) - { - } - - bool clipToRectangle (const Rectangle& r) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - { - cloneClipIfMultiplyReferenced(); - clip = clip->clipToRectangle (transform.translated (r)); - } - else - { - Path p; - p.addRectangle (r); - clipToPath (p, AffineTransform::identity); - } - } - - return clip != nullptr; - } - - bool clipToRectangleList (const RectangleList& r) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - { - cloneClipIfMultiplyReferenced(); - RectangleList offsetList (r); - offsetList.offsetAll (transform.xOffset, transform.yOffset); - clip = clip->clipToRectangleList (offsetList); - } - else - { - clipToPath (r.toPath(), AffineTransform::identity); - } - } - - return clip != nullptr; - } - - bool excludeClipRectangle (const Rectangle& r) - { - if (clip != nullptr) - { - cloneClipIfMultiplyReferenced(); - - if (transform.isOnlyTranslated) - { - clip = clip->excludeClipRectangle (transform.translated (r)); - } - else - { - Path p; - p.addRectangle (r.toFloat()); - p.applyTransform (transform.complexTransform); - p.addRectangle (clip->getClipBounds().toFloat()); - p.setUsingNonZeroWinding (false); - clip = clip->clipToPath (p, AffineTransform::identity); - } - } - - return clip != nullptr; - } - - void clipToPath (const Path& p, const AffineTransform& t) - { - if (clip != nullptr) - { - cloneClipIfMultiplyReferenced(); - clip = clip->clipToPath (p, transform.getTransformWith (t)); - } - } - - void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t) - { - if (clip != nullptr) - { - if (sourceImage.hasAlphaChannel()) - { - cloneClipIfMultiplyReferenced(); - clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t), - interpolationQuality != Graphics::lowResamplingQuality); - } - else - { - Path p; - p.addRectangle (sourceImage.getBounds()); - clipToPath (p, t); - } - } - } - - bool clipRegionIntersects (const Rectangle& r) const - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - return clip->clipRegionIntersects (transform.translated (r)); - else - return getClipBounds().intersects (r); - } - - return false; - } - - Rectangle getClipBounds() const - { - return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds()) - : Rectangle(); - } - - SavedState* beginTransparencyLayer (float opacity) - { - SavedState* s = new SavedState (*this); - - if (clip != nullptr) - { - const Rectangle layerBounds (clip->getClipBounds()); - - s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true); - s->transparencyLayerAlpha = opacity; - s->transform.moveOriginInDeviceSpace (-layerBounds.getX(), -layerBounds.getY()); - - s->cloneClipIfMultiplyReferenced(); - s->clip->translate (-layerBounds.getPosition()); - } - - return s; - } - - void endTransparencyLayer (SavedState& finishedLayerState) - { - if (clip != nullptr) - { - const Rectangle layerBounds (clip->getClipBounds()); - - const ScopedPointer g (image.createLowLevelContext()); - g->setOpacity (finishedLayerState.transparencyLayerAlpha); - g->drawImage (finishedLayerState.image, AffineTransform::translation ((float) layerBounds.getX(), - (float) layerBounds.getY())); - } - } - - //============================================================================== - void fillRect (const Rectangle& r, const bool replaceContents) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - { - if (fillType.isColour()) - { - Image::BitmapData destData (image, Image::BitmapData::readWrite); - clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB(), replaceContents); - } - else - { - const Rectangle totalClip (clip->getClipBounds()); - const Rectangle clipped (totalClip.getIntersection (transform.translated (r))); - - if (! clipped.isEmpty()) - fillShape (new SoftwareRendererClasses::ClipRegion_RectangleList (clipped), false); - } - } - else - { - Path p; - p.addRectangle (r); - fillPath (p, AffineTransform::identity); - } - } - } - - void fillRect (const Rectangle& r) - { - if (clip != nullptr) - { - if (transform.isOnlyTranslated) - { - if (fillType.isColour()) - { - Image::BitmapData destData (image, Image::BitmapData::readWrite); - clip->fillRectWithColour (destData, transform.translated (r), fillType.colour.getPixelARGB()); - } - else - { - const Rectangle totalClip (clip->getClipBounds().toFloat()); - const Rectangle clipped (totalClip.getIntersection (transform.translated (r))); - - if (! clipped.isEmpty()) - fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clipped), false); - } - } - else - { - Path p; - p.addRectangle (r); - fillPath (p, AffineTransform::identity); - } - } - } - - void fillPath (const Path& path, const AffineTransform& t) - { - if (clip != nullptr) - fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (clip->getClipBounds(), path, transform.getTransformWith (t)), false); - } - - void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y) - { - jassert (transform.isOnlyTranslated); - - if (clip != nullptr) - { - SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (edgeTable); - edgeTableClip->edgeTable.translate (x + transform.xOffset, - y + transform.yOffset); - fillShape (edgeTableClip, false); - } - } - - void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& t) - { - if (clip != nullptr) - { - const ScopedPointer et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, transform.getTransformWith (t))); - - if (et != nullptr) - fillShape (new SoftwareRendererClasses::ClipRegion_EdgeTable (*et), false); - } - } - - void fillShape (SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents) - { - jassert (clip != nullptr); - - shapeToFill = clip->applyClipTo (shapeToFill); - - if (shapeToFill != nullptr) - { - Image::BitmapData destData (image, Image::BitmapData::readWrite); - - if (fillType.isGradient()) - { - jassert (! replaceContents); // that option is just for solid colours - - ColourGradient g2 (*(fillType.gradient)); - g2.multiplyOpacity (fillType.getOpacity()); - AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f)); - - const bool isIdentity = t.isOnlyTranslation(); - - if (isIdentity) - { - // If our translation doesn't involve any distortion, we can speed it up.. - g2.point1.applyTransform (t); - g2.point2.applyTransform (t); - t = AffineTransform::identity; - } - - shapeToFill->fillAllWithGradient (destData, g2, t, isIdentity); - } - else if (fillType.isTiledImage()) - { - renderImage (fillType.image, fillType.transform, shapeToFill); - } - else - { - shapeToFill->fillAllWithColour (destData, fillType.colour.getPixelARGB(), replaceContents); - } - } - } - - //============================================================================== - void renderImage (const Image& sourceImage, const AffineTransform& trans, - const SoftwareRendererClasses::ClipRegionBase* const tiledFillClipRegion) - { - const AffineTransform t (transform.getTransformWith (trans)); - - const Image::BitmapData destData (image, Image::BitmapData::readWrite); - const Image::BitmapData srcData (sourceImage, Image::BitmapData::readOnly); - const int alpha = fillType.colour.getAlpha(); - const bool betterQuality = (interpolationQuality != Graphics::lowResamplingQuality); - - if (t.isOnlyTranslation()) - { - // If our translation doesn't involve any distortion, just use a simple blit.. - int tx = (int) (t.getTranslationX() * 256.0f); - int ty = (int) (t.getTranslationY() * 256.0f); - - if ((! betterQuality) || ((tx | ty) & 224) == 0) - { - tx = ((tx + 128) >> 8); - ty = ((ty + 128) >> 8); - - if (tiledFillClipRegion != nullptr) - { - tiledFillClipRegion->renderImageUntransformed (destData, srcData, alpha, tx, ty, true); - } - else - { - Rectangle area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()); - area = area.getIntersection (image.getBounds()); - - if (! area.isEmpty()) - { - SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->applyClipTo (new SoftwareRendererClasses::ClipRegion_EdgeTable (area))); - - if (c != nullptr) - c->renderImageUntransformed (destData, srcData, alpha, tx, ty, false); - } - } - - return; - } - } - - if (t.isSingularity()) - return; - - if (tiledFillClipRegion != nullptr) - { - tiledFillClipRegion->renderImageTransformed (destData, srcData, alpha, t, betterQuality, true); - } - else - { - Path p; - p.addRectangle (sourceImage.getBounds()); - - SoftwareRendererClasses::ClipRegionBase::Ptr c (clip->clone()); - c = c->clipToPath (p, t); - - if (c != nullptr) - c->renderImageTransformed (destData, srcData, alpha, t, betterQuality, false); - } - } - - //============================================================================== - Image image; - SoftwareRendererClasses::ClipRegionBase::Ptr clip; - RenderingHelpers::TranslationOrTransform transform; - Font font; - FillType fillType; - Graphics::ResamplingQuality interpolationQuality; - -private: - float transparencyLayerAlpha; - - void cloneClipIfMultiplyReferenced() - { - if (clip->getReferenceCount() > 1) - clip = clip->clone(); - } - - SavedState& operator= (const SavedState&); -}; - - -//============================================================================== LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image) - : savedState (new SavedState (image, image.getBounds())) + : RenderingHelpers::StackBasedLowLevelGraphicsContext + (new RenderingHelpers::SoftwareRendererSavedState (image, image.getBounds())) { } -LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, const Point& origin, - const RectangleList& initialClip) - : savedState (new SavedState (image, initialClip, origin.x, origin.y)) +LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, Point origin, + const RectangleList& initialClip) + : RenderingHelpers::StackBasedLowLevelGraphicsContext + (new RenderingHelpers::SoftwareRendererSavedState (image, initialClip, origin)) { } -LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() -{ -} - -bool LowLevelGraphicsSoftwareRenderer::isVectorDevice() const -{ - return false; -} - -//============================================================================== -void LowLevelGraphicsSoftwareRenderer::setOrigin (int x, int y) -{ - savedState->transform.setOrigin (x, y); -} - -void LowLevelGraphicsSoftwareRenderer::addTransform (const AffineTransform& transform) -{ - savedState->transform.addTransform (transform); -} - -float LowLevelGraphicsSoftwareRenderer::getScaleFactor() -{ - return savedState->transform.getScaleFactor(); -} - -bool LowLevelGraphicsSoftwareRenderer::clipToRectangle (const Rectangle& r) -{ - return savedState->clipToRectangle (r); -} - -bool LowLevelGraphicsSoftwareRenderer::clipToRectangleList (const RectangleList& clipRegion) -{ - return savedState->clipToRectangleList (clipRegion); -} - -void LowLevelGraphicsSoftwareRenderer::excludeClipRectangle (const Rectangle& r) -{ - savedState->excludeClipRectangle (r); -} - -void LowLevelGraphicsSoftwareRenderer::clipToPath (const Path& path, const AffineTransform& transform) -{ - savedState->clipToPath (path, transform); -} - -void LowLevelGraphicsSoftwareRenderer::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) -{ - savedState->clipToImageAlpha (sourceImage, transform); -} - -bool LowLevelGraphicsSoftwareRenderer::clipRegionIntersects (const Rectangle& r) -{ - return savedState->clipRegionIntersects (r); -} - -Rectangle LowLevelGraphicsSoftwareRenderer::getClipBounds() const -{ - return savedState->getClipBounds(); -} - -bool LowLevelGraphicsSoftwareRenderer::isClipEmpty() const -{ - return savedState->clip == nullptr; -} - -//============================================================================== -void LowLevelGraphicsSoftwareRenderer::saveState() { savedState.save(); } -void LowLevelGraphicsSoftwareRenderer::restoreState() { savedState.restore(); } - -void LowLevelGraphicsSoftwareRenderer::beginTransparencyLayer (float opacity) { savedState.beginTransparencyLayer (opacity); } -void LowLevelGraphicsSoftwareRenderer::endTransparencyLayer() { savedState.endTransparencyLayer(); } - -//============================================================================== -void LowLevelGraphicsSoftwareRenderer::setFill (const FillType& fillType) -{ - savedState->fillType = fillType; -} - -void LowLevelGraphicsSoftwareRenderer::setOpacity (float newOpacity) -{ - savedState->fillType.setOpacity (newOpacity); -} - -void LowLevelGraphicsSoftwareRenderer::setInterpolationQuality (Graphics::ResamplingQuality quality) -{ - savedState->interpolationQuality = quality; -} - -//============================================================================== -void LowLevelGraphicsSoftwareRenderer::fillRect (const Rectangle& r, const bool replaceExistingContents) -{ - savedState->fillRect (r, replaceExistingContents); -} - -void LowLevelGraphicsSoftwareRenderer::fillPath (const Path& path, const AffineTransform& transform) -{ - savedState->fillPath (path, transform); -} - -void LowLevelGraphicsSoftwareRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform) -{ - savedState->renderImage (sourceImage, transform, nullptr); -} - -void LowLevelGraphicsSoftwareRenderer::drawLine (const Line & line) -{ - Path p; - p.addLineSegment (line, 1.0f); - fillPath (p, AffineTransform::identity); -} - -void LowLevelGraphicsSoftwareRenderer::drawVerticalLine (const int x, float top, float bottom) -{ - if (bottom > top) - savedState->fillRect (Rectangle ((float) x, top, 1.0f, bottom - top)); -} - -void LowLevelGraphicsSoftwareRenderer::drawHorizontalLine (const int y, float left, float right) -{ - if (right > left) - savedState->fillRect (Rectangle (left, (float) y, right - left, 1.0f)); -} - -void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform) -{ - Font& f = savedState->font; - - if (transform.isOnlyTranslation() && savedState->transform.isOnlyTranslated) - { - RenderingHelpers::GlyphCache , SavedState>::getInstance() - .drawGlyph (*savedState, f, glyphNumber, - transform.getTranslationX(), - transform.getTranslationY()); - } - else - { - const float fontHeight = f.getHeight(); - savedState->drawGlyph (f, glyphNumber, - AffineTransform::scale (fontHeight * f.getHorizontalScale(), fontHeight) - .followedBy (transform)); - } -} - -void LowLevelGraphicsSoftwareRenderer::setFont (const Font& newFont) { savedState->font = newFont; } -const Font& LowLevelGraphicsSoftwareRenderer::getFont() { return savedState->font; } - -#if JUCE_MSVC - #pragma warning (pop) - - #if JUCE_DEBUG - #pragma optimize ("", on) // resets optimisations to the project defaults - #endif -#endif +LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() {} diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h index 94e004a..9928092 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h @@ -1,36 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ -#define __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ +#ifndef JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_H_INCLUDED +#define JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_H_INCLUDED -#include "juce_LowLevelGraphicsContext.h" - -#ifndef DOXYGEN -#include "../native/juce_RenderingHelpers.h" -#endif //============================================================================== /** @@ -40,61 +34,23 @@ 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 +class JUCE_API LowLevelGraphicsSoftwareRenderer : public RenderingHelpers::StackBasedLowLevelGraphicsContext { public: //============================================================================== - LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOn); - LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOn, const Point& origin, const RectangleList& initialClip); + /** Creates a context to render into an image. */ + LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOnto); + + /** Creates a context to render into a clipped subsection of an image. */ + LowLevelGraphicsSoftwareRenderer (const Image& imageToRenderOnto, Point origin, + const RectangleList& initialClip); + + /** Destructor. */ ~LowLevelGraphicsSoftwareRenderer(); - bool isVectorDevice() const; - void setOrigin (int x, int y); - void addTransform (const AffineTransform&); - float getScaleFactor(); - bool clipToRectangle (const Rectangle&); - bool clipToRectangleList (const RectangleList&); - void excludeClipRectangle (const Rectangle&); - void clipToPath (const Path&, const AffineTransform&); - void clipToImageAlpha (const Image&, const AffineTransform&); - bool clipRegionIntersects (const Rectangle&); - Rectangle getClipBounds() const; - bool isClipEmpty() const; - - void saveState(); - void restoreState(); - - void beginTransparencyLayer (float opacity); - void endTransparencyLayer(); - - void setFill (const FillType&); - void setOpacity (float opacity); - void setInterpolationQuality (Graphics::ResamplingQuality); - - void fillRect (const Rectangle&, bool replaceExistingContents); - void fillPath (const Path&, const AffineTransform&); - - void drawImage (const Image&, const AffineTransform&); - - void drawLine (const Line & line); - - void drawVerticalLine (int x, float top, float bottom); - void drawHorizontalLine (int x, float top, float bottom); - - void setFont (const Font&); - const Font& getFont(); - void drawGlyph (int glyphNumber, float x, float y); - void drawGlyph (int glyphNumber, const AffineTransform&); - - #ifndef DOXYGEN - class SavedState; - #endif - -protected: - RenderingHelpers::SavedStateStack savedState; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsSoftwareRenderer); +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsSoftwareRenderer) }; -#endif // __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ +#endif // JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.cpp b/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.cpp index 8a69403..448fab8 100644 --- a/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.cpp +++ b/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.cpp @@ -1,107 +1,182 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if JUCE_MSVC && JUCE_DEBUG - #pragma optimize ("t", on) -#endif +static inline void blurDataTriplets (uint8* d, int num, const int delta) noexcept +{ + uint32 last = d[0]; + d[0] = (uint8) ((d[0] + d[delta] + 1) / 3); + d += delta; + + num -= 2; + + do + { + const uint32 newLast = d[0]; + d[0] = (uint8) ((last + d[0] + d[delta] + 1) / 3); + d += delta; + last = newLast; + } + while (--num > 0); + + d[0] = (uint8) ((last + d[0] + 1) / 3); +} + +static void blurSingleChannelImage (uint8* const data, const int width, const int height, + const int lineStride, const int repetitions) noexcept +{ + jassert (width > 2 && height > 2); + + for (int y = 0; y < height; ++y) + for (int i = repetitions; --i >= 0;) + blurDataTriplets (data + lineStride * y, width, 1); + + for (int x = 0; x < width; ++x) + for (int i = repetitions; --i >= 0;) + blurDataTriplets (data + x, height, lineStride); +} + +static void blurSingleChannelImage (Image& image, int radius) +{ + const Image::BitmapData bm (image, Image::BitmapData::readWrite); + blurSingleChannelImage (bm.data, bm.width, bm.height, bm.lineStride, 2 * radius); +} //============================================================================== -DropShadowEffect::DropShadowEffect() - : offsetX (0), - offsetY (0), - radius (4), - opacity (0.6f) +DropShadow::DropShadow() noexcept + : colour (0x90000000), radius (4) { } -DropShadowEffect::~DropShadowEffect() +DropShadow::DropShadow (Colour shadowColour, const int r, Point o) noexcept + : colour (shadowColour), radius (r), offset (o) { + jassert (radius > 0); } -void DropShadowEffect::setShadowProperties (const float newRadius, - const float newOpacity, - const int newShadowOffsetX, - const int newShadowOffsetY) +void DropShadow::drawForImage (Graphics& g, const Image& srcImage) const { - radius = jmax (1.1f, newRadius); - offsetX = newShadowOffsetX; - offsetY = newShadowOffsetY; - opacity = newOpacity; -} - -void DropShadowEffect::applyEffect (Image& image, Graphics& g, float alpha) -{ - const int w = image.getWidth(); - const int h = image.getHeight(); - - Image shadowImage (Image::SingleChannel, w, h, false); + jassert (radius > 0); + if (srcImage.isValid()) { - const Image::BitmapData srcData (image, Image::BitmapData::readOnly); - const Image::BitmapData destData (shadowImage, Image::BitmapData::readWrite); + Image shadowImage (srcImage.convertedToFormat (Image::SingleChannel)); + shadowImage.duplicateIfShared(); - const int filter = roundToInt (63.0f / radius); - const int radiusMinus1 = roundToInt ((radius - 1.0f) * 63.0f); + blurSingleChannelImage (shadowImage, radius); - for (int x = w; --x >= 0;) - { - int shadowAlpha = 0; - - const PixelARGB* src = ((const PixelARGB*) srcData.data) + x; - uint8* shadowPix = destData.data + x; - - for (int y = h; --y >= 0;) - { - shadowAlpha = ((shadowAlpha * radiusMinus1 + (src->getAlpha() << 6)) * filter) >> 12; - - *shadowPix = (uint8) shadowAlpha; - src = addBytesToPointer (src, srcData.lineStride); - shadowPix += destData.lineStride; - } - } - - for (int y = h; --y >= 0;) - { - int shadowAlpha = 0; - uint8* shadowPix = destData.getLinePointer (y); - - for (int x = w; --x >= 0;) - { - shadowAlpha = ((shadowAlpha * radiusMinus1 + (*shadowPix << 6)) * filter) >> 12; - *shadowPix++ = (uint8) shadowAlpha; - } - } + g.setColour (colour); + g.drawImageAt (shadowImage, offset.x, offset.y, true); } +} - g.setColour (Colours::black.withAlpha (opacity * alpha)); - g.drawImageAt (shadowImage, offsetX, offsetY, true); +void DropShadow::drawForPath (Graphics& g, const Path& path) const +{ + jassert (radius > 0); + + const Rectangle area ((path.getBounds().getSmallestIntegerContainer() + offset) + .expanded (radius + 1) + .getIntersection (g.getClipBounds().expanded (radius + 1))); + + if (area.getWidth() > 2 && area.getHeight() > 2) + { + Image renderedPath (Image::SingleChannel, area.getWidth(), area.getHeight(), true); + + { + Graphics g2 (renderedPath); + g2.setColour (Colours::white); + g2.fillPath (path, AffineTransform::translation ((float) (offset.x - area.getX()), + (float) (offset.y - area.getY()))); + } + + blurSingleChannelImage (renderedPath, radius); + + g.setColour (colour); + g.drawImageAt (renderedPath, area.getX(), area.getY(), true); + } +} + +static void drawShadowSection (Graphics& g, ColourGradient& cg, const Rectangle& area, + bool isCorner, float centreX, float centreY, float edgeX, float edgeY) +{ + cg.point1 = area.getRelativePoint (centreX, centreY).toFloat(); + cg.point2 = area.getRelativePoint (edgeX, edgeY).toFloat(); + cg.isRadial = isCorner; + + g.setGradientFill (cg); + g.fillRect (area); +} + +void DropShadow::drawForRectangle (Graphics& g, const Rectangle& targetArea) const +{ + ColourGradient cg (colour, 0, 0, colour.withAlpha (0.0f), 0, 0, false); + + for (float i = 0.05f; i < 1.0f; i += 0.1f) + cg.addColour (1.0 - i, colour.withMultipliedAlpha (i * i)); + + const int radiusInset = (radius + 1) / 2; + const int expandedRadius = radius + radiusInset; + + const Rectangle area (targetArea.reduced (radiusInset) + offset); + + Rectangle r (area.expanded (expandedRadius)); + Rectangle top (r.removeFromTop (expandedRadius)); + Rectangle bottom (r.removeFromBottom (expandedRadius)); + + drawShadowSection (g, cg, top.removeFromLeft (expandedRadius), true, 1.0f, 1.0f, 0, 1.0f); + drawShadowSection (g, cg, top.removeFromRight (expandedRadius), true, 0, 1.0f, 1.0f, 1.0f); + drawShadowSection (g, cg, top, false, 0, 1.0f, 0, 0); + + drawShadowSection (g, cg, bottom.removeFromLeft (expandedRadius), true, 1.0f, 0, 0, 0); + drawShadowSection (g, cg, bottom.removeFromRight (expandedRadius), true, 0, 0, 1.0f, 0); + drawShadowSection (g, cg, bottom, false, 0, 0, 0, 1.0f); + + drawShadowSection (g, cg, r.removeFromLeft (expandedRadius), false, 1.0f, 0, 0, 0); + drawShadowSection (g, cg, r.removeFromRight (expandedRadius), false, 0, 0, 1.0f, 0); + + g.setColour (colour); + g.fillRect (area); +} + +//============================================================================== +DropShadowEffect::DropShadowEffect() {} +DropShadowEffect::~DropShadowEffect() {} + +void DropShadowEffect::setShadowProperties (const DropShadow& newShadow) +{ + shadow = newShadow; +} + +void DropShadowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) +{ + DropShadow s (shadow); + s.radius = roundToInt (s.radius * scaleFactor); + s.colour = s.colour.withMultipliedAlpha (alpha); + s.offset.x = roundToInt (s.offset.x * scaleFactor); + s.offset.y = roundToInt (s.offset.y * scaleFactor); + + s.drawForImage (g, image); g.setOpacity (alpha); g.drawImageAt (image, 0, 0); } - -#if JUCE_MSVC && JUCE_DEBUG - #pragma optimize ("", on) // resets optimisations to the project defaults -#endif diff --git a/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.h b/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.h index 2fdc706..1f9362a 100644 --- a/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.h +++ b/JuceLibraryCode/modules/juce_graphics/effects/juce_DropShadowEffect.h @@ -1,33 +1,66 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ -#define __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ +#ifndef JUCE_DROPSHADOWEFFECT_H_INCLUDED +#define JUCE_DROPSHADOWEFFECT_H_INCLUDED -#include "juce_ImageEffectFilter.h" +//============================================================================== +/** + Defines a drop-shadow effect. +*/ +struct JUCE_API DropShadow +{ + /** Creates a default drop-shadow effect. */ + DropShadow() noexcept; + + /** Creates a drop-shadow object with the given parameters. */ + DropShadow (Colour shadowColour, int radius, Point offset) noexcept; + + /** Renders a drop-shadow based on the alpha-channel of the given image. */ + void drawForImage (Graphics& g, const Image& srcImage) const; + + /** Renders a drop-shadow based on the shape of a path. */ + void drawForPath (Graphics& g, const Path& path) const; + + /** Renders a drop-shadow for a rectangle. + Note that for speed, this approximates the shadow using gradients. + */ + void drawForRectangle (Graphics& g, const Rectangle& area) const; + + /** The colour with which to render the shadow. + In most cases you'll probably want to leave this as black with an alpha + value of around 0.5 + */ + Colour colour; + + /** The approximate spread of the shadow. */ + int radius; + + /** The offset of the shadow. */ + Point offset; +}; //============================================================================== /** @@ -50,9 +83,7 @@ class JUCE_API DropShadowEffect : public ImageEffectFilter public: //============================================================================== /** Creates a default drop-shadow effect. - - To customise the shadow's appearance, use the setShadowProperties() - method. + To customise the shadow's appearance, use the setShadowProperties() method. */ DropShadowEffect(); @@ -60,33 +91,20 @@ public: ~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); - + /** Sets up parameters affecting the shadow's appearance. */ + void setShadowProperties (const DropShadow& newShadow); //============================================================================== /** @internal */ - void applyEffect (Image& sourceImage, Graphics& destContext, float alpha); + void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha); private: //============================================================================== - int offsetX, offsetY; - float radius, opacity; + DropShadow shadow; - JUCE_LEAK_DETECTOR (DropShadowEffect); + JUCE_LEAK_DETECTOR (DropShadowEffect) }; -#endif // __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ +#endif // JUCE_DROPSHADOWEFFECT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.cpp b/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.cpp index ef23889..7db7ef5 100644 --- a/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.cpp +++ b/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -34,17 +33,17 @@ GlowEffect::~GlowEffect() } void GlowEffect::setGlowProperties (const float newRadius, - const Colour& newColour) + Colour newColour) { radius = newRadius; colour = newColour; } -void GlowEffect::applyEffect (Image& image, Graphics& g, float alpha) +void GlowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha) { Image temp (image.getFormat(), image.getWidth(), image.getHeight(), true); - ImageConvolutionKernel blurKernel (roundToInt (radius * 2.0f)); + ImageConvolutionKernel blurKernel (roundToInt (radius * scaleFactor * 2.0f)); blurKernel.createGaussianBlur (radius); blurKernel.rescaleAllValues (radius); diff --git a/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.h b/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.h index e843c05..ab9ce3b 100644 --- a/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.h +++ b/JuceLibraryCode/modules/juce_graphics/effects/juce_GlowEffect.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_GLOWEFFECT_JUCEHEADER__ -#define __JUCE_GLOWEFFECT_JUCEHEADER__ - -#include "juce_ImageEffectFilter.h" +#ifndef JUCE_GLOWEFFECT_H_INCLUDED +#define JUCE_GLOWEFFECT_H_INCLUDED //============================================================================== @@ -58,20 +55,20 @@ public: opacity). */ void setGlowProperties (float newRadius, - const Colour& newColour); + Colour newColour); //============================================================================== /** @internal */ - void applyEffect (Image& sourceImage, Graphics& destContext, float alpha); + void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha); private: //============================================================================== float radius; Colour colour; - JUCE_LEAK_DETECTOR (GlowEffect); + JUCE_LEAK_DETECTOR (GlowEffect) }; -#endif // __JUCE_GLOWEFFECT_JUCEHEADER__ +#endif // JUCE_GLOWEFFECT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/effects/juce_ImageEffectFilter.h b/JuceLibraryCode/modules/juce_graphics/effects/juce_ImageEffectFilter.h index 66b8994..84d6b0b 100644 --- a/JuceLibraryCode/modules/juce_graphics/effects/juce_ImageEffectFilter.h +++ b/JuceLibraryCode/modules/juce_graphics/effects/juce_ImageEffectFilter.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ -#define __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ - -#include "../contexts/juce_GraphicsContext.h" +#ifndef JUCE_IMAGEEFFECTFILTER_H_INCLUDED +#define JUCE_IMAGEEFFECTFILTER_H_INCLUDED //============================================================================== @@ -53,11 +50,15 @@ public: 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. + @param scaleFactor a scale factor that has been applied to the image - e.g. if + this is 2, then the image is actually scaled-up to twice the + original resolution @param alpha the alpha with which to draw the resultant image to the target context */ virtual void applyEffect (Image& sourceImage, Graphics& destContext, + float scaleFactor, float alpha) = 0; /** Destructor. */ @@ -65,4 +66,4 @@ public: }; -#endif // __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ +#endif // JUCE_IMAGEEFFECTFILTER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp index a84e237..43b6318 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp @@ -1,34 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -AttributedString::Attribute::Attribute (const Range& range_, const Colour& colour_) +AttributedString::Attribute::Attribute (Range range_, Colour colour_) : range (range_), colour (new Colour (colour_)) { } -AttributedString::Attribute::Attribute (const Range& range_, const Font& font_) +AttributedString::Attribute::Attribute (Range range_, const Font& font_) : range (range_), font (new Font (font_)) { } @@ -40,6 +39,13 @@ AttributedString::Attribute::Attribute (const Attribute& other) { } +AttributedString::Attribute::Attribute (const Attribute& other, const int offset) + : range (other.range + offset), + font (other.font.createCopy()), + colour (other.colour.createCopy()) +{ +} + AttributedString::Attribute::~Attribute() {} //============================================================================== @@ -130,7 +136,7 @@ void AttributedString::append (const String& textToAppend, const Font& font) setFont (Range (oldLength, oldLength + newLength), font); } -void AttributedString::append (const String& textToAppend, const Colour& colour) +void AttributedString::append (const String& textToAppend, Colour colour) { const int oldLength = text.length(); const int newLength = textToAppend.length(); @@ -139,7 +145,7 @@ void AttributedString::append (const String& textToAppend, const Colour& colour) setColour (Range (oldLength, oldLength + newLength), colour); } -void AttributedString::append (const String& textToAppend, const Font& font, const Colour& colour) +void AttributedString::append (const String& textToAppend, const Font& font, Colour colour) { const int oldLength = text.length(); const int newLength = textToAppend.length(); @@ -149,13 +155,22 @@ void AttributedString::append (const String& textToAppend, const Font& font, con setColour (Range (oldLength, oldLength + newLength), colour); } +void AttributedString::append (const AttributedString& other) +{ + const int originalLength = text.length(); + text += other.text; + + for (int i = 0; i < other.attributes.size(); ++i) + attributes.add (new Attribute (*other.attributes.getUnchecked(i), originalLength)); +} + void AttributedString::clear() { - text = String::empty; + text.clear(); attributes.clear(); } -void AttributedString::setJustification (const Justification& newJustification) noexcept +void AttributedString::setJustification (Justification newJustification) noexcept { justification = newJustification; } @@ -175,12 +190,12 @@ void AttributedString::setLineSpacing (const float newLineSpacing) noexcept lineSpacing = newLineSpacing; } -void AttributedString::setColour (const Range& range, const Colour& colour) +void AttributedString::setColour (Range range, Colour colour) { attributes.add (new Attribute (range, colour)); } -void AttributedString::setColour (const Colour& colour) +void AttributedString::setColour (Colour colour) { for (int i = attributes.size(); --i >= 0;) if (attributes.getUnchecked(i)->getColour() != nullptr) @@ -189,7 +204,7 @@ void AttributedString::setColour (const Colour& colour) setColour (Range (0, text.length()), colour); } -void AttributedString::setFont (const Range& range, const Font& font) +void AttributedString::setFont (Range range, const Font& font) { attributes.add (new Attribute (range, font)); } @@ -207,7 +222,7 @@ void AttributedString::draw (Graphics& g, const Rectangle& area) const { if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer())) { - if (! g.getInternalContext()->drawTextLayout (*this, area)) + if (! g.getInternalContext().drawTextLayout (*this, area)) { TextLayout layout; layout.createLayout (*this, area.getWidth()); diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.h b/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.h index f2c289b..8c33d75 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.h +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_ATTRIBUTEDSTRING_JUCEHEADER__ -#define __JUCE_ATTRIBUTEDSTRING_JUCEHEADER__ +#ifndef JUCE_ATTRIBUTEDSTRING_H_INCLUDED +#define JUCE_ATTRIBUTEDSTRING_H_INCLUDED //============================================================================== @@ -71,9 +70,15 @@ public: /** Appends some text, with a specified font, and the default colour (black). */ void append (const String& textToAppend, const Font& font); /** Appends some text, with a specified colour, and the default font. */ - void append (const String& textToAppend, const Colour& colour); + void append (const String& textToAppend, Colour colour); /** Appends some text, with a specified font and colour. */ - void append (const String& textToAppend, const Font& font, const Colour& colour); + void append (const String& textToAppend, const Font& font, Colour colour); + + /** Appends another AttributedString to this one. + Note that this will only append the text, fonts, and colours - it won't copy any + other properties such as justification, line-spacing, etc from the other object. + */ + void append (const AttributedString& other); /** Resets the string, clearing all text and attributes. Note that this won't affect global settings like the justification type, @@ -97,7 +102,7 @@ public: /** Sets the justification that should be used for laying-out the text. This may include both vertical and horizontal flags. */ - void setJustification (const Justification& newJustification) noexcept; + void setJustification (Justification newJustification) noexcept; //============================================================================== /** Types of word-wrap behaviour. @@ -148,12 +153,12 @@ public: /** Creates an attribute that changes the colour for a range of characters. @see AttributedString::setColour() */ - Attribute (const Range& range, const Colour& colour); + Attribute (Range range, Colour colour); /** Creates an attribute that changes the font for a range of characters. @see AttributedString::setFont() */ - Attribute (const Range& range, const Font& font); + Attribute (Range range, const Font& font); Attribute (const Attribute&); ~Attribute(); @@ -171,9 +176,11 @@ public: ScopedPointer font; ScopedPointer colour; + friend class AttributedString; + Attribute (const Attribute&, int); Attribute& operator= (const Attribute&); - JUCE_LEAK_DETECTOR (Attribute); + JUCE_LEAK_DETECTOR (Attribute) }; /** Returns the number of attributes that have been added to this string. */ @@ -186,13 +193,13 @@ public: //============================================================================== /** Adds a colour attribute for the specified range. */ - void setColour (const Range& range, const Colour& colour); + void setColour (Range range, Colour colour); /** Removes all existing colour attributes, and applies this colour to the whole string. */ - void setColour (const Colour& colour); + void setColour (Colour colour); /** Adds a font attribute for the specified range. */ - void setFont (const Range& range, const Font& font); + void setFont (Range range, const Font& font); /** Removes all existing font attributes, and applies this font to the whole string. */ void setFont (const Font& font); @@ -205,7 +212,7 @@ private: ReadingDirection readingDirection; OwnedArray attributes; - JUCE_LEAK_DETECTOR (AttributedString); + JUCE_LEAK_DETECTOR (AttributedString) }; -#endif // __JUCE_ATTRIBUTEDSTRING_JUCEHEADER__ +#endif // JUCE_ATTRIBUTEDSTRING_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp index a9505f0..a2adb04 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,8 +25,8 @@ class CustomTypeface::GlyphInfo { public: - GlyphInfo (const juce_wchar character_, const Path& path_, const float width_) noexcept - : character (character_), path (path_), width (width_) + GlyphInfo (const juce_wchar c, const Path& p, const float w) noexcept + : character (c), path (p), width (w) { } @@ -49,11 +48,9 @@ public: float getHorizontalSpacing (const juce_wchar subsequentCharacter) const noexcept { if (subsequentCharacter != 0) - { for (int i = kerningPairs.size(); --i >= 0;) if (kerningPairs.getReference(i).character2 == subsequentCharacter) return width + kerningPairs.getReference(i).kerningAmount; - } return width; } @@ -64,7 +61,7 @@ public: Array kerningPairs; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphInfo); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphInfo) }; //============================================================================== @@ -102,13 +99,13 @@ namespace CustomTypefaceHelpers //============================================================================== CustomTypeface::CustomTypeface() - : Typeface (String::empty) + : Typeface (String(), String()) { clear(); } CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) - : Typeface (String::empty) + : Typeface (String(), String()) { clear(); @@ -116,14 +113,17 @@ CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) BufferedInputStream in (gzin, 32768); name = in.readString(); - isBold = in.readBool(); - isItalic = in.readBool(); + + const bool isBold = in.readBool(); + const bool isItalic = in.readBool(); + style = FontStyleHelpers::getStyleName (isBold, isItalic); + ascent = in.readFloat(); defaultCharacter = CustomTypefaceHelpers::readChar (in); - int i, numChars = in.readInt(); + int numChars = in.readInt(); - for (i = 0; i < numChars; ++i) + for (int i = 0; i < numChars; ++i) { const juce_wchar c = CustomTypefaceHelpers::readChar (in); const float width = in.readFloat(); @@ -135,7 +135,7 @@ CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) const int numKerningPairs = in.readInt(); - for (i = 0; i < numKerningPairs; ++i) + for (int i = 0; i < numKerningPairs; ++i) { const juce_wchar char1 = CustomTypefaceHelpers::readChar (in); const juce_wchar char2 = CustomTypefaceHelpers::readChar (in); @@ -153,19 +153,27 @@ void CustomTypeface::clear() { defaultCharacter = 0; ascent = 1.0f; - isBold = isItalic = false; + style = "Regular"; zeromem (lookupTable, sizeof (lookupTable)); glyphs.clear(); } -void CustomTypeface::setCharacteristics (const String& name_, const float ascent_, const bool isBold_, - const bool isItalic_, const juce_wchar defaultCharacter_) noexcept +void CustomTypeface::setCharacteristics (const String& newName, const float newAscent, const bool isBold, + const bool isItalic, const juce_wchar newDefaultCharacter) noexcept { - name = name_; - defaultCharacter = defaultCharacter_; - ascent = ascent_; - isBold = isBold_; - isItalic = isItalic_; + name = newName; + defaultCharacter = newDefaultCharacter; + ascent = newAscent; + style = FontStyleHelpers::getStyleName (isBold, isItalic); +} + +void CustomTypeface::setCharacteristics (const String& newName, const String& newStyle, const float newAscent, + const juce_wchar newDefaultCharacter) noexcept +{ + name = newName; + style = newStyle; + defaultCharacter = newDefaultCharacter; + ascent = newAscent; } void CustomTypeface::addGlyph (const juce_wchar character, const Path& path, const float width) noexcept @@ -183,11 +191,10 @@ void CustomTypeface::addKerningPair (const juce_wchar char1, const juce_wchar ch { if (extraAmount != 0) { - GlyphInfo* const g = findGlyph (char1, true); - jassert (g != nullptr); // can only add kerning pairs for characters that exist! - - if (g != nullptr) + if (GlyphInfo* const g = findGlyph (char1, true)) g->addKerningPair (char2, extraAmount); + else + jassertfalse; // can only add kerning pairs for characters that exist! } } @@ -216,7 +223,7 @@ bool CustomTypeface::loadGlyphIfPossible (const juce_wchar /*characterNeeded*/) void CustomTypeface::addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept { - setCharacteristics (name, typefaceToCopy.getAscent(), isBold, isItalic, defaultCharacter); + setCharacteristics (name, style, typefaceToCopy.getAscent(), defaultCharacter); for (int i = 0; i < numCharacters; ++i) { @@ -256,15 +263,15 @@ bool CustomTypeface::writeToStream (OutputStream& outputStream) GZIPCompressorOutputStream out (&outputStream); out.writeString (name); - out.writeBool (isBold); - out.writeBool (isItalic); + out.writeBool (FontStyleHelpers::isBold (style)); + out.writeBool (FontStyleHelpers::isItalic (style)); out.writeFloat (ascent); CustomTypefaceHelpers::writeChar (out, defaultCharacter); out.writeInt (glyphs.size()); - int i, numKerningPairs = 0; + int numKerningPairs = 0; - for (i = 0; i < glyphs.size(); ++i) + for (int i = 0; i < glyphs.size(); ++i) { const GlyphInfo* const g = glyphs.getUnchecked (i); CustomTypefaceHelpers::writeChar (out, g->character); @@ -276,7 +283,7 @@ bool CustomTypeface::writeToStream (OutputStream& outputStream) out.writeInt (numKerningPairs); - for (i = 0; i < glyphs.size(); ++i) + for (int i = 0; i < glyphs.size(); ++i) { const GlyphInfo* const g = glyphs.getUnchecked (i); @@ -293,36 +300,29 @@ bool CustomTypeface::writeToStream (OutputStream& outputStream) } //============================================================================== -float CustomTypeface::getAscent() const -{ - return ascent; -} - -float CustomTypeface::getDescent() const -{ - return 1.0f - ascent; -} +float CustomTypeface::getAscent() const { return ascent; } +float CustomTypeface::getDescent() const { return 1.0f - ascent; } +float CustomTypeface::getHeightToPointsFactor() const { return ascent; } float CustomTypeface::getStringWidth (const String& text) { float x = 0; - String::CharPointerType t (text.getCharPointer()); - while (! t.isEmpty()) + for (String::CharPointerType t (text.getCharPointer()); ! t.isEmpty();) { const juce_wchar c = t.getAndAdvance(); - const GlyphInfo* const glyph = findGlyph (c, true); - if (glyph == nullptr) + if (const GlyphInfo* const glyph = findGlyph (c, true)) + { + x += glyph->getHorizontalSpacing (*t); + } + else { const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) x += fallbackTypeface->getStringWidth (String::charToString (c)); } - - if (glyph != nullptr) - x += glyph->getHorizontalSpacing (*t); } return x; @@ -332,16 +332,22 @@ void CustomTypeface::getGlyphPositions (const String& text, Array & resultG { xOffsets.add (0); float x = 0; - String::CharPointerType t (text.getCharPointer()); - while (! t.isEmpty()) + for (String::CharPointerType t (text.getCharPointer()); ! t.isEmpty();) { - const juce_wchar c = t.getAndAdvance(); - const GlyphInfo* const glyph = findGlyph (c, true); + float width = 0.0f; + int glyphChar = 0; - if (glyph == nullptr) + const juce_wchar c = t.getAndAdvance(); + + if (const GlyphInfo* const glyph = findGlyph (c, true)) { - const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + width = glyph->getHorizontalSpacing (*t); + glyphChar = (int) glyph->character; + } + else + { + const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) { @@ -351,58 +357,50 @@ void CustomTypeface::getGlyphPositions (const String& text, Array & resultG if (subGlyphs.size() > 0) { - resultGlyphs.add (subGlyphs.getFirst()); - x += subOffsets[1]; - xOffsets.add (x); + glyphChar = subGlyphs.getFirst(); + width = subOffsets[1]; } } } - if (glyph != nullptr) - { - x += glyph->getHorizontalSpacing (*t); - resultGlyphs.add ((int) glyph->character); - xOffsets.add (x); - } + x += width; + resultGlyphs.add (glyphChar); + xOffsets.add (x); } } bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path) { - const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true); - - if (glyph == nullptr) - { - const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); - - if (fallbackTypeface != nullptr && fallbackTypeface != this) - fallbackTypeface->getOutlineForGlyph (glyphNumber, path); - } - - if (glyph != nullptr) + if (const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true)) { path = glyph->path; return true; } + const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); + + if (fallbackTypeface != nullptr && fallbackTypeface != this) + return fallbackTypeface->getOutlineForGlyph (glyphNumber, path); + return false; } -EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) +EdgeTable* CustomTypeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) { - const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true); - - if (glyph == nullptr) + if (const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true)) { - const Typeface::Ptr fallbackTypeface (Typeface::getFallbackTypeface()); + if (! glyph->path.isEmpty()) + return new EdgeTable (glyph->path.getBoundsTransformed (transform) + .getSmallestIntegerContainer().expanded (1, 0), + glyph->path, transform); + } + else + { + const Typeface::Ptr fallbackTypeface (getFallbackTypeface()); if (fallbackTypeface != nullptr && fallbackTypeface != this) - return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform); + return fallbackTypeface->getEdgeTableForGlyph (glyphNumber, transform, fontHeight); } - if (glyph != nullptr && ! glyph->path.isEmpty()) - return new EdgeTable (glyph->path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), - glyph->path, transform); - return nullptr; } diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.h b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.h index bd4d14a..25db68b 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.h +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.h @@ -1,34 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_CUSTOMTYPEFACE_JUCEHEADER__ -#define __JUCE_CUSTOMTYPEFACE_JUCEHEADER__ - -#include "juce_Typeface.h" -class InputStream; -class OutputStream; +#ifndef JUCE_CUSTOMTYPEFACE_H_INCLUDED +#define JUCE_CUSTOMTYPEFACE_H_INCLUDED //============================================================================== @@ -41,6 +36,11 @@ class OutputStream; If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface() to copy glyphs into this face. + NOTE! For most people this class is almost certainly NOT the right tool to use! + If what you want to do is to embed a font into your exe, then your best plan is + probably to embed your TTF/OTF font file into your binary using the Introjucer, + and then call Typeface::createSystemTypefaceFor() to load it from memory. + @see Typeface, Font */ class JUCE_API CustomTypeface : public Typeface @@ -52,6 +52,11 @@ public: /** Loads a typeface from a previously saved stream. The stream must have been created by writeToStream(). + + NOTE! Since this class was written, support was added for loading real font files from + memory, so for most people, using Typeface::createSystemTypefaceFor() to load a real font + is more appropriate than using this class to store it in a proprietary format. + @see writeToStream */ explicit CustomTypeface (InputStream& serialisedTypefaceStream); @@ -64,19 +69,31 @@ public: 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 + @param fontFamily the typeface's font family + @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, + void setCharacteristics (const String& fontFamily, float ascent, bool isBold, bool isItalic, juce_wchar defaultCharacter) noexcept; + /** Sets the vital statistics for the typeface. + @param fontFamily the typeface's font family + @param fontStyle the typeface's font style + @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 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& fontFamily, const String& fontStyle, + float ascent, juce_wchar defaultCharacter) noexcept; + /** Adds a glyph to the typeface. The path that is passed in is normalised so that the font height is 1.0, and its @@ -101,23 +118,27 @@ public: /** 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. + + NOTE! Since this class was written, support was added for loading real font files from + memory, so for most people, using Typeface::createSystemTypefaceFor() to load a real font + is more appropriate than using this class to store it in a proprietary format. */ bool writeToStream (OutputStream& outputStream); //============================================================================== // The following methods implement the basic Typeface behaviour. - float getAscent() const; - float getDescent() const; - float getStringWidth (const String& text); - void getGlyphPositions (const String& text, Array & glyphs, Array& xOffsets); - bool getOutlineForGlyph (int glyphNumber, Path& path); - EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform); + float getAscent() const override; + float getDescent() const override; + float getHeightToPointsFactor() const override; + float getStringWidth (const String&) override; + void getGlyphPositions (const String&, Array & glyphs, Array& xOffsets) override; + bool getOutlineForGlyph (int glyphNumber, Path&) override; + EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&, float fontHeight) override; protected: //============================================================================== juce_wchar defaultCharacter; float ascent; - bool isBold, isItalic; //============================================================================== /** If a subclass overrides this, it can load glyphs into the font on-demand. @@ -131,13 +152,13 @@ protected: private: //============================================================================== class GlyphInfo; - friend class OwnedArray; - OwnedArray glyphs; + friend struct ContainerDeletePolicy; + OwnedArray glyphs; short lookupTable [128]; GlyphInfo* findGlyph (const juce_wchar character, bool loadIfNeeded) noexcept; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTypeface); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTypeface) }; -#endif // __JUCE_CUSTOMTYPEFACE_JUCEHEADER__ +#endif // JUCE_CUSTOMTYPEFACE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp index d43636d..bc3f850 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -32,17 +31,17 @@ namespace FontValues const float defaultFontHeight = 14.0f; String fallbackFont; + String fallbackFontStyle; } typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&); GetTypefaceForFont juce_getTypefaceForFont = nullptr; //============================================================================== -class TypefaceCache : public DeletedAtShutdown +class TypefaceCache : private DeletedAtShutdown { public: - TypefaceCache() - : counter (0) + TypefaceCache() : counter (0) { setSize (10); } @@ -52,26 +51,40 @@ public: clearSingletonInstance(); } - juce_DeclareSingleton_SingleThreaded_Minimal (TypefaceCache); + juce_DeclareSingleton (TypefaceCache, false); void setSize (const int numToCache) { + const ScopedWriteLock sl (lock); + faces.clear(); faces.insertMultiple (-1, CachedFace(), numToCache); } + void clear() + { + const ScopedWriteLock sl (lock); + + setSize (faces.size()); + defaultFace = nullptr; + } + Typeface::Ptr findTypefaceFor (const Font& font) { - const int flags = font.getStyleFlags() & (Font::bold | Font::italic); - const String faceName (font.getTypefaceName()); + const ScopedReadLock slr (lock); - int i; - for (i = faces.size(); --i >= 0;) + const String faceName (font.getTypefaceName()); + const String faceStyle (font.getTypefaceStyle()); + + jassert (faceName.isNotEmpty()); + + for (int i = faces.size(); --i >= 0;) { CachedFace& face = faces.getReference(i); - if (face.flags == flags - && face.typefaceName == faceName + if (face.typefaceName == faceName + && face.typefaceStyle == faceStyle + && face.typeface != nullptr && face.typeface->isSuitableForFont (font)) { face.lastUsageCount = ++counter; @@ -79,10 +92,11 @@ public: } } + const ScopedWriteLock slw (lock); int replaceIndex = 0; - size_t bestLastUsageCount = std::numeric_limits::max(); + size_t bestLastUsageCount = std::numeric_limits::max(); - for (i = faces.size(); --i >= 0;) + for (int i = faces.size(); --i >= 0;) { const size_t lu = faces.getReference(i).lastUsageCount; @@ -95,7 +109,7 @@ public: CachedFace& face = faces.getReference (replaceIndex); face.typefaceName = faceName; - face.flags = flags; + face.typefaceStyle = faceStyle; face.lastUsageCount = ++counter; if (juce_getTypefaceForFont == nullptr) @@ -111,130 +125,148 @@ public: return face.typeface; } - Typeface::Ptr getDefaultTypeface() const noexcept - { - return defaultFace; - } + Typeface::Ptr defaultFace; private: struct CachedFace { - CachedFace() noexcept - : lastUsageCount (0), flags (-1) - { - } + CachedFace() noexcept : lastUsageCount (0) {} // Although it seems a bit wacky to store the name here, it's because it may be a // placeholder rather than a real one, e.g. "" vs the actual typeface name. // Since the typeface itself doesn't know that it may have this alias, the name under // which it was fetched needs to be stored separately. - String typefaceName; + String typefaceName, typefaceStyle; size_t lastUsageCount; - int flags; Typeface::Ptr typeface; }; - Array faces; - Typeface::Ptr defaultFace; + ReadWriteLock lock; + Array faces; size_t counter; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache) }; -juce_ImplementSingleton_SingleThreaded (TypefaceCache) +juce_ImplementSingleton (TypefaceCache) void Typeface::setTypefaceCacheSize (int numFontsToCache) { TypefaceCache::getInstance()->setSize (numFontsToCache); } +#if JUCE_MODULE_AVAILABLE_juce_opengl +extern void clearOpenGLGlyphCache(); +#endif + +void Typeface::clearTypefaceCache() +{ + TypefaceCache::getInstance()->clear(); + + RenderingHelpers::SoftwareRendererSavedState::clearGlyphCache(); + + #if JUCE_MODULE_AVAILABLE_juce_opengl + clearOpenGLGlyphCache(); + #endif +} + //============================================================================== -class Font::SharedFontInternal : public SingleThreadedReferenceCountedObject +class Font::SharedFontInternal : public ReferenceCountedObject { public: - SharedFontInternal (const float height_, const int styleFlags_) noexcept - : typefaceName (Font::getDefaultSansSerifFontName()), - height (height_), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (styleFlags_), - typeface ((styleFlags_ & (Font::bold | Font::italic)) == 0 - ? TypefaceCache::getInstance()->getDefaultTypeface() : nullptr) - { - } - - SharedFontInternal (const String& typefaceName_, const float height_, const int styleFlags_) noexcept - : typefaceName (typefaceName_), - height (height_), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (styleFlags_), - typeface (nullptr) - { - } - - SharedFontInternal (const Typeface::Ptr& typeface_) noexcept - : typefaceName (typeface_->getName()), + SharedFontInternal() noexcept + : typeface (TypefaceCache::getInstance()->defaultFace), + typefaceName (Font::getDefaultSansSerifFontName()), + typefaceStyle (Font::getDefaultStyle()), height (FontValues::defaultFontHeight), - horizontalScale (1.0f), - kerning (0), - ascent (0), - styleFlags (Font::plain), - typeface (typeface_) + horizontalScale (1.0f), kerning (0), ascent (0), underline (false) { } + SharedFontInternal (int styleFlags, float fontHeight) noexcept + : typefaceName (Font::getDefaultSansSerifFontName()), + typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)), + height (fontHeight), + horizontalScale (1.0f), kerning (0), ascent (0), underline ((styleFlags & underlined) != 0) + { + if (styleFlags == plain) + typeface = TypefaceCache::getInstance()->defaultFace; + } + + SharedFontInternal (const String& name, int styleFlags, float fontHeight) noexcept + : typefaceName (name), + typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)), + height (fontHeight), + horizontalScale (1.0f), kerning (0), ascent (0), underline ((styleFlags & underlined) != 0) + { + if (styleFlags == plain && typefaceName.isEmpty()) + typeface = TypefaceCache::getInstance()->defaultFace; + } + + SharedFontInternal (const String& name, const String& style, float fontHeight) noexcept + : typefaceName (name), typefaceStyle (style), height (fontHeight), + horizontalScale (1.0f), kerning (0), ascent (0), underline (false) + { + if (typefaceName.isEmpty()) + typefaceName = Font::getDefaultSansSerifFontName(); + } + + explicit SharedFontInternal (const Typeface::Ptr& face) noexcept + : typeface (face), + typefaceName (face->getName()), + typefaceStyle (face->getStyle()), + height (FontValues::defaultFontHeight), + horizontalScale (1.0f), kerning (0), ascent (0), underline (false) + { + jassert (typefaceName.isNotEmpty()); + } + SharedFontInternal (const SharedFontInternal& other) noexcept - : typefaceName (other.typefaceName), + : ReferenceCountedObject(), + typeface (other.typeface), + typefaceName (other.typefaceName), + typefaceStyle (other.typefaceStyle), height (other.height), horizontalScale (other.horizontalScale), kerning (other.kerning), ascent (other.ascent), - styleFlags (other.styleFlags), - typeface (other.typeface) + underline (other.underline) { } bool operator== (const SharedFontInternal& other) const noexcept { return height == other.height - && styleFlags == other.styleFlags + && underline == other.underline && horizontalScale == other.horizontalScale && kerning == other.kerning - && typefaceName == other.typefaceName; + && typefaceName == other.typefaceName + && typefaceStyle == other.typefaceStyle; } - String typefaceName; - float height, horizontalScale, kerning, ascent; - int styleFlags; Typeface::Ptr typeface; + String typefaceName, typefaceStyle; + float height, horizontalScale, kerning, ascent; + bool underline; }; //============================================================================== -Font::Font() - : font (new SharedFontInternal (FontValues::defaultFontHeight, Font::plain)) +Font::Font() : font (new SharedFontInternal()) {} +Font::Font (const Typeface::Ptr& typeface) : font (new SharedFontInternal (typeface)) {} +Font::Font (const Font& other) noexcept : font (other.font) {} + +Font::Font (float fontHeight, int styleFlags) + : font (new SharedFontInternal (styleFlags, FontValues::limitFontHeight (fontHeight))) { } -Font::Font (const float fontHeight, const int styleFlags) - : font (new SharedFontInternal (FontValues::limitFontHeight (fontHeight), styleFlags)) +Font::Font (const String& typefaceName, float fontHeight, int styleFlags) + : font (new SharedFontInternal (typefaceName, styleFlags, FontValues::limitFontHeight (fontHeight))) { } -Font::Font (const String& typefaceName, const float fontHeight, const int styleFlags) - : font (new SharedFontInternal (typefaceName, FontValues::limitFontHeight (fontHeight), styleFlags)) -{ -} - -Font::Font (const Typeface::Ptr& typeface) - : font (new SharedFontInternal (typeface)) -{ -} - -Font::Font (const Font& other) noexcept - : font (other.font) +Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight) + : font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight))) { } @@ -278,34 +310,54 @@ void Font::dupeInternalIfShared() font = new SharedFontInternal (*font); } +void Font::checkTypefaceSuitability() +{ + if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this)) + font->typeface = nullptr; +} + //============================================================================== -const String& Font::getDefaultSansSerifFontName() +struct FontPlaceholderNames { - static const String name (""); - return name; + FontPlaceholderNames() + : sans (""), + serif (""), + mono (""), + regular ("") + { + } + + String sans, serif, mono, regular; +}; + +const FontPlaceholderNames& getFontPlaceholderNames() +{ + static FontPlaceholderNames names; + return names; } -const String& Font::getDefaultSerifFontName() -{ - static const String name (""); - return name; -} +#if JUCE_MSVC +// This is a workaround for the lack of thread-safety in MSVC's handling of function-local +// statics - if multiple threads all try to create the first Font object at the same time, +// it can cause a race-condition in creating these placeholder strings. +struct FontNamePreloader { FontNamePreloader() { getFontPlaceholderNames(); } }; +static FontNamePreloader fnp; +#endif -const String& Font::getDefaultMonospacedFontName() -{ - static const String name (""); - return name; -} +const String& Font::getDefaultSansSerifFontName() { return getFontPlaceholderNames().sans; } +const String& Font::getDefaultSerifFontName() { return getFontPlaceholderNames().serif; } +const String& Font::getDefaultMonospacedFontName() { return getFontPlaceholderNames().mono; } +const String& Font::getDefaultStyle() { return getFontPlaceholderNames().regular; } -const String& Font::getTypefaceName() const noexcept -{ - return font->typefaceName; -} +const String& Font::getTypefaceName() const noexcept { return font->typefaceName; } +const String& Font::getTypefaceStyle() const noexcept { return font->typefaceStyle; } void Font::setTypefaceName (const String& faceName) { if (faceName != font->typefaceName) { + jassert (faceName.isNotEmpty()); + dupeInternalIfShared(); font->typefaceName = faceName; font->typeface = nullptr; @@ -313,10 +365,36 @@ void Font::setTypefaceName (const String& faceName) } } +void Font::setTypefaceStyle (const String& typefaceStyle) +{ + if (typefaceStyle != font->typefaceStyle) + { + dupeInternalIfShared(); + font->typefaceStyle = typefaceStyle; + font->typeface = nullptr; + font->ascent = 0; + } +} + +Font Font::withTypefaceStyle (const String& newStyle) const +{ + Font f (*this); + f.setTypefaceStyle (newStyle); + return f; +} + +StringArray Font::getAvailableStyles() const +{ + return findAllTypefaceStyles (getTypeface()->getName()); +} + Typeface* Font::getTypeface() const { if (font->typeface == nullptr) + { font->typeface = TypefaceCache::getInstance()->findTypefaceFor (*this); + jassert (font->typeface != nullptr); + } return font->typeface; } @@ -336,12 +414,21 @@ void Font::setFallbackFontName (const String& name) #endif } -//============================================================================== -float Font::getHeight() const noexcept +const String& Font::getFallbackFontStyle() { - return font->height; + return FontValues::fallbackFontStyle; } +void Font::setFallbackFontStyle (const String& style) +{ + FontValues::fallbackFontStyle = style; + + #if JUCE_MAC || JUCE_IOS + jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX.. + #endif +} + +//============================================================================== Font Font::withHeight (const float newHeight) const { Font f (*this); @@ -349,6 +436,18 @@ Font Font::withHeight (const float newHeight) const return f; } +float Font::getHeightToPointsFactor() const +{ + return getTypeface()->getHeightToPointsFactor(); +} + +Font Font::withPointHeight (float heightInPoints) const +{ + Font f (*this); + f.setHeight (heightInPoints / getHeightToPointsFactor()); + return f; +} + void Font::setHeight (float newHeight) { newHeight = FontValues::limitFontHeight (newHeight); @@ -357,6 +456,7 @@ void Font::setHeight (float newHeight) { dupeInternalIfShared(); font->height = newHeight; + checkTypefaceSuitability(); } } @@ -369,12 +469,18 @@ void Font::setHeightWithoutChangingWidth (float newHeight) dupeInternalIfShared(); font->horizontalScale *= (font->height / newHeight); font->height = newHeight; + checkTypefaceSuitability(); } } int Font::getStyleFlags() const noexcept { - return font->styleFlags; + int styleFlags = font->underline ? underlined : plain; + + if (isBold()) styleFlags |= bold; + if (isItalic()) styleFlags |= italic; + + return styleFlags; } Font Font::withStyle (const int newFlags) const @@ -386,11 +492,12 @@ Font Font::withStyle (const int newFlags) const void Font::setStyleFlags (const int newFlags) { - if (font->styleFlags != newFlags) + if (getStyleFlags() != newFlags) { dupeInternalIfShared(); - font->styleFlags = newFlags; font->typeface = nullptr; + font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags); + font->underline = (newFlags & underlined) != 0; font->ascent = 0; } } @@ -410,14 +517,31 @@ void Font::setSizeAndStyle (float newHeight, font->height = newHeight; font->horizontalScale = newHorizontalScale; font->kerning = newKerningAmount; + checkTypefaceSuitability(); } setStyleFlags (newStyleFlags); } -float Font::getHorizontalScale() const noexcept +void Font::setSizeAndStyle (float newHeight, + const String& newStyle, + const float newHorizontalScale, + const float newKerningAmount) { - return font->horizontalScale; + newHeight = FontValues::limitFontHeight (newHeight); + + if (font->height != newHeight + || font->horizontalScale != newHorizontalScale + || font->kerning != newKerningAmount) + { + dupeInternalIfShared(); + font->height = newHeight; + font->horizontalScale = newHorizontalScale; + font->kerning = newKerningAmount; + checkTypefaceSuitability(); + } + + setTypefaceStyle (newStyle); } Font Font::withHorizontalScale (const float newHorizontalScale) const @@ -431,6 +555,12 @@ void Font::setHorizontalScale (const float scaleFactor) { dupeInternalIfShared(); font->horizontalScale = scaleFactor; + checkTypefaceSuitability(); +} + +float Font::getHorizontalScale() const noexcept +{ + return font->horizontalScale; } float Font::getExtraKerningFactor() const noexcept @@ -449,35 +579,35 @@ void Font::setExtraKerningFactor (const float extraKerning) { dupeInternalIfShared(); font->kerning = extraKerning; + checkTypefaceSuitability(); } -Font Font::boldened() const { return withStyle (font->styleFlags | bold); } -Font Font::italicised() const { return withStyle (font->styleFlags | italic); } +Font Font::boldened() const { return withStyle (getStyleFlags() | bold); } +Font Font::italicised() const { return withStyle (getStyleFlags() | italic); } -bool Font::isBold() const noexcept { return (font->styleFlags & bold) != 0; } -bool Font::isItalic() const noexcept { return (font->styleFlags & italic) != 0; } +bool Font::isBold() const noexcept { return FontStyleHelpers::isBold (font->typefaceStyle); } +bool Font::isItalic() const noexcept { return FontStyleHelpers::isItalic (font->typefaceStyle); } +bool Font::isUnderlined() const noexcept { return font->underline; } void Font::setBold (const bool shouldBeBold) { - setStyleFlags (shouldBeBold ? (font->styleFlags | bold) - : (font->styleFlags & ~bold)); + const int flags = getStyleFlags(); + setStyleFlags (shouldBeBold ? (flags | bold) + : (flags & ~bold)); } void Font::setItalic (const bool shouldBeItalic) { - setStyleFlags (shouldBeItalic ? (font->styleFlags | italic) - : (font->styleFlags & ~italic)); + const int flags = getStyleFlags(); + setStyleFlags (shouldBeItalic ? (flags | italic) + : (flags & ~italic)); } void Font::setUnderline (const bool shouldBeUnderlined) { - setStyleFlags (shouldBeUnderlined ? (font->styleFlags | underlined) - : (font->styleFlags & ~underlined)); -} - -bool Font::isUnderlined() const noexcept -{ - return (font->styleFlags & underlined) != 0; + dupeInternalIfShared(); + font->underline = shouldBeUnderlined; + checkTypefaceSuitability(); } float Font::getAscent() const @@ -488,10 +618,12 @@ float Font::getAscent() const return font->height * font->ascent; } -float Font::getDescent() const -{ - return font->height - getAscent(); -} +float Font::getHeight() const noexcept { return font->height; } +float Font::getDescent() const { return font->height - getAscent(); } + +float Font::getHeightInPoints() const { return getHeight() * getHeightToPointsFactor(); } +float Font::getAscentInPoints() const { return getAscent() * getHeightToPointsFactor(); } +float Font::getDescentInPoints() const { return getDescent() * getHeightToPointsFactor(); } int Font::getStringWidth (const String& text) const { @@ -537,23 +669,30 @@ void Font::findFonts (Array& destArray) const StringArray names (findAllTypefaceNames()); for (int i = 0; i < names.size(); ++i) - destArray.add (Font (names[i], FontValues::defaultFontHeight, Font::plain)); + { + const StringArray styles (findAllTypefaceStyles (names[i])); + + String style ("Regular"); + + if (! styles.contains (style, true)) + style = styles[0]; + + destArray.add (Font (names[i], style, FontValues::defaultFontHeight)); + } } //============================================================================== String Font::toString() const { - String s (getTypefaceName()); + String s; - if (s == getDefaultSansSerifFontName()) - s = String::empty; - else - s += "; "; + if (getTypefaceName() != getDefaultSansSerifFontName()) + s << getTypefaceName() << "; "; - s += String (getHeight(), 1); + s << String (getHeight(), 1); - if (isBold()) s += " bold"; - if (isItalic()) s += " italic"; + if (getTypefaceStyle() != getDefaultStyle()) + s << ' ' << getTypefaceStyle(); return s; } @@ -569,15 +708,13 @@ Font Font::fromString (const String& fontDescription) if (name.isEmpty()) name = getDefaultSansSerifFontName(); - String sizeAndStyle (fontDescription.substring (separator + 1)); + String sizeAndStyle (fontDescription.substring (separator + 1).trimStart()); float height = sizeAndStyle.getFloatValue(); if (height <= 0) height = 10.0f; - int flags = Font::plain; - if (sizeAndStyle.containsIgnoreCase ("bold")) flags |= Font::bold; - if (sizeAndStyle.containsIgnoreCase ("italic")) flags |= Font::italic; + const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false)); - return Font (name, height, flags); + return Font (name, style, height); } diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h index b237d63..6649d36 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Font.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FONT_JUCEHEADER__ -#define __JUCE_FONT_JUCEHEADER__ - -#include "juce_Typeface.h" -class LowLevelGraphicsContext; +#ifndef JUCE_FONT_H_INCLUDED +#define JUCE_FONT_H_INCLUDED //============================================================================== @@ -68,7 +64,7 @@ public: /** Creates a font with a given typeface and parameters. - @param typefaceName the name of the typeface to use + @param typefaceName the font family 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 @@ -77,6 +73,14 @@ public: */ Font (const String& typefaceName, float fontHeight, int styleFlags); + /** Creates a font with a given typeface and parameters. + + @param typefaceName the font family of the typeface to use + @param typefaceStyle the font style of the typeface to use + @param fontHeight the height in pixels (can be fractional) + */ + Font (const String& typefaceName, const String& typefaceStyle, float fontHeight); + /** Creates a copy of another Font object. */ Font (const Font& other) noexcept; @@ -106,80 +110,102 @@ public: ~Font() noexcept; //============================================================================== - /** Changes the name of the typeface family. + /** Changes the font family of the typeface. 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. + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font family names, + but are generic font family names that are used to represent the various default fonts. + If you need to know the exact typeface font family being used, you can call + Font::getTypeface()->getName(), which will give you the platform-specific font family. If a suitable font isn't found on the machine, it'll just use a default instead. */ void setTypefaceName (const String& faceName); - /** Returns the name of the typeface family that this font uses. + /** Returns the font family of the typeface 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. + or Font::getDefaultMonospacedFontName(), which are not actual platform-specific font family names, + but are generic font familiy 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 you need to know the exact typeface font family being used, you can call + Font::getTypeface()->getName(), which will give you the platform-specific font family. */ const String& getTypefaceName() const noexcept; //============================================================================== - /** Returns a typeface name that represents the default sans-serif font. + /** Returns the font style of the typeface that this font uses. + @see withTypefaceStyle, getAvailableStyles() + */ + const String& getTypefaceStyle() const noexcept; + + /** Changes the font style of the typeface. + @see getAvailableStyles() + */ + void setTypefaceStyle (const String& newStyle); + + /** Returns a copy of this font with a new typeface style. + @see getAvailableStyles() + */ + Font withTypefaceStyle (const String& newStyle) const; + + /** Returns a list of the styles that this font can use. */ + StringArray getAvailableStyles() const; + + //============================================================================== + /** Returns a typeface font family 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. + sans-serif font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName */ static const String& getDefaultSansSerifFontName(); - /** Returns a typeface name that represents the default sans-serif font. + /** Returns a typeface font family that represents the default 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. + serif font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName */ static const String& getDefaultSerifFontName(); - /** Returns a typeface name that represents the default sans-serif font. + /** Returns a typeface font family that represents the default monospaced 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. + monospaced font" - it's not the actual font family of this font. @see setTypefaceName, getDefaultSansSerifFontName, getDefaultSerifFontName */ static const String& getDefaultMonospacedFontName(); + /** Returns a font style name that represents the default style. + + Note that this method just returns a generic placeholder string that means "the default + font style" - it's not the actual name of the font style of any particular font. + + @see setTypefaceStyle + */ + static const String& getDefaultStyle(); + /** Returns the default system typeface for the given font. */ static Typeface::Ptr getDefaultTypefaceForFont (const Font& font); //============================================================================== - /** 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 withHeight, setHeightWithoutChangingWidth, getAscent - */ - float getHeight() const noexcept; - /** Returns a copy of this font with a new height. */ Font withHeight (float height) const; + /** Returns a copy of this font with a new height, specified in points. */ + Font withPointHeight (float heightInPoints) const; + /** Changes the font's height. @see getHeight, withHeight, setHeightWithoutChangingWidth */ @@ -190,18 +216,46 @@ public: */ void setHeightWithoutChangingWidth (float newHeight); - /** Returns the height of the font above its baseline. + /** Returns the total height of this font, in pixels. + This is the maximum height, from the top of the ascent to the bottom of the + descenders. + + @see withHeight, setHeightWithoutChangingWidth, getAscent + */ + float getHeight() const noexcept; + + /** Returns the total height of this font, in points. + This is the maximum height, from the top of the ascent to the bottom of the + descenders. + + @see withPointHeight, getHeight + */ + float getHeightInPoints() const; + + /** Returns the height of the font above its baseline, in pixels. This is the maximum height from the baseline to the top. @see getHeight, getDescent */ float getAscent() const; - /** Returns the amount that the font descends below its baseline. + /** Returns the height of the font above its baseline, in points. + This is the maximum height from the baseline to the top. + @see getHeight, getDescent + */ + float getAscentInPoints() const; + + /** Returns the amount that the font descends below its baseline, in pixels. This is calculated as (getHeight() - getAscent()). @see getAscent, getHeight */ float getDescent() const; + /** Returns the amount that the font descends below its baseline, in points. + This is calculated as (getHeight() - getAscent()). + @see getAscent, getHeight + */ + float getDescentInPoints() const; + //============================================================================== /** Returns the font's style flags. This will return a bitwise-or'ed combination of values from the FontStyleFlags @@ -245,7 +299,6 @@ public: //============================================================================== /** 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. @@ -299,15 +352,19 @@ public: float newHorizontalScale, float newKerningAmount); + /** Changes all the font's characteristics with one call. */ + void setSizeAndStyle (float newHeight, + const String& newStyle, + float newHorizontalScale, + float newKerningAmount); + //============================================================================== /** 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; /** Returns the total width of a string as it would be drawn using this font. - @see getStringWidth */ float getStringWidthFloat (const String& text) const; @@ -329,33 +386,52 @@ public: /** 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 + If you just need the font family 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); - /** Returns a list of all the available typeface names. + /** Returns a list of all the available typeface font families. 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. + You can use this instead of findFonts() if you only need their font family names, + and not font objects. */ static StringArray findAllTypefaceNames(); + /** Returns a list of all the available typeface font styles. + + The names returned can be passed into setTypefaceStyle(). + + You can use this instead of findFonts() if you only need their styles, and not + font objects. + */ + static StringArray findAllTypefaceStyles (const String& family); + //============================================================================== - /** Returns the name of the typeface to be used for rendering glyphs that aren't found - in the requested typeface. + /** Returns the font family of the typeface to be used for rendering glyphs that aren't + found in the requested typeface. */ static const String& getFallbackFontName(); - /** 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. + /** Sets the (platform-specific) font family 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); + /** Returns the font style of the typeface to be used for rendering glyphs that aren't + found in the requested typeface. + */ + static const String& getFallbackFontStyle(); + + /** Sets the (platform-specific) font style of the typeface to use to find glyphs that + aren't available in whatever font you're trying to use. + */ + static void setFallbackFontStyle (const String& style); + //============================================================================== /** Creates a string to describe this font. The string will contain information to describe the font's typeface, size, and @@ -375,8 +451,10 @@ private: class SharedFontInternal; ReferenceCountedObjectPtr font; void dupeInternalIfShared(); + void checkTypefaceSuitability(); + float getHeightToPointsFactor() const; - JUCE_LEAK_DETECTOR (Font); + JUCE_LEAK_DETECTOR (Font) }; -#endif // __JUCE_FONT_JUCEHEADER__ +#endif // JUCE_FONT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp index e6df03c..d2d9ff7 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp @@ -1,28 +1,32 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +PositionedGlyph::PositionedGlyph() noexcept + : character (0), glyph (0), x (0), y (0), w (0), whitespace (false) +{ +} + PositionedGlyph::PositionedGlyph (const Font& font_, const juce_wchar character_, const int glyph_, const float x_, const float y_, const float w_, const bool whitespace_) : font (font_), character (character_), glyph (glyph_), @@ -50,35 +54,30 @@ PositionedGlyph& PositionedGlyph::operator= (const PositionedGlyph& other) return *this; } +static inline void drawGlyphWithFont (const Graphics& g, int glyph, const Font& font, const AffineTransform& t) +{ + LowLevelGraphicsContext& context = g.getInternalContext(); + context.setFont (font); + context.drawGlyph (glyph, t); +} + void PositionedGlyph::draw (const Graphics& g) const { if (! isWhitespace()) - { - LowLevelGraphicsContext* const context = g.getInternalContext(); - context->setFont (font); - context->drawGlyph (glyph, AffineTransform::translation (x, y)); - } + drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y)); } -void PositionedGlyph::draw (const Graphics& g, - const AffineTransform& transform) const +void PositionedGlyph::draw (const Graphics& g, const AffineTransform& transform) const { if (! isWhitespace()) - { - LowLevelGraphicsContext* const context = g.getInternalContext(); - context->setFont (font); - context->drawGlyph (glyph, AffineTransform::translation (x, y) - .followedBy (transform)); - } + drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y).followedBy (transform)); } void PositionedGlyph::createPath (Path& path) const { if (! isWhitespace()) { - Typeface* const t = font.getTypeface(); - - if (t != nullptr) + if (Typeface* const t = font.getTypeface()) { Path p; t->getOutlineForGlyph (glyph, p); @@ -93,9 +92,7 @@ bool PositionedGlyph::hitTest (float px, float py) const { if (getBounds().contains (px, py) && ! isWhitespace()) { - Typeface* const t = font.getTypeface(); - - if (t != nullptr) + if (Typeface* const t = font.getTypeface()) { Path p; t->getOutlineForGlyph (glyph, p); @@ -126,18 +123,13 @@ GlyphArrangement::GlyphArrangement() } GlyphArrangement::GlyphArrangement (const GlyphArrangement& other) + : glyphs (other.glyphs) { - addGlyphArrangement (other); } GlyphArrangement& GlyphArrangement::operator= (const GlyphArrangement& other) { - if (this != &other) - { - clear(); - addGlyphArrangement (other); - } - + glyphs = other.glyphs; return *this; } @@ -151,23 +143,20 @@ void GlyphArrangement::clear() glyphs.clear(); } -PositionedGlyph& GlyphArrangement::getGlyph (const int index) const +PositionedGlyph& GlyphArrangement::getGlyph (const int index) const noexcept { - jassert (isPositiveAndBelow (index, glyphs.size())); - - return *glyphs [index]; + return glyphs.getReference (index); } //============================================================================== void GlyphArrangement::addGlyphArrangement (const GlyphArrangement& other) { - glyphs.ensureStorageAllocated (glyphs.size() + other.glyphs.size()); - glyphs.addCopiesOf (other.glyphs); + glyphs.addArray (other.glyphs); } void GlyphArrangement::addGlyph (const PositionedGlyph& glyph) { - glyphs.add (new PositionedGlyph (glyph)); + glyphs.add (glyph); } void GlyphArrangement::removeRangeOfGlyphs (int startIndex, const int num) @@ -181,9 +170,7 @@ void GlyphArrangement::addLineOfText (const Font& font, const float xOffset, const float yOffset) { - addCurtailedLineOfText (font, text, - xOffset, yOffset, - 1.0e10f, false); + addCurtailedLineOfText (font, text, xOffset, yOffset, 1.0e10f, false); } void GlyphArrangement::addCurtailedLineOfText (const Font& font, @@ -205,7 +192,6 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font, for (int i = 0; i < textLen; ++i) { - const float thisX = xOffsets.getUnchecked (i); const float nextX = xOffsets.getUnchecked (i + 1); if (nextX > maxWidthPixels + 1.0f) @@ -218,12 +204,13 @@ void GlyphArrangement::addCurtailedLineOfText (const Font& font, } else { + const float thisX = xOffsets.getUnchecked (i); const bool isWhitespace = t.isWhitespace(); - glyphs.add (new PositionedGlyph (font, t.getAndAdvance(), - newGlyphs.getUnchecked(i), - xOffset + thisX, yOffset, - nextX - thisX, isWhitespace)); + glyphs.add (PositionedGlyph (font, t.getAndAdvance(), + newGlyphs.getUnchecked(i), + xOffset + thisX, yOffset, + nextX - thisX, isWhitespace)); } } } @@ -245,9 +232,9 @@ int GlyphArrangement::insertEllipsis (const Font& font, const float maxXPos, while (endIndex > startIndex) { - const PositionedGlyph* pg = glyphs.getUnchecked (--endIndex); - xOffset = pg->x; - yOffset = pg->y; + const PositionedGlyph& pg = glyphs.getReference (--endIndex); + xOffset = pg.x; + yOffset = pg.y; glyphs.remove (endIndex); ++numDeleted; @@ -258,8 +245,8 @@ int GlyphArrangement::insertEllipsis (const Font& font, const float maxXPos, for (int i = 3; --i >= 0;) { - glyphs.insert (endIndex++, new PositionedGlyph (font, '.', dotGlyphs.getFirst(), - xOffset, yOffset, dx, false)); + glyphs.insert (endIndex++, PositionedGlyph (font, '.', dotGlyphs.getFirst(), + xOffset, yOffset, dx, false)); --numDeleted; xOffset += dx; @@ -275,7 +262,7 @@ void GlyphArrangement::addJustifiedText (const Font& font, const String& text, float x, float y, const float maxLineWidth, - const Justification& horizontalLayout) + Justification horizontalLayout) { int lineStartIndex = glyphs.size(); addLineOfText (font, text, x, y); @@ -286,33 +273,33 @@ void GlyphArrangement::addJustifiedText (const Font& font, { int i = lineStartIndex; - if (glyphs.getUnchecked(i)->getCharacter() != '\n' - && glyphs.getUnchecked(i)->getCharacter() != '\r') + if (glyphs.getReference(i).getCharacter() != '\n' + && glyphs.getReference(i).getCharacter() != '\r') ++i; - const float lineMaxX = glyphs.getUnchecked (lineStartIndex)->getLeft() + maxLineWidth; + const float lineMaxX = glyphs.getReference (lineStartIndex).getLeft() + maxLineWidth; int lastWordBreakIndex = -1; while (i < glyphs.size()) { - const PositionedGlyph* pg = glyphs.getUnchecked (i); - const juce_wchar c = pg->getCharacter(); + const PositionedGlyph& pg = glyphs.getReference (i); + const juce_wchar c = pg.getCharacter(); if (c == '\r' || c == '\n') { ++i; if (c == '\r' && i < glyphs.size() - && glyphs.getUnchecked(i)->getCharacter() == '\n') + && glyphs.getReference(i).getCharacter() == '\n') ++i; break; } - else if (pg->isWhitespace()) + else if (pg.isWhitespace()) { lastWordBreakIndex = i + 1; } - else if (pg->getRight() - 0.0001f >= lineMaxX) + else if (pg.getRight() - 0.0001f >= lineMaxX) { if (lastWordBreakIndex >= 0) i = lastWordBreakIndex; @@ -323,14 +310,14 @@ void GlyphArrangement::addJustifiedText (const Font& font, ++i; } - const float currentLineStartX = glyphs.getUnchecked (lineStartIndex)->getLeft(); + const float currentLineStartX = glyphs.getReference (lineStartIndex).getLeft(); float currentLineEndX = currentLineStartX; for (int j = i; --j >= lineStartIndex;) { - if (! glyphs.getUnchecked (j)->isWhitespace()) + if (! glyphs.getReference (j).isWhitespace()) { - currentLineEndX = glyphs.getUnchecked (j)->getRight(); + currentLineEndX = glyphs.getReference (j).getRight(); break; } } @@ -357,7 +344,7 @@ void GlyphArrangement::addFittedText (const Font& f, const String& text, const float x, const float y, const float width, const float height, - const Justification& layout, + Justification layout, int maximumLines, const float minimumHorizontalScale) { @@ -373,19 +360,12 @@ void GlyphArrangement::addFittedText (const Font& f, float dy = y - bb.getY(); - if (layout.testFlags (Justification::verticallyCentred)) - dy += (height - bb.getHeight()) * 0.5f; - else if (layout.testFlags (Justification::bottom)) - dy += height - bb.getHeight(); + if (layout.testFlags (Justification::verticallyCentred)) dy += (height - bb.getHeight()) * 0.5f; + else if (layout.testFlags (Justification::bottom)) dy += (height - bb.getHeight()); ga.moveRangeOfGlyphs (0, -1, 0.0f, dy); - glyphs.ensureStorageAllocated (glyphs.size() + ga.glyphs.size()); - - for (int i = 0; i < ga.glyphs.size(); ++i) - glyphs.add (ga.glyphs.getUnchecked (i)); - - ga.glyphs.clear (false); + glyphs.addArray (ga.glyphs); return; } @@ -394,8 +374,8 @@ void GlyphArrangement::addFittedText (const Font& f, if (glyphs.size() > startIndex) { - float lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() - - glyphs.getUnchecked (startIndex)->getLeft(); + float lineWidth = glyphs.getReference (glyphs.size() - 1).getRight() + - glyphs.getReference (startIndex).getLeft(); if (lineWidth <= 0) return; @@ -440,8 +420,8 @@ void GlyphArrangement::addFittedText (const Font& f, removeRangeOfGlyphs (startIndex, -1); addLineOfText (font, txt, x, y); - lineWidth = glyphs.getUnchecked (glyphs.size() - 1)->getRight() - - glyphs.getUnchecked (startIndex)->getLeft(); + lineWidth = glyphs.getReference (glyphs.size() - 1).getRight() + - glyphs.getReference (startIndex).getLeft(); } if (numLines > lineWidth / width || newFontHeight < 8.0f) @@ -453,13 +433,11 @@ void GlyphArrangement::addFittedText (const Font& f, float lineY = y; float widthPerLine = lineWidth / numLines; - int lastLineStartIndex = 0; for (int line = 0; line < numLines; ++line) { int i = startIndex; - lastLineStartIndex = i; - float lineStartX = glyphs.getUnchecked (startIndex)->getLeft(); + float lineStartX = glyphs.getReference (startIndex).getLeft(); if (line == numLines - 1) { @@ -470,7 +448,7 @@ void GlyphArrangement::addFittedText (const Font& f, { while (i < glyphs.size()) { - lineWidth = (glyphs.getUnchecked (i)->getRight() - lineStartX); + lineWidth = (glyphs.getReference (i).getRight() - lineStartX); if (lineWidth > widthPerLine) { @@ -480,10 +458,10 @@ void GlyphArrangement::addFittedText (const Font& f, while (i < glyphs.size()) { - if ((glyphs.getUnchecked (i)->getRight() - lineStartX) * minimumHorizontalScale < width) + if ((glyphs.getReference (i).getRight() - lineStartX) * minimumHorizontalScale < width) { - if (glyphs.getUnchecked (i)->isWhitespace() - || glyphs.getUnchecked (i)->getCharacter() == '-') + if (glyphs.getReference (i).isWhitespace() + || glyphs.getReference (i).getCharacter() == '-') { ++i; break; @@ -494,10 +472,10 @@ void GlyphArrangement::addFittedText (const Font& f, // can't find a suitable break, so try looking backwards.. i = searchStartIndex; - for (int back = 1; back < jmin (5, i - startIndex - 1); ++back) + for (int back = 1; back < jmin (7, i - startIndex - 1); ++back) { - if (glyphs.getUnchecked (i - back)->isWhitespace() - || glyphs.getUnchecked (i - back)->getCharacter() == '-') + if (glyphs.getReference (i - back).isWhitespace() + || glyphs.getReference (i - back).getCharacter() == '-') { i -= back - 1; break; @@ -517,12 +495,12 @@ void GlyphArrangement::addFittedText (const Font& f, } int wsStart = i; - while (wsStart > 0 && glyphs.getUnchecked (wsStart - 1)->isWhitespace()) + while (wsStart > 0 && glyphs.getReference (wsStart - 1).isWhitespace()) --wsStart; int wsEnd = i; - while (wsEnd < glyphs.size() && glyphs.getUnchecked (wsEnd)->isWhitespace()) + while (wsEnd < glyphs.size() && glyphs.getReference (wsEnd).isWhitespace()) ++wsEnd; removeRangeOfGlyphs (wsStart, wsEnd - wsStart); @@ -548,8 +526,7 @@ void GlyphArrangement::addFittedText (const Font& f, } //============================================================================== -void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, - const float dx, const float dy) +void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, const float dx, const float dy) { jassert (startIndex >= 0); @@ -559,23 +536,23 @@ void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, num = glyphs.size() - startIndex; while (--num >= 0) - glyphs.getUnchecked (startIndex++)->moveBy (dx, dy); + glyphs.getReference (startIndex++).moveBy (dx, dy); } } int GlyphArrangement::fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font& font, - const Justification& justification, float minimumHorizontalScale) + Justification justification, float minimumHorizontalScale) { int numDeleted = 0; - const float lineStartX = glyphs.getUnchecked (start)->getLeft(); - float lineWidth = glyphs.getUnchecked (start + numGlyphs - 1)->getRight() - lineStartX; + const float lineStartX = glyphs.getReference (start).getLeft(); + float lineWidth = glyphs.getReference (start + numGlyphs - 1).getRight() - lineStartX; if (lineWidth > w) { if (minimumHorizontalScale < 1.0f) { stretchRangeOfGlyphs (start, numGlyphs, jmax (minimumHorizontalScale, w / lineWidth)); - lineWidth = glyphs.getUnchecked (start + numGlyphs - 1)->getRight() - lineStartX - 0.5f; + lineWidth = glyphs.getReference (start + numGlyphs - 1).getRight() - lineStartX - 0.5f; } if (lineWidth > w) @@ -599,15 +576,15 @@ void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num, if (num > 0) { - const float xAnchor = glyphs.getUnchecked (startIndex)->getLeft(); + const float xAnchor = glyphs.getReference (startIndex).getLeft(); while (--num >= 0) { - PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); + PositionedGlyph& pg = glyphs.getReference (startIndex++); - pg->x = xAnchor + (pg->x - xAnchor) * horizontalScaleFactor; - pg->font.setHorizontalScale (pg->font.getHorizontalScale() * horizontalScaleFactor); - pg->w *= horizontalScaleFactor; + pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor; + pg.font.setHorizontalScale (pg.font.getHorizontalScale() * horizontalScaleFactor); + pg.w *= horizontalScaleFactor; } } } @@ -623,10 +600,10 @@ Rectangle GlyphArrangement::getBoundingBox (int startIndex, int num, cons while (--num >= 0) { - const PositionedGlyph* const pg = glyphs.getUnchecked (startIndex++); + const PositionedGlyph& pg = glyphs.getReference (startIndex++); - if (includeWhitespace || ! pg->isWhitespace()) - result = result.getUnion (pg->getBounds()); + if (includeWhitespace || ! pg.isWhitespace()) + result = result.getUnion (pg.getBounds()); } return result; @@ -634,7 +611,7 @@ Rectangle GlyphArrangement::getBoundingBox (int startIndex, int num, cons void GlyphArrangement::justifyGlyphs (const int startIndex, const int num, const float x, const float y, const float width, const float height, - const Justification& justification) + Justification justification) { jassert (num >= 0 && startIndex >= 0); @@ -642,37 +619,28 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, const int num, { const Rectangle bb (getBoundingBox (startIndex, num, ! justification.testFlags (Justification::horizontallyJustified | Justification::horizontallyCentred))); - float deltaX = 0.0f; + float deltaX = 0.0f, deltaY = 0.0f; - if (justification.testFlags (Justification::horizontallyJustified)) - deltaX = x - bb.getX(); - else if (justification.testFlags (Justification::horizontallyCentred)) - deltaX = x + (width - bb.getWidth()) * 0.5f - bb.getX(); - else if (justification.testFlags (Justification::right)) - deltaX = (x + width) - bb.getRight(); - else - deltaX = x - bb.getX(); + if (justification.testFlags (Justification::horizontallyJustified)) deltaX = x - bb.getX(); + else if (justification.testFlags (Justification::horizontallyCentred)) deltaX = x + (width - bb.getWidth()) * 0.5f - bb.getX(); + else if (justification.testFlags (Justification::right)) deltaX = x + width - bb.getRight(); + else deltaX = x - bb.getX(); - float deltaY = 0.0f; - - if (justification.testFlags (Justification::top)) - deltaY = y - bb.getY(); - else if (justification.testFlags (Justification::bottom)) - deltaY = (y + height) - bb.getBottom(); - else - deltaY = y + (height - bb.getHeight()) * 0.5f - bb.getY(); + if (justification.testFlags (Justification::top)) deltaY = y - bb.getY(); + else if (justification.testFlags (Justification::bottom)) deltaY = y + height - bb.getBottom(); + else deltaY = y + (height - bb.getHeight()) * 0.5f - bb.getY(); moveRangeOfGlyphs (startIndex, num, deltaX, deltaY); if (justification.testFlags (Justification::horizontallyJustified)) { int lineStart = 0; - float baseY = glyphs.getUnchecked (startIndex)->getBaselineY(); + float baseY = glyphs.getReference (startIndex).getBaselineY(); int i; for (i = 0; i < num; ++i) { - const float glyphY = glyphs.getUnchecked (startIndex + i)->getBaselineY(); + const float glyphY = glyphs.getReference (startIndex + i).getBaselineY(); if (glyphY != baseY) { @@ -692,15 +660,15 @@ void GlyphArrangement::justifyGlyphs (const int startIndex, const int num, void GlyphArrangement::spreadOutLine (const int start, const int num, const float targetWidth) { if (start + num < glyphs.size() - && glyphs.getUnchecked (start + num - 1)->getCharacter() != '\r' - && glyphs.getUnchecked (start + num - 1)->getCharacter() != '\n') + && glyphs.getReference (start + num - 1).getCharacter() != '\r' + && glyphs.getReference (start + num - 1).getCharacter() != '\n') { int numSpaces = 0; int spacesAtEnd = 0; for (int i = 0; i < num; ++i) { - if (glyphs.getUnchecked (start + i)->isWhitespace()) + if (glyphs.getReference (start + i).isWhitespace()) { ++spacesAtEnd; ++numSpaces; @@ -715,8 +683,8 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa if (numSpaces > 0) { - const float startX = glyphs.getUnchecked (start)->getLeft(); - const float endX = glyphs.getUnchecked (start + num - 1 - spacesAtEnd)->getRight(); + const float startX = glyphs.getReference (start).getLeft(); + const float endX = glyphs.getReference (start + num - 1 - spacesAtEnd).getRight(); const float extraPaddingBetweenWords = (targetWidth - (endX - startX)) / (float) numSpaces; @@ -725,9 +693,9 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa for (int i = 0; i < num; ++i) { - glyphs.getUnchecked (start + i)->moveBy (deltaX, 0.0f); + glyphs.getReference (start + i).moveBy (deltaX, 0.0f); - if (glyphs.getUnchecked (start + i)->isWhitespace()) + if (glyphs.getReference (start + i).isWhitespace()) deltaX += extraPaddingBetweenWords; } } @@ -735,26 +703,31 @@ void GlyphArrangement::spreadOutLine (const int start, const int num, const floa } //============================================================================== +inline void GlyphArrangement::drawGlyphUnderline (const Graphics& g, const PositionedGlyph& pg, + const int i, const AffineTransform& transform) const +{ + const float lineThickness = (pg.font.getDescent()) * 0.3f; + + float nextX = pg.x + pg.w; + + if (i < glyphs.size() - 1 && glyphs.getReference (i + 1).y == pg.y) + nextX = glyphs.getReference (i + 1).x; + + Path p; + p.addRectangle (pg.x, pg.y + lineThickness * 2.0f, nextX - pg.x, lineThickness); + g.fillPath (p, transform); +} + void GlyphArrangement::draw (const Graphics& g) const { for (int i = 0; i < glyphs.size(); ++i) { - const PositionedGlyph* const pg = glyphs.getUnchecked(i); + const PositionedGlyph& pg = glyphs.getReference(i); - if (pg->font.isUnderlined()) - { - const float lineThickness = (pg->font.getDescent()) * 0.3f; + if (pg.font.isUnderlined()) + drawGlyphUnderline (g, pg, i, AffineTransform::identity); - float nextX = pg->x + pg->w; - - if (i < glyphs.size() - 1 && glyphs.getUnchecked (i + 1)->y == pg->y) - nextX = glyphs.getUnchecked (i + 1)->x; - - g.fillRect (pg->x, pg->y + lineThickness * 2.0f, - nextX - pg->x, lineThickness); - } - - pg->draw (g); + pg.draw (g); } } @@ -762,39 +735,25 @@ void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform { for (int i = 0; i < glyphs.size(); ++i) { - const PositionedGlyph* const pg = glyphs.getUnchecked(i); + const PositionedGlyph& pg = glyphs.getReference(i); - if (pg->font.isUnderlined()) - { - const float lineThickness = (pg->font.getDescent()) * 0.3f; + if (pg.font.isUnderlined()) + drawGlyphUnderline (g, pg, i, transform); - float nextX = pg->x + pg->w; - - if (i < glyphs.size() - 1 && glyphs.getUnchecked (i + 1)->y == pg->y) - nextX = glyphs.getUnchecked (i + 1)->x; - - Path p; - p.addLineSegment (Line (pg->x, pg->y + lineThickness * 2.0f, - nextX, pg->y + lineThickness * 2.0f), - lineThickness); - - g.fillPath (p, transform); - } - - pg->draw (g, transform); + pg.draw (g, transform); } } void GlyphArrangement::createPath (Path& path) const { for (int i = 0; i < glyphs.size(); ++i) - glyphs.getUnchecked (i)->createPath (path); + glyphs.getReference (i).createPath (path); } -int GlyphArrangement::findGlyphIndexAt (float x, float y) const +int GlyphArrangement::findGlyphIndexAt (const float x, const float y) const { for (int i = 0; i < glyphs.size(); ++i) - if (glyphs.getUnchecked (i)->hitTest (x, y)) + if (glyphs.getReference (i).hitTest (x, y)) return i; return -1; diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.h b/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.h index 23c90d0..f11894d 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.h +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_GlyphArrangement.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ -#define __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ - -#include "juce_Font.h" -#include "../contexts/juce_GraphicsContext.h" +#ifndef JUCE_GLYPHARRANGEMENT_H_INCLUDED +#define JUCE_GLYPHARRANGEMENT_H_INCLUDED //============================================================================== @@ -44,11 +40,12 @@ class JUCE_API PositionedGlyph { public: //============================================================================== + PositionedGlyph() noexcept; PositionedGlyph (const Font& font, juce_wchar character, int glyphNumber, float anchorX, float baselineY, float width, bool isWhitespace); - PositionedGlyph (const PositionedGlyph& other); - PositionedGlyph& operator= (const PositionedGlyph& other); + PositionedGlyph (const PositionedGlyph&); + PositionedGlyph& operator= (const PositionedGlyph&); ~PositionedGlyph(); /** Returns the character the glyph represents. */ @@ -98,7 +95,7 @@ private: float x, y, w; bool whitespace; - JUCE_LEAK_DETECTOR (PositionedGlyph); + JUCE_LEAK_DETECTOR (PositionedGlyph) }; @@ -120,12 +117,12 @@ public: GlyphArrangement(); /** Takes a copy of another arrangement. */ - GlyphArrangement (const GlyphArrangement& other); + GlyphArrangement (const GlyphArrangement&); /** Copies another arrangement onto this one. To add another arrangement without clearing this one, use addGlyphArrangement(). */ - GlyphArrangement& operator= (const GlyphArrangement& other); + GlyphArrangement& operator= (const GlyphArrangement&); /** Destructor. */ ~GlyphArrangement(); @@ -140,11 +137,10 @@ public: careful not to pass an out-of-range index here, as it doesn't do any bounds-checking. */ - PositionedGlyph& getGlyph (int index) const; + PositionedGlyph& getGlyph (int index) const noexcept; //============================================================================== - /** Clears all text from the arrangement and resets it. - */ + /** Clears all text from the arrangement and resets it. */ void clear(); /** Appends a line of text to the arrangement. @@ -181,14 +177,14 @@ public: 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 + The y coordinate 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); + Justification horizontalLayout); /** Tries to fit some text withing a given space. @@ -208,15 +204,15 @@ public: void addFittedText (const Font& font, const String& text, float x, float y, float width, float height, - const Justification& layout, + Justification layout, int maximumLinesToUse, float minimumHorizontalScale = 0.7f); /** Appends another glyph arrangement to this one. */ - void addGlyphArrangement (const GlyphArrangement& other); + void addGlyphArrangement (const GlyphArrangement&); /** Appends a custom glyph to the arrangement. */ - void addGlyph (const PositionedGlyph& glyph); + void addGlyph (const PositionedGlyph&); //============================================================================== /** Draws this glyph arrangement to a graphics context. @@ -224,23 +220,21 @@ public: 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; + void draw (const Graphics&) 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; + void draw (const Graphics&, const AffineTransform&) 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. - + /** Looks for a glyph that contains the given coordinate. @returns the index of the glyph, or -1 if none were found. */ int findGlyphIndexAt (float x, float y) const; @@ -296,20 +290,21 @@ public: */ void justifyGlyphs (int startIndex, int numGlyphs, float x, float y, float width, float height, - const Justification& justification); + Justification justification); private: //============================================================================== - OwnedArray glyphs; + Array glyphs; int insertEllipsis (const Font&, float maxXPos, int startIndex, int endIndex); int fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font&, - const Justification&, float minimumHorizontalScale); + Justification, float minimumHorizontalScale); void spreadOutLine (int start, int numGlyphs, float targetWidth); + void drawGlyphUnderline (const Graphics&, const PositionedGlyph&, int, const AffineTransform&) const; - JUCE_LEAK_DETECTOR (GlyphArrangement); + JUCE_LEAK_DETECTOR (GlyphArrangement) }; -#endif // __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ +#endif // JUCE_GLYPHARRANGEMENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp index 62d6f7b..526cac1 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -TextLayout::Glyph::Glyph (const int glyphCode_, const Point& anchor_, float width_) noexcept +TextLayout::Glyph::Glyph (const int glyphCode_, Point anchor_, float width_) noexcept : glyphCode (glyphCode_), anchor (anchor_), width (width_) { } @@ -49,7 +48,7 @@ TextLayout::Run::Run() noexcept { } -TextLayout::Run::Run (const Range& range, const int numGlyphsToPreallocate) +TextLayout::Run::Run (Range range, const int numGlyphsToPreallocate) : colour (0xff000000), stringRange (range) { glyphs.ensureStorageAllocated (numGlyphsToPreallocate); @@ -71,7 +70,7 @@ TextLayout::Line::Line() noexcept { } -TextLayout::Line::Line (const Range& stringRange_, const Point& lineOrigin_, +TextLayout::Line::Line (Range stringRange_, Point lineOrigin_, const float ascent_, const float descent_, const float leading_, const int numRunsToPreallocate) : stringRange (stringRange_), lineOrigin (lineOrigin_), @@ -98,17 +97,16 @@ Range TextLayout::Line::getLineBoundsX() const noexcept for (int i = runs.size(); --i >= 0;) { - const Run* run = runs.getUnchecked(i); - jassert (run != nullptr); + const Run& run = *runs.getUnchecked(i); - if (run->glyphs.size() > 0) + if (run.glyphs.size() > 0) { - float minX = run->glyphs.getReference(0).anchor.x; + float minX = run.glyphs.getReference(0).anchor.x; float maxX = minX; - for (int j = run->glyphs.size(); --j > 0;) + for (int j = run.glyphs.size(); --j >= 0;) { - const Glyph& glyph = run->glyphs.getReference (j); + const Glyph& glyph = run.glyphs.getReference (j); const float x = glyph.anchor.x; minX = jmin (minX, x); maxX = jmax (maxX, x + glyph.width); @@ -174,10 +172,10 @@ TextLayout::~TextLayout() float TextLayout::getHeight() const noexcept { - const Line* const lastLine = lines.getLast(); + if (const Line* const lastLine = lines.getLast()) + return lastLine->lineOrigin.y + lastLine->descent; - return lastLine != nullptr ? lastLine->lineOrigin.y + lastLine->descent - : 0; + return 0.0f; } TextLayout::Line& TextLayout::getLine (const int index) const @@ -197,9 +195,9 @@ void TextLayout::addLine (Line* line) void TextLayout::draw (Graphics& g, const Rectangle& area) const { - const Point origin (justification.appliedToRectangle (Rectangle (0, 0, width, getHeight()), area).getPosition()); + const Point origin (justification.appliedToRectangle (Rectangle (width, getHeight()), area).getPosition()); - LowLevelGraphicsContext& context = *g.getInternalContext(); + LowLevelGraphicsContext& context = g.getInternalContext(); for (int i = 0; i < getNumLines(); ++i) { @@ -208,14 +206,13 @@ void TextLayout::draw (Graphics& g, const Rectangle& area) const for (int j = 0; j < line.runs.size(); ++j) { - const Run* const run = line.runs.getUnchecked (j); - jassert (run != nullptr); - context.setFont (run->font); - context.setFill (run->colour); + const Run& run = *line.runs.getUnchecked (j); + context.setFont (run.font); + context.setFill (run.colour); - for (int k = 0; k < run->glyphs.size(); ++k) + for (int k = 0; k < run.glyphs.size(); ++k) { - const Glyph& glyph = run->glyphs.getReference (k); + const Glyph& glyph = run.glyphs.getReference (k); context.drawGlyph (glyph.glyphCode, AffineTransform::translation (lineOrigin.x + glyph.anchor.x, lineOrigin.y + glyph.anchor.y)); } @@ -232,7 +229,7 @@ void TextLayout::createLayout (const AttributedString& text, float maxWidth) if (! createNativeLayout (text)) createStandardLayout (text); - recalculateWidth(); + recalculateWidth (text); } //============================================================================== @@ -240,7 +237,7 @@ namespace TextLayoutHelpers { struct FontAndColour { - FontAndColour (const Font* font_) noexcept : font (font_), colour (0xff000000) {} + FontAndColour (const Font* f) noexcept : font (f), colour (0xff000000) {} const Font* font; Colour colour; @@ -253,8 +250,8 @@ namespace TextLayoutHelpers struct RunAttribute { - RunAttribute (const FontAndColour& fontAndColour_, const Range& range_) noexcept - : fontAndColour (fontAndColour_), range (range_) + RunAttribute (const FontAndColour& fc, const Range r) noexcept + : fontAndColour (fc), range (r) {} FontAndColour fontAndColour; @@ -263,18 +260,19 @@ namespace TextLayoutHelpers struct Token { - Token (const String& t, const Font& f, const Colour& c, const bool isWhitespace_) + Token (const String& t, const Font& f, Colour c, const bool whitespace) : text (t), font (f), colour (c), - area (font.getStringWidth (t), roundToInt (f.getHeight())), - isWhitespace (isWhitespace_), + area (font.getStringWidthFloat (t), f.getHeight()), + isWhitespace (whitespace), isNewLine (t.containsChar ('\n') || t.containsChar ('\r')) {} const String text; const Font font; const Colour colour; - Rectangle area; - int line, lineHeight; + Rectangle area; + int line; + float lineHeight; const bool isWhitespace, isNewLine; private: @@ -292,8 +290,7 @@ namespace TextLayoutHelpers layout.ensureStorageAllocated (totalLines); addTextRuns (text); - - layoutRuns ((int) layout.getWidth()); + layoutRuns (layout.getWidth()); int charPosition = 0; int lineStartPosition = 0; @@ -306,60 +303,72 @@ namespace TextLayoutHelpers for (int i = 0; i < tokens.size(); ++i) { - const Token* const t = tokens.getUnchecked (i); - const Point tokenPos (t->area.getPosition().toFloat()); + const Token& t = *tokens.getUnchecked (i); Array newGlyphs; Array xOffsets; - t->font.getGlyphPositions (t->text.trimEnd(), newGlyphs, xOffsets); + t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text), newGlyphs, xOffsets); if (currentRun == nullptr) currentRun = new TextLayout::Run(); if (currentLine == nullptr) currentLine = new TextLayout::Line(); - currentRun->glyphs.ensureStorageAllocated (currentRun->glyphs.size() + newGlyphs.size()); - - for (int j = 0; j < newGlyphs.size(); ++j) + if (newGlyphs.size() > 0) { + currentRun->glyphs.ensureStorageAllocated (currentRun->glyphs.size() + newGlyphs.size()); + const Point tokenOrigin (t.area.getPosition().translated (0, t.font.getAscent())); + if (needToSetLineOrigin) { needToSetLineOrigin = false; - currentLine->lineOrigin = tokenPos.translated (0, t->font.getAscent()); + currentLine->lineOrigin = tokenOrigin; } - const float x = xOffsets.getUnchecked (j); - currentRun->glyphs.add (TextLayout::Glyph (newGlyphs.getUnchecked(j), - Point (tokenPos.getX() + x, 0), - xOffsets.getUnchecked (j + 1) - x)); - ++charPosition; + const Point glyphOffset (tokenOrigin - currentLine->lineOrigin); + + for (int j = 0; j < newGlyphs.size(); ++j) + { + const float x = xOffsets.getUnchecked (j); + currentRun->glyphs.add (TextLayout::Glyph (newGlyphs.getUnchecked(j), + glyphOffset.translated (x, 0), + xOffsets.getUnchecked (j + 1) - x)); + } + + charPosition += newGlyphs.size(); } - if (t->isWhitespace || t->isNewLine) + if (t.isWhitespace || t.isNewLine) ++charPosition; const Token* const nextToken = tokens [i + 1]; if (nextToken == nullptr) // this is the last token { - addRun (currentLine, currentRun.release(), t, runStartPosition, charPosition); + addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition); currentLine->stringRange = Range (lineStartPosition, charPosition); - layout.addLine (currentLine.release()); + + if (! needToSetLineOrigin) + layout.addLine (currentLine.release()); + + needToSetLineOrigin = true; } else { - if (t->font != nextToken->font || t->colour != nextToken->colour) + if (t.font != nextToken->font || t.colour != nextToken->colour) { - addRun (currentLine, currentRun.release(), t, runStartPosition, charPosition); + addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition); runStartPosition = charPosition; } - if (t->line != nextToken->line) + if (t.line != nextToken->line) { if (currentRun == nullptr) currentRun = new TextLayout::Run(); - addRun (currentLine, currentRun.release(), t, runStartPosition, charPosition); + addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition); currentLine->stringRange = Range (lineStartPosition, charPosition); - layout.addLine (currentLine.release()); + + if (! needToSetLineOrigin) + layout.addLine (currentLine.release()); runStartPosition = charPosition; lineStartPosition = charPosition; @@ -370,12 +379,12 @@ namespace TextLayoutHelpers if ((text.getJustification().getFlags() & (Justification::right | Justification::horizontallyCentred)) != 0) { - const int totalW = (int) layout.getWidth(); + const float totalW = layout.getWidth(); const bool isCentred = (text.getJustification().getFlags() & Justification::horizontallyCentred) != 0; for (int i = 0; i < layout.getNumLines(); ++i) { - float dx = (float) (totalW - getLineWidth (i)); + float dx = totalW - layout.getLine(i).getLineBoundsX().getLength(); if (isCentred) dx /= 2.0f; @@ -386,15 +395,15 @@ namespace TextLayoutHelpers } private: - static void addRun (TextLayout::Line* glyphLine, TextLayout::Run* glyphRun, - const Token* const t, const int start, const int end) + static void addRun (TextLayout::Line& glyphLine, TextLayout::Run* glyphRun, + const Token& t, const int start, const int end) { glyphRun->stringRange = Range (start, end); - glyphRun->font = t->font; - glyphRun->colour = t->colour; - glyphLine->ascent = jmax (glyphLine->ascent, t->font.getAscent()); - glyphLine->descent = jmax (glyphLine->descent, t->font.getDescent()); - glyphLine->runs.add (glyphRun); + glyphRun->font = t.font; + glyphRun->colour = t.colour; + glyphLine.ascent = jmax (glyphLine.ascent, t.font.getAscent()); + glyphLine.descent = jmax (glyphLine.descent, t.font.getDescent()); + glyphLine.runs.add (glyphRun); } static int getCharacterType (const juce_wchar c) noexcept @@ -405,8 +414,8 @@ namespace TextLayoutHelpers return CharacterFunctions::isWhitespace (c) ? 2 : 1; } - void appendText (const AttributedString& text, const Range& stringRange, - const Font& font, const Colour& colour) + void appendText (const AttributedString& text, const Range stringRange, + const Font& font, Colour colour) { const String stringText (text.getText().substring (stringRange.getStart(), stringRange.getEnd())); String::CharPointerType t (stringText.getCharPointer()); @@ -444,25 +453,25 @@ namespace TextLayoutHelpers tokens.add (new Token (currentString, font, colour, lastCharType == 2)); } - void layoutRuns (const int maxWidth) + void layoutRuns (const float maxWidth) { - int x = 0, y = 0, h = 0; + float x = 0, y = 0, h = 0; int i; for (i = 0; i < tokens.size(); ++i) { - Token* const t = tokens.getUnchecked(i); - t->area.setPosition (x, y); - t->line = totalLines; - x += t->area.getWidth(); - h = jmax (h, t->area.getHeight()); + Token& t = *tokens.getUnchecked(i); + t.area.setPosition (x, y); + t.line = totalLines; + x += t.area.getWidth(); + h = jmax (h, t.area.getHeight()); const Token* const nextTok = tokens[i + 1]; if (nextTok == nullptr) break; - if (t->isNewLine || ((! nextTok->isWhitespace) && x + nextTok->area.getWidth() > maxWidth)) + if (t.isNewLine || ((! nextTok->isWhitespace) && x + nextTok->area.getWidth() > maxWidth)) { setLastLineHeight (i + 1, h); x = 0; @@ -476,34 +485,19 @@ namespace TextLayoutHelpers ++totalLines; } - void setLastLineHeight (int i, const int height) noexcept + void setLastLineHeight (int i, const float height) noexcept { while (--i >= 0) { - Token* const tok = tokens.getUnchecked (i); + Token& tok = *tokens.getUnchecked (i); - if (tok->line == totalLines) - tok->lineHeight = height; + if (tok.line == totalLines) + tok.lineHeight = height; else break; } } - int getLineWidth (const int lineNumber) const noexcept - { - int maxW = 0; - - for (int i = tokens.size(); --i >= 0;) - { - const Token* const t = tokens.getUnchecked (i); - - if (t->line == lineNumber && ! t->isWhitespace) - maxW = jmax (maxW, t->area.getRight()); - } - - return maxW; - } - void addTextRuns (const AttributedString& text) { Font defaultFont; @@ -512,7 +506,7 @@ namespace TextLayoutHelpers { const int stringLength = text.getText().length(); int rangeStart = 0; - FontAndColour lastFontAndColour (nullptr); + FontAndColour lastFontAndColour (&defaultFont); // Iterate through every character in the string for (int i = 0; i < stringLength; ++i) @@ -522,26 +516,26 @@ namespace TextLayoutHelpers for (int j = 0; j < numCharacterAttributes; ++j) { - const AttributedString::Attribute* const attr = text.getAttribute (j); + const AttributedString::Attribute& attr = *text.getAttribute (j); - // Check if the current character falls within the range of a font attribute - if (attr->getFont() != nullptr && (i >= attr->range.getStart()) && (i < attr->range.getEnd())) - newFontAndColour.font = attr->getFont(); - - // Check if the current character falls within the range of a foreground colour attribute - if (attr->getColour() != nullptr && (i >= attr->range.getStart()) && (i < attr->range.getEnd())) - newFontAndColour.colour = *attr->getColour(); + if (attr.range.contains (i)) + { + if (const Font* f = attr.getFont()) newFontAndColour.font = f; + if (const Colour* c = attr.getColour()) newFontAndColour.colour = *c; + } } - if (i > 0 && (newFontAndColour != lastFontAndColour || i == stringLength - 1)) + if (i > 0 && newFontAndColour != lastFontAndColour) { - runAttributes.add (RunAttribute (lastFontAndColour, - Range (rangeStart, (i < stringLength - 1) ? i : (i + 1)))); + runAttributes.add (RunAttribute (lastFontAndColour, Range (rangeStart, i))); rangeStart = i; } lastFontAndColour = newFontAndColour; } + + if (rangeStart < stringLength) + runAttributes.add (RunAttribute (lastFontAndColour, Range (rangeStart, stringLength))); } for (int i = 0; i < runAttributes.size(); ++i) @@ -551,10 +545,19 @@ namespace TextLayoutHelpers } } + static String getTrimmedEndIfNotAllWhitespace (const String& s) + { + String trimmed (s.trimEnd()); + if (trimmed.isEmpty() && ! s.isEmpty()) + trimmed = s.replaceCharacters ("\r\n\t", " "); + + return trimmed; + } + OwnedArray tokens; int totalLines; - JUCE_DECLARE_NON_COPYABLE (TokenList); + JUCE_DECLARE_NON_COPYABLE (TokenList) }; } @@ -600,17 +603,16 @@ void TextLayout::createStandardLayout (const AttributedString& text) l.createLayout (text, *this); } -void TextLayout::recalculateWidth() +void TextLayout::recalculateWidth (const AttributedString& text) { - if (lines.size() > 0) + if (lines.size() > 0 && text.getReadingDirection() != AttributedString::rightToLeft) { Range range (lines.getFirst()->getLineBoundsX()); - int i; - for (i = lines.size(); --i > 0;) + for (int i = lines.size(); --i > 0;) range = range.getUnionWith (lines.getUnchecked(i)->getLineBoundsX()); - for (i = lines.size(); --i >= 0;) + for (int i = lines.size(); --i >= 0;) lines.getUnchecked(i)->lineOrigin.x -= range.getStart(); width = range.getLength(); diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.h b/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.h index d051af5..b5f9eea 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.h +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_TextLayout.h @@ -1,34 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ -#define __JUCE_TEXTLAYOUT_JUCEHEADER__ +#ifndef JUCE_TEXTLAYOUT_H_INCLUDED +#define JUCE_TEXTLAYOUT_H_INCLUDED -#include "juce_Font.h" -#include "../placement/juce_Justification.h" -class Graphics; //============================================================================== /** @@ -82,7 +78,7 @@ public: class JUCE_API Glyph { public: - Glyph (int glyphCode, const Point& anchor, float width) noexcept; + Glyph (int glyphCode, Point anchor, float width) noexcept; Glyph (const Glyph&) noexcept; Glyph& operator= (const Glyph&) noexcept; ~Glyph() noexcept; @@ -98,7 +94,7 @@ public: float width; private: - JUCE_LEAK_DETECTOR (Glyph); + JUCE_LEAK_DETECTOR (Glyph) }; //============================================================================== @@ -108,7 +104,7 @@ public: public: Run() noexcept; Run (const Run&); - Run (const Range& stringRange, int numGlyphsToPreallocate); + Run (Range stringRange, int numGlyphsToPreallocate); ~Run() noexcept; Font font; /**< The run's font. */ @@ -118,7 +114,7 @@ public: original string that was used to create it. */ private: Run& operator= (const Run&); - JUCE_LEAK_DETECTOR (Run); + JUCE_LEAK_DETECTOR (Run) }; //============================================================================== @@ -128,7 +124,7 @@ public: public: Line() noexcept; Line (const Line&); - Line (const Range& stringRange, const Point& lineOrigin, + Line (Range stringRange, Point lineOrigin, float ascent, float descent, float leading, int numRunsToPreallocate); ~Line() noexcept; @@ -143,7 +139,7 @@ public: private: Line& operator= (const Line&); - JUCE_LEAK_DETECTOR (Line); + JUCE_LEAK_DETECTOR (Line) }; //============================================================================== @@ -173,9 +169,9 @@ private: void createStandardLayout (const AttributedString&); bool createNativeLayout (const AttributedString&); - void recalculateWidth(); + void recalculateWidth (const AttributedString&); - JUCE_LEAK_DETECTOR (TextLayout); + JUCE_LEAK_DETECTOR (TextLayout) }; -#endif // __JUCE_TEXTLAYOUT_JUCEHEADER__ +#endif // JUCE_TEXTLAYOUT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp index 1c18308..4eda6b0 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.cpp @@ -1,30 +1,108 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -Typeface::Typeface (const String& name_) noexcept - : name (name_) +struct FontStyleHelpers +{ + static const char* getStyleName (const bool bold, + const bool italic) noexcept + { + if (bold && italic) return "Bold Italic"; + if (bold) return "Bold"; + if (italic) return "Italic"; + return "Regular"; + } + + static const char* getStyleName (const int styleFlags) noexcept + { + return getStyleName ((styleFlags & Font::bold) != 0, + (styleFlags & Font::italic) != 0); + } + + static bool isBold (const String& style) noexcept + { + return style.containsWholeWordIgnoreCase ("Bold"); + } + + static bool isItalic (const String& style) noexcept + { + return style.containsWholeWordIgnoreCase ("Italic") + || style.containsWholeWordIgnoreCase ("Oblique"); + } + + static bool isPlaceholderFamilyName (const String& family) + { + return family == Font::getDefaultSansSerifFontName() + || family == Font::getDefaultSerifFontName() + || family == Font::getDefaultMonospacedFontName(); + } + + struct ConcreteFamilyNames + { + ConcreteFamilyNames() + : sans (findName (Font::getDefaultSansSerifFontName())), + serif (findName (Font::getDefaultSerifFontName())), + mono (findName (Font::getDefaultMonospacedFontName())) + { + } + + String lookUp (const String& placeholder) + { + if (placeholder == Font::getDefaultSansSerifFontName()) return sans; + if (placeholder == Font::getDefaultSerifFontName()) return serif; + if (placeholder == Font::getDefaultMonospacedFontName()) return mono; + + return findName (placeholder); + } + + private: + static String findName (const String& placeholder) + { + const Font f (placeholder, Font::getDefaultStyle(), 15.0f); + return Font::getDefaultTypefaceForFont (f)->getName(); + } + + String sans, serif, mono; + }; + + static String getConcreteFamilyNameFromPlaceholder (const String& placeholder) + { + static ConcreteFamilyNames names; + return names.lookUp (placeholder); + } + + static String getConcreteFamilyName (const Font& font) + { + const String& family = font.getTypefaceName(); + + return isPlaceholderFamilyName (family) ? getConcreteFamilyNameFromPlaceholder (family) + : family; + } +}; + +//============================================================================== +Typeface::Typeface (const String& faceName, const String& styleName) noexcept + : name (faceName), style (styleName) { } @@ -34,17 +112,149 @@ Typeface::~Typeface() Typeface::Ptr Typeface::getFallbackTypeface() { - const Font fallbackFont (Font::getFallbackFontName(), 10, 0); + const Font fallbackFont (Font::getFallbackFontName(), Font::getFallbackFontStyle(), 10.0f); return fallbackFont.getTypeface(); } -EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) +EdgeTable* Typeface::getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight) { Path path; if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) + { + applyVerticalHintingTransform (fontHeight, path); + return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), path, transform); + } return nullptr; } + +//============================================================================== +struct Typeface::HintingParams +{ + HintingParams (Typeface& t) + : cachedSize (0), top (0), middle (0), bottom (0) + { + Font font (&t); + font = font.withHeight ((float) standardHeight); + + top = getAverageY (font, "BDEFPRTZOQ", true); + middle = getAverageY (font, "acegmnopqrsuvwxy", true); + bottom = getAverageY (font, "BDELZOC", false); + } + + void applyVerticalHintingTransform (float fontSize, Path& path) + { + if (cachedSize != fontSize) + { + cachedSize = fontSize; + cachedScale = Scaling (top, middle, bottom, fontSize); + } + + if (bottom < top + 3.0f / fontSize) + return; + + Path result; + + for (Path::Iterator i (path); i.next();) + { + switch (i.elementType) + { + case Path::Iterator::startNewSubPath: result.startNewSubPath (i.x1, cachedScale.apply (i.y1)); break; + case Path::Iterator::lineTo: result.lineTo (i.x1, cachedScale.apply (i.y1)); break; + case Path::Iterator::quadraticTo: result.quadraticTo (i.x1, cachedScale.apply (i.y1), + i.x2, cachedScale.apply (i.y2)); break; + case Path::Iterator::cubicTo: result.cubicTo (i.x1, cachedScale.apply (i.y1), + i.x2, cachedScale.apply (i.y2), + i.x3, cachedScale.apply (i.y3)); break; + case Path::Iterator::closePath: result.closeSubPath(); break; + default: jassertfalse; break; + } + } + + result.swapWithPath (path); + } + +private: + struct Scaling + { + Scaling() noexcept : middle(), upperScale(), upperOffset(), lowerScale(), lowerOffset() {} + + Scaling (float t, float m, float b, float fontSize) noexcept : middle (m) + { + const float newT = std::floor (fontSize * t + 0.5f) / fontSize; + const float newB = std::floor (fontSize * b + 0.5f) / fontSize; + const float newM = std::floor (fontSize * m + 0.3f) / fontSize; // this is slightly biased so that lower-case letters + // are more likely to become taller than shorter. + upperScale = jlimit (0.9f, 1.1f, (newM - newT) / (m - t)); + lowerScale = jlimit (0.9f, 1.1f, (newB - newM) / (b - m)); + + upperOffset = newM - m * upperScale; + lowerOffset = newB - b * lowerScale; + } + + float apply (float y) const noexcept + { + return y < middle ? (y * upperScale + upperOffset) + : (y * lowerScale + lowerOffset); + } + + float middle, upperScale, upperOffset, lowerScale, lowerOffset; + }; + + float cachedSize; + Scaling cachedScale; + + static float getAverageY (const Font& font, const char* chars, bool getTop) + { + GlyphArrangement ga; + ga.addLineOfText (font, chars, 0, 0); + + Array y; + DefaultElementComparator sorter; + + for (int i = 0; i < ga.getNumGlyphs(); ++i) + { + Path p; + ga.getGlyph (i).createPath (p); + Rectangle bounds (p.getBounds()); + + if (! p.isEmpty()) + y.addSorted (sorter, getTop ? bounds.getY() : bounds.getBottom()); + } + + float median = y[y.size() / 2]; + + float total = 0; + int num = 0; + + for (int i = 0; i < y.size(); ++i) + { + if (std::abs (median - y.getUnchecked(i)) < 0.05f * (float) standardHeight) + { + total += y.getUnchecked(i); + ++num; + } + } + + return num < 4 ? 0.0f : total / (num * (float) standardHeight); + } + + enum { standardHeight = 100 }; + float top, middle, bottom; +}; + +void Typeface::applyVerticalHintingTransform (float fontSize, Path& path) +{ + if (fontSize > 3.0f && fontSize < 25.0f) + { + ScopedLock sl (hintingLock); + + if (hintingParams == nullptr) + hintingParams = new HintingParams (*this); + + return hintingParams->applyVerticalHintingTransform (fontSize, path); + } +} diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h index 507c5e1..a392fb7 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TYPEFACE_JUCEHEADER__ -#define __JUCE_TYPEFACE_JUCEHEADER__ - -class Path; -class Font; -class EdgeTable; -class AffineTransform; +#ifndef JUCE_TYPEFACE_H_INCLUDED +#define JUCE_TYPEFACE_H_INCLUDED //============================================================================== @@ -47,7 +41,7 @@ class AffineTransform; @see CustomTypeface, Font */ -class JUCE_API Typeface : public SingleThreadedReferenceCountedObject +class JUCE_API Typeface : public ReferenceCountedObject { public: //============================================================================== @@ -55,15 +49,27 @@ public: typedef ReferenceCountedObjectPtr Ptr; //============================================================================== - /** Returns the name of the typeface. + /** Returns the font family of the typeface. @see Font::getTypefaceName */ const String& getName() const noexcept { return name; } + //============================================================================== + /** Returns the font style of the typeface. + @see Font::getTypefaceStyle + */ + const String& getStyle() const noexcept { return style; } + //============================================================================== /** Creates a new system typeface. */ static Ptr createSystemTypefaceFor (const Font& font); + /** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image). + The system will take its own internal copy of the data, so you can free the block once + this method has returned. + */ + static Ptr createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize); + //============================================================================== /** Destructor. */ virtual ~Typeface(); @@ -88,30 +94,30 @@ public: */ virtual float getDescent() const = 0; + /** Returns the value by which you should multiply a juce font-height value to + convert it to the equivalent point-size. + */ + virtual float getHeightToPointsFactor() 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; /** Returns a new EdgeTable that contains the path for the givem glyph, with the specified transform applied. */ - virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform); + virtual EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform, float fontHeight); /** Returns true if the typeface uses hinting. */ virtual bool isHinted() const { return false; } @@ -120,17 +126,36 @@ public: /** Changes the number of fonts that are cached in memory. */ static void setTypefaceCacheSize (int numFontsToCache); + /** Clears any fonts that are currently cached in memory. */ + static void clearTypefaceCache(); + + /** On some platforms, this allows a specific path to be scanned. + Currently only available when using FreeType. + */ + static void scanFolderForFonts (const File& folder); + + /** Makes an attempt at performing a good overall distortion that will scale a font of + the given size to align vertically with the pixel grid. The path should be an unscaled + (i.e. normalised to height of 1.0) path for a glyph. + */ + void applyVerticalHintingTransform (float fontHeight, Path& path); + protected: //============================================================================== - String name; + String name, style; - explicit Typeface (const String& name) noexcept; + Typeface (const String& name, const String& style) noexcept; static Ptr getFallbackTypeface(); private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Typeface); + struct HintingParams; + friend struct ContainerDeletePolicy; + ScopedPointer hintingParams; + CriticalSection hintingLock; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Typeface) }; -#endif // __JUCE_TYPEFACE_JUCEHEADER__ +#endif // JUCE_TYPEFACE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp index 3acd1e7..59ebde3 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -35,10 +34,10 @@ AffineTransform::AffineTransform (const AffineTransform& other) noexcept { } -AffineTransform::AffineTransform (const float mat00_, const float mat01_, const float mat02_, - const float mat10_, const float mat11_, const float mat12_) noexcept - : mat00 (mat00_), mat01 (mat01_), mat02 (mat02_), - mat10 (mat10_), mat11 (mat11_), mat12 (mat12_) +AffineTransform::AffineTransform (const float m00, const float m01, const float m02, + const float m10, const float m11, const float m12) noexcept + : mat00 (m00), mat01 (m01), mat02 (m02), + mat10 (m10), mat11 (m11), mat12 (m12) { } @@ -105,6 +104,12 @@ AffineTransform AffineTransform::translation (const float dx, const float dy) no 0, 1.0f, dy); } +AffineTransform AffineTransform::withAbsoluteTranslation (const float tx, const float ty) const noexcept +{ + return AffineTransform (mat00, mat01, tx, + mat10, mat11, ty); +} + AffineTransform AffineTransform::rotated (const float rad) const noexcept { const float cosRad = std::cos (rad); @@ -147,10 +152,20 @@ AffineTransform AffineTransform::scaled (const float factorX, const float factor factorY * mat10, factorY * mat11, factorY * mat12); } +AffineTransform AffineTransform::scaled (const float factor) const noexcept +{ + return AffineTransform (factor * mat00, factor * mat01, factor * mat02, + factor * mat10, factor * mat11, factor * mat12); +} + AffineTransform AffineTransform::scale (const float factorX, const float factorY) noexcept { - return AffineTransform (factorX, 0, 0, - 0, factorY, 0); + return AffineTransform (factorX, 0, 0, 0, factorY, 0); +} + +AffineTransform AffineTransform::scale (const float factor) noexcept +{ + return AffineTransform (factor, 0, 0, 0, factor, 0); } AffineTransform AffineTransform::scaled (const float factorX, const float factorY, @@ -243,5 +258,5 @@ bool AffineTransform::isOnlyTranslation() const noexcept float AffineTransform::getScaleFactor() const noexcept { - return juce_hypot (mat00 + mat01, mat10 + mat11); + return (mat00 + mat11) / 2.0f; } diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h index 984c992..dfc3a29 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_AffineTransform.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ -#define __JUCE_AFFINETRANSFORM_JUCEHEADER__ +#ifndef JUCE_AFFINETRANSFORM_H_INCLUDED +#define JUCE_AFFINETRANSFORM_H_INCLUDED //============================================================================== @@ -74,13 +73,12 @@ public: 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. */ + /** Transforms a 2D coordinate using this matrix. */ template void transformPoint (ValueType& x, ValueType& y) const noexcept { @@ -89,7 +87,7 @@ public: y = static_cast (mat10 * oldX + mat11 * y + mat12); } - /** Transforms two 2D co-ordinates using this matrix. + /** Transforms two 2D coordinates using this matrix. This is just a shortcut for calling transformPoint() on each of these pairs of coordinates in turn. (And putting all the calculations into one function hopefully also gives the compiler a bit more scope for pipelining it). @@ -105,7 +103,7 @@ public: y2 = static_cast (mat10 * oldX2 + mat11 * y2 + mat12); } - /** Transforms three 2D co-ordinates using this matrix. + /** Transforms three 2D coordinates using this matrix. This is just a shortcut for calling transformPoint() on each of these pairs of coordinates in turn. (And putting all the calculations into one function hopefully also gives the compiler a bit more scope for pipelining it). @@ -129,10 +127,28 @@ public: AffineTransform translated (float deltaX, float deltaY) const noexcept; + /** Returns a new transform which is the same as this one followed by a translation. */ + template + AffineTransform translated (PointType delta) const noexcept + { + return translated ((float) delta.x, (float) delta.y); + } + /** Returns a new transform which is a translation. */ static AffineTransform translation (float deltaX, float deltaY) noexcept; + /** Returns a new transform which is a translation. */ + template + static AffineTransform translation (PointType delta) noexcept + { + return translation ((float) delta.x, (float) delta.y); + } + + /** Returns a copy of this transform with the specified translation matrix values. */ + AffineTransform withAbsoluteTranslation (float translationX, + float translationY) const noexcept; + /** 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 @@ -143,7 +159,7 @@ public: /** 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. + the coordinates passed in. */ AffineTransform rotated (float angleInRadians, float pivotX, @@ -163,6 +179,11 @@ public: AffineTransform scaled (float factorX, float factorY) const noexcept; + /** Returns a transform which is the same as this one followed by a re-scaling. + The scaling is centred around the origin (0, 0). + */ + AffineTransform scaled (float factor) const noexcept; + /** Returns a transform which is the same as this one followed by a re-scaling. The scaling is centred around the origin provided. */ @@ -173,6 +194,9 @@ public: static AffineTransform scale (float factorX, float factorY) noexcept; + /** Returns a new transform which is a re-scale about the origin. */ + static AffineTransform scale (float factor) noexcept; + /** Returns a new transform which is a re-scale centred around the point provided. */ static AffineTransform scale (float factorX, float factorY, float pivotX, float pivotY) noexcept; @@ -185,7 +209,7 @@ public: /** Returns a shear transform, centred around the origin (0, 0). */ static AffineTransform shear (float shearX, float shearY) noexcept; - /** Returns a transform that will flip co-ordinates vertically within a window of the given height. + /** Returns a transform that will flip coordinates vertically within a window of the given height. This is handy for converting between upside-down coordinate systems such as OpenGL or CoreGraphics. */ static AffineTransform verticalFlip (float height) noexcept; @@ -256,7 +280,7 @@ public: private: //============================================================================== - JUCE_LEAK_DETECTOR (AffineTransform); + JUCE_LEAK_DETECTOR (AffineTransform) }; -#endif // __JUCE_AFFINETRANSFORM_JUCEHEADER__ +#endif // JUCE_AFFINETRANSFORM_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_BorderSize.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_BorderSize.h index 0fa9e12..b6a6686 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_BorderSize.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_BorderSize.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_BORDERSIZE_JUCEHEADER__ -#define __JUCE_BORDERSIZE_JUCEHEADER__ - -#include "juce_Rectangle.h" +#ifndef JUCE_BORDERSIZE_H_INCLUDED +#define JUCE_BORDERSIZE_H_INCLUDED //============================================================================== @@ -150,9 +147,7 @@ public: private: //============================================================================== ValueType top, left, bottom, right; - - JUCE_LEAK_DETECTOR (BorderSize); }; -#endif // __JUCE_BORDERSIZE_JUCEHEADER__ +#endif // JUCE_BORDERSIZE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp index 4882390..73ee1c3 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,14 +25,14 @@ const int juce_edgeTableDefaultEdgesPerLine = 32; //============================================================================== -EdgeTable::EdgeTable (const Rectangle& bounds_, +EdgeTable::EdgeTable (const Rectangle& area, const Path& path, const AffineTransform& transform) - : bounds (bounds_), + : bounds (area), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), - needToCheckEmptinesss (true) + lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), + needToCheckEmptiness (true) { - table.malloc ((size_t) ((bounds.getHeight() + 1) * lineStrideElements)); + allocate(); int* t = table; for (int i = bounds.getHeight(); --i >= 0;) @@ -104,10 +103,10 @@ EdgeTable::EdgeTable (const Rectangle& bounds_, EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) : bounds (rectangleToAdd), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), - needToCheckEmptinesss (true) + lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), + needToCheckEmptiness (true) { - table.malloc ((size_t) jmax (1, bounds.getHeight()) * lineStrideElements); + allocate(); table[0] = 0; const int x1 = rectangleToAdd.getX() << 8; @@ -125,34 +124,65 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) } } -EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) +EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) : bounds (rectanglesToAdd.getBounds()), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), - lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), - needToCheckEmptinesss (true) + lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1), + needToCheckEmptiness (true) { - table.malloc ((size_t) jmax (1, bounds.getHeight()) * lineStrideElements); + allocate(); + clearLineSizes(); - int* t = table; - for (int i = bounds.getHeight(); --i >= 0;) + for (const Rectangle* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r) { - *t = 0; - t += lineStrideElements; - } - - for (RectangleList::Iterator iter (rectanglesToAdd); iter.next();) - { - const Rectangle* const r = iter.getRectangle(); - const int x1 = r->getX() << 8; const int x2 = r->getRight() << 8; int y = r->getY() - bounds.getY(); for (int j = r->getHeight(); --j >= 0;) + addEdgePointPair (x1, x2, y++, 255); + } + + sanitiseLevels (true); +} + +EdgeTable::EdgeTable (const RectangleList& rectanglesToAdd) + : bounds (rectanglesToAdd.getBounds().getSmallestIntegerContainer()), + maxEdgesPerLine (rectanglesToAdd.getNumRectangles() * 2), + lineStrideElements (rectanglesToAdd.getNumRectangles() * 4 + 1), + needToCheckEmptiness (true) +{ + bounds.setHeight (bounds.getHeight() + 1); + allocate(); + clearLineSizes(); + + for (const Rectangle* r = rectanglesToAdd.begin(), * const e = rectanglesToAdd.end(); r != e; ++r) + { + const int x1 = roundToInt (r->getX() * 256.0f); + const int x2 = roundToInt (r->getRight() * 256.0f); + + const int y1 = roundToInt (r->getY() * 256.0f) - (bounds.getY() << 8); + const int y2 = roundToInt (r->getBottom() * 256.0f) - (bounds.getY() << 8); + + if (x2 <= x1 || y2 <= y1) + continue; + + int y = y1 >> 8; + const int lastLine = y2 >> 8; + + if (y == lastLine) { - addEdgePoint (x1, y, 255); - addEdgePoint (x2, y, -255); - ++y; + addEdgePointPair (x1, x2, y, y2 - y1); + } + else + { + addEdgePointPair (x1, x2, y++, 255 - (y1 & 255)); + + while (y < lastLine) + addEdgePointPair (x1, x2, y++, 255); + + jassert (y < bounds.getHeight()); + addEdgePointPair (x1, x2, y, y2 & 255); } } @@ -166,10 +196,10 @@ EdgeTable::EdgeTable (const Rectangle& rectangleToAdd) 2 + (int) rectangleToAdd.getHeight())), maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine), lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1), - needToCheckEmptinesss (true) + needToCheckEmptiness (true) { jassert (! rectangleToAdd.isEmpty()); - table.malloc ((size_t) jmax (1, bounds.getHeight()) * lineStrideElements); + allocate(); table[0] = 0; const int x1 = roundToInt (rectangleToAdd.getX() * 256.0f); @@ -247,9 +277,9 @@ EdgeTable& EdgeTable::operator= (const EdgeTable& other) bounds = other.bounds; maxEdgesPerLine = other.maxEdgesPerLine; lineStrideElements = other.lineStrideElements; - needToCheckEmptinesss = other.needToCheckEmptinesss; + needToCheckEmptiness = other.needToCheckEmptiness; - table.malloc ((size_t) jmax (1, bounds.getHeight()) * lineStrideElements); + allocate(); copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight()); return *this; } @@ -259,11 +289,32 @@ EdgeTable::~EdgeTable() } //============================================================================== +static size_t getEdgeTableAllocationSize (int lineStride, int height) noexcept +{ + // (leave an extra line at the end for use as scratch space) + return (size_t) (lineStride * (2 + jmax (0, height))); +} + +void EdgeTable::allocate() +{ + table.malloc (getEdgeTableAllocationSize (lineStrideElements, bounds.getHeight())); +} + +void EdgeTable::clearLineSizes() noexcept +{ + int* t = table; + for (int i = bounds.getHeight(); --i >= 0;) + { + *t = 0; + t += lineStrideElements; + } +} + void EdgeTable::copyEdgeTableData (int* dest, const int destLineStride, const int* src, const int srcLineStride, int numLines) noexcept { while (--numLines >= 0) { - memcpy (dest, src, (src[0] * 2 + 1) * sizeof (int)); + memcpy (dest, src, (size_t) (src[0] * 2 + 1) * sizeof (int)); src += srcLineStride; dest += destLineStride; } @@ -274,49 +325,61 @@ void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept // Convert the table from relative windings to absolute levels.. int* lineStart = table; - for (int i = bounds.getHeight(); --i >= 0;) + for (int y = bounds.getHeight(); --y >= 0;) { - int* line = lineStart; - lineStart += lineStrideElements; + int num = lineStart[0]; - int num = *line; - if (num == 0) - continue; - - int level = 0; - - if (useNonZeroWinding) + if (num > 0) { - while (--num > 0) - { - line += 2; - level += *line; - int corrected = abs (level); - if (corrected >> 8) - corrected = 255; + LineItem* items = reinterpret_cast (lineStart + 1); + LineItem* const itemsEnd = items + num; - *line = corrected; - } - } - else - { - while (--num > 0) + // sort the X coords + std::sort (items, itemsEnd); + + const LineItem* src = items; + int correctedNum = num; + int level = 0; + + while (src < itemsEnd) { - line += 2; - level += *line; - int corrected = abs (level); - if (corrected >> 8) + level += src->level; + const int x = src->x; + ++src; + + while (src < itemsEnd && src->x == x) { - corrected &= 511; - if (corrected >> 8) - corrected = 511 - corrected; + level += src->level; + ++src; + --correctedNum; } - *line = corrected; + int corrected = std::abs (level); + + if (corrected >> 8) + { + if (useNonZeroWinding) + { + corrected = 255; + } + else + { + corrected &= 511; + if (corrected >> 8) + corrected = 511 - corrected; + } + } + + items->x = x; + items->level = corrected; + ++items; } + + lineStart[0] = correctedNum; + (items - 1)->level = 0; // force the last level to 0, just in case something went wrong in creating the table } - line[2] = 0; // force the last level to 0, just in case something went wrong in creating the table + lineStart += lineStrideElements; } } @@ -329,7 +392,7 @@ void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine) jassert (bounds.getHeight() > 0); const int newLineStrideElements = maxEdgesPerLine * 2 + 1; - HeapBlock newTable ((size_t) (bounds.getHeight() * newLineStrideElements)); + HeapBlock newTable (getEdgeTableAllocationSize (newLineStrideElements, bounds.getHeight())); copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight()); @@ -354,41 +417,40 @@ void EdgeTable::addEdgePoint (const int x, const int y, const int winding) int* line = table + lineStrideElements * y; const int numPoints = line[0]; - int n = numPoints << 1; - if (n > 0) + if (numPoints >= maxEdgesPerLine) { - while (n > 0) - { - const int cx = line [n - 1]; - - if (cx <= x) - { - if (cx == x) - { - line [n] += winding; - return; - } - - break; - } - - n -= 2; - } - - if (numPoints >= maxEdgesPerLine) - { - remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); - jassert (numPoints < maxEdgesPerLine); - line = table + lineStrideElements * y; - } - - memmove (line + (n + 3), line + (n + 1), sizeof (int) * ((numPoints << 1) - n)); + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + jassert (numPoints < maxEdgesPerLine); + line = table + lineStrideElements * y; } + line[0]++; + int n = numPoints << 1; line [n + 1] = x; line [n + 2] = winding; - line[0]++; +} + +void EdgeTable::addEdgePointPair (int x1, int x2, int y, int winding) +{ + jassert (y >= 0 && y < bounds.getHeight()); + + int* line = table + lineStrideElements * y; + const int numPoints = line[0]; + + if (numPoints + 1 >= maxEdgesPerLine) + { + remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); + jassert (numPoints < maxEdgesPerLine); + line = table + lineStrideElements * y; + } + + line[0] = numPoints + 2; + line += numPoints << 1; + line[1] = x1; + line[2] = winding; + line[3] = x2; + line[4] = -winding; } void EdgeTable::translate (float dx, const int dy) noexcept @@ -412,18 +474,40 @@ void EdgeTable::translate (float dx, const int dy) noexcept } } -void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) +void EdgeTable::multiplyLevels (float amount) +{ + int* lineStart = table; + const int multiplier = (int) (amount * 256.0f); + + for (int y = 0; y < bounds.getHeight(); ++y) + { + int numPoints = lineStart[0]; + LineItem* item = reinterpret_cast (lineStart + 1); + lineStart += lineStrideElements; + + while (--numPoints > 0) + { + item->level = jmin (255, (item->level * multiplier) >> 8); + ++item; + } + } +} + +void EdgeTable::intersectWithEdgeTableLine (const int y, const int* const otherLine) { jassert (y >= 0 && y < bounds.getHeight()); - int* dest = table + lineStrideElements * y; - if (dest[0] == 0) + int* srcLine = table + lineStrideElements * y; + int srcNum1 = *srcLine; + + if (srcNum1 == 0) return; - int otherNumPoints = *otherLine; - if (otherNumPoints == 0) + int srcNum2 = *otherLine; + + if (srcNum2 == 0) { - *dest = 0; + *srcLine = 0; return; } @@ -431,23 +515,18 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) // optimise for the common case where our line lies entirely within a // single pair of points, as happens when clipping to a simple rect. - if (otherNumPoints == 2 && otherLine[2] >= 255) + if (srcNum2 == 2 && otherLine[2] >= 255) { - clipEdgeTableLineToRange (dest, otherLine[1], jmin (right, otherLine[3])); + clipEdgeTableLineToRange (srcLine, otherLine[1], jmin (right, otherLine[3])); return; } - ++otherLine; - const size_t lineSizeBytes = (dest[0] * 2 + 1) * sizeof (int); - int* temp = static_cast (alloca (lineSizeBytes)); - memcpy (temp, dest, lineSizeBytes); + bool isUsingTempSpace = false; - const int* src1 = temp; - int srcNum1 = *src1++; + const int* src1 = srcLine + 1; int x1 = *src1++; - const int* src2 = otherLine; - int srcNum2 = otherNumPoints; + const int* src2 = otherLine + 1; int x2 = *src2++; int destIndex = 0, destTotal = 0; @@ -458,23 +537,20 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) { int nextX; - if (x1 < x2) + if (x1 <= x2) { + if (x1 == x2) + { + level2 = *src2++; + x2 = *src2++; + --srcNum2; + } + nextX = x1; level1 = *src1++; x1 = *src1++; --srcNum1; } - else if (x1 == x2) - { - nextX = x1; - level1 = *src1++; - level2 = *src2++; - x1 = *src1++; - x2 = *src2++; - --srcNum1; - --srcNum2; - } else { nextX = x2; @@ -497,15 +573,41 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) { if (destTotal >= maxEdgesPerLine) { - dest[0] = destTotal; - remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); - dest = table + lineStrideElements * y; + srcLine[0] = destTotal; + + if (isUsingTempSpace) + { + const size_t tempSize = (size_t) srcNum1 * 2 * sizeof (int); + int* const oldTemp = static_cast (alloca (tempSize)); + memcpy (oldTemp, src1, tempSize); + + remapTableForNumEdges (jmax (256, destTotal * 2)); + srcLine = table + lineStrideElements * y; + + int* const newTemp = table + lineStrideElements * bounds.getHeight(); + memcpy (newTemp, oldTemp, tempSize); + src1 = newTemp; + } + else + { + remapTableForNumEdges (jmax (256, destTotal * 2)); + srcLine = table + lineStrideElements * y; + } } ++destTotal; lastLevel = nextLevel; - dest[++destIndex] = nextX; - dest[++destIndex] = nextLevel; + + if (! isUsingTempSpace) + { + isUsingTempSpace = true; + int* const temp = table + lineStrideElements * bounds.getHeight(); + memcpy (temp, src1, (size_t) srcNum1 * 2 * sizeof (int)); + src1 = temp; + } + + srcLine[++destIndex] = nextX; + srcLine[++destIndex] = nextLevel; } } } @@ -514,28 +616,17 @@ void EdgeTable::intersectWithEdgeTableLine (const int y, const int* otherLine) { if (destTotal >= maxEdgesPerLine) { - dest[0] = destTotal; - remapTableForNumEdges (maxEdgesPerLine + juce_edgeTableDefaultEdgesPerLine); - dest = table + lineStrideElements * y; + srcLine[0] = destTotal; + remapTableForNumEdges (jmax (256, destTotal * 2)); + srcLine = table + lineStrideElements * y; } ++destTotal; - dest[++destIndex] = right; - dest[++destIndex] = 0; + srcLine[++destIndex] = right; + srcLine[++destIndex] = 0; } - dest[0] = destTotal; - -#if JUCE_DEBUG - int last = std::numeric_limits::min(); - for (int i = 0; i < dest[0]; ++i) - { - jassert (dest[i * 2 + 1] > last); - last = dest[i * 2 + 1]; - } - - jassert (dest [dest[0] * 2] == 0); -#endif + srcLine[0] = destTotal; } void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) noexcept @@ -570,7 +661,7 @@ void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) if (itemsRemoved > 0) { dest[0] -= itemsRemoved; - memmove (dest + 1, lastItem, dest[0] * (sizeof (int) * 2)); + memmove (dest + 1, lastItem, (size_t) dest[0] * (sizeof (int) * 2)); } dest[1] = x1; @@ -585,7 +676,7 @@ void EdgeTable::clipToRectangle (const Rectangle& r) if (clipped.isEmpty()) { - needToCheckEmptinesss = false; + needToCheckEmptiness = false; bounds.setHeight (0); } else @@ -614,7 +705,7 @@ void EdgeTable::clipToRectangle (const Rectangle& r) } } - needToCheckEmptinesss = true; + needToCheckEmptiness = true; } } @@ -635,7 +726,7 @@ void EdgeTable::excludeRectangle (const Rectangle& r) for (int i = top; i < bottom; ++i) intersectWithEdgeTableLine (i, rectLine); - needToCheckEmptinesss = true; + needToCheckEmptiness = true; } } @@ -645,7 +736,7 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) if (clipped.isEmpty()) { - needToCheckEmptinesss = false; + needToCheckEmptiness = false; bounds.setHeight (0); } else @@ -659,19 +750,18 @@ void EdgeTable::clipToEdgeTable (const EdgeTable& other) if (clipped.getRight() < bounds.getRight()) bounds.setRight (clipped.getRight()); - int i = 0; - for (i = top; --i >= 0;) + for (int i = 0; i < top; ++i) table [lineStrideElements * i] = 0; const int* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY()); - for (i = top; i < bottom; ++i) + for (int i = top; i < bottom; ++i) { intersectWithEdgeTableLine (i, otherLine); otherLine += other.lineStrideElements; } - needToCheckEmptinesss = true; + needToCheckEmptiness = true; } } @@ -682,7 +772,7 @@ void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, if (y < 0 || y >= bounds.getHeight()) return; - needToCheckEmptinesss = true; + needToCheckEmptiness = true; if (numPixels <= 0) { @@ -690,7 +780,7 @@ void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, return; } - int* tempLine = static_cast (alloca ((numPixels * 2 + 4) * sizeof (int))); + int* tempLine = static_cast (alloca ((size_t) (numPixels * 2 + 4) * sizeof (int))); int destIndex = 0, lastLevel = 0; while (--numPixels >= 0) @@ -721,9 +811,9 @@ void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, bool EdgeTable::isEmpty() noexcept { - if (needToCheckEmptinesss) + if (needToCheckEmptiness) { - needToCheckEmptinesss = false; + needToCheckEmptiness = false; int* t = table; for (int i = bounds.getHeight(); --i >= 0;) diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h index fd1e625..d8b6d66 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_EdgeTable.h @@ -1,36 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_EDGETABLE_JUCEHEADER__ -#define __JUCE_EDGETABLE_JUCEHEADER__ - -#include "../geometry/juce_AffineTransform.h" -#include "../geometry/juce_Rectangle.h" -#include "../geometry/juce_RectangleList.h" -class Path; -class Image; +#ifndef JUCE_EDGETABLE_H_INCLUDED +#define JUCE_EDGETABLE_H_INCLUDED //============================================================================== @@ -60,16 +53,19 @@ public: explicit EdgeTable (const Rectangle& rectangleToAdd); /** Creates an edge table containing a rectangle list. */ - explicit EdgeTable (const RectangleList& rectanglesToAdd); + explicit EdgeTable (const RectangleList& rectanglesToAdd); + + /** Creates an edge table containing a rectangle list. */ + explicit EdgeTable (const RectangleList& rectanglesToAdd); /** Creates an edge table containing a rectangle. */ explicit EdgeTable (const Rectangle& rectangleToAdd); /** Creates a copy of another edge table. */ - EdgeTable (const EdgeTable& other); + EdgeTable (const EdgeTable&); /** Copies from another edge table. */ - EdgeTable& operator= (const EdgeTable& other); + EdgeTable& operator= (const EdgeTable&); /** Destructor. */ ~EdgeTable(); @@ -77,12 +73,15 @@ public: //============================================================================== void clipToRectangle (const Rectangle& r); void excludeRectangle (const Rectangle& r); - void clipToEdgeTable (const EdgeTable& other); + void clipToEdgeTable (const EdgeTable&); void clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels); bool isEmpty() noexcept; const Rectangle& getMaximumBounds() const noexcept { return bounds; } void translate (float dx, int dy) noexcept; + /** Scales all the alpha-levels in the table by the given multiplier. */ + void multiplyLevels (float factor); + /** Reduces the amount of space the table has allocated. This will shrink the table down to use as little memory as possible - useful for @@ -192,20 +191,30 @@ public: private: //============================================================================== // table line format: number of points; point0 x, point0 levelDelta, point1 x, point1 levelDelta, etc + struct LineItem + { + int x, level; + + bool operator< (const LineItem& other) const noexcept { return x < other.x; } + }; + HeapBlock table; Rectangle bounds; int maxEdgesPerLine, lineStrideElements; - bool needToCheckEmptinesss; + bool needToCheckEmptiness; + void allocate(); + void clearLineSizes() noexcept; void addEdgePoint (int x, int y, int winding); + void addEdgePointPair (int x1, int x2, int y, int winding); void remapTableForNumEdges (int newNumEdgesPerLine); void intersectWithEdgeTableLine (int y, const int* otherLine); void clipEdgeTableLineToRange (int* line, int x1, int x2) noexcept; void sanitiseLevels (bool useNonZeroWinding) noexcept; static void copyEdgeTableData (int* dest, int destLineStride, const int* src, int srcLineStride, int numLines) noexcept; - JUCE_LEAK_DETECTOR (EdgeTable); + JUCE_LEAK_DETECTOR (EdgeTable) }; -#endif // __JUCE_EDGETABLE_JUCEHEADER__ +#endif // JUCE_EDGETABLE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h index 4d86e54..95665c5 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Line.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_LINE_JUCEHEADER__ -#define __JUCE_LINE_JUCEHEADER__ - -#include "juce_Point.h" +#ifndef JUCE_LINE_H_INCLUDED +#define JUCE_LINE_H_INCLUDED //============================================================================== @@ -58,7 +55,7 @@ public: { } - /** Creates a line based on the co-ordinates of its start and end points. */ + /** Creates a line based on the coordinates of its start and end points. */ Line (ValueType startX, ValueType startY, ValueType endX, ValueType endY) noexcept : start (startX, startY), end (endX, endY) @@ -66,8 +63,8 @@ public: } /** Creates a line from its start and end points. */ - Line (const Point& startPoint, - const Point& endPoint) noexcept + Line (const Point startPoint, + const Point endPoint) noexcept : start (startPoint), end (endPoint) { @@ -85,23 +82,23 @@ public: ~Line() noexcept {} //============================================================================== - /** Returns the x co-ordinate of the line's start point. */ + /** Returns the x coordinate of the line's start point. */ inline ValueType getStartX() const noexcept { return start.x; } - /** Returns the y co-ordinate of the line's start point. */ + /** Returns the y coordinate of the line's start point. */ inline ValueType getStartY() const noexcept { return start.y; } - /** Returns the x co-ordinate of the line's end point. */ + /** Returns the x coordinate of the line's end point. */ inline ValueType getEndX() const noexcept { return end.x; } - /** Returns the y co-ordinate of the line's end point. */ + /** Returns the y coordinate of the line's end point. */ inline ValueType getEndY() const noexcept { return end.y; } /** Returns the line's start point. */ - inline const Point& getStart() const noexcept { return start; } + inline Point getStart() const noexcept { return start; } /** Returns the line's end point. */ - inline const Point& getEnd() const noexcept { return end; } + inline Point getEnd() const noexcept { return end; } /** Changes this line's start point */ void setStart (ValueType newStartX, ValueType newStartY) noexcept { start.setXY (newStartX, newStartY); } @@ -110,10 +107,10 @@ public: void setEnd (ValueType newEndX, ValueType newEndY) noexcept { end.setXY (newEndX, newEndY); } /** Changes this line's start point */ - void setStart (const Point& newStart) noexcept { start = newStart; } + void setStart (const Point newStart) noexcept { start = newStart; } /** Changes this line's end point */ - void setEnd (const Point& newEnd) noexcept { end = newEnd; } + void setEnd (const Point newEnd) noexcept { end = newEnd; } /** Returns a line that is the same as this one, but with the start and end reversed, */ const Line reversed() const noexcept { return Line (end, start); } @@ -129,10 +126,10 @@ public: /** Returns the length of the line. */ ValueType getLength() const noexcept { return start.getDistanceFrom (end); } - /** Returns true if the line's start and end x co-ordinates are the same. */ + /** Returns true if the line's start and end x coordinates are the same. */ bool isVertical() const noexcept { return start.x == end.x; } - /** Returns true if the line's start and end y co-ordinates are the same. */ + /** Returns true if the line's start and end y coordinates are the same. */ bool isHorizontal() const noexcept { return start.y == end.y; } /** Returns the line's angle. @@ -142,6 +139,12 @@ public: */ typename Point::FloatType getAngle() const noexcept { return start.getAngleToPoint (end); } + /** Casts this line to float coordinates. */ + Line toFloat() const noexcept { return Line (start.toFloat(), end.toFloat()); } + + /** Casts this line to double coordinates. */ + Line toDouble() const noexcept { return Line (start.toDouble(), end.toDouble()); } + //============================================================================== /** Compares two lines. */ bool operator== (const Line& other) const noexcept { return start == other.start && end == other.end; } @@ -150,23 +153,6 @@ public: bool operator!= (const Line& other) const noexcept { 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 noexcept - { - return findIntersection (start, end, line.start, line.end, intersection); - } - /** Finds the intersection between two lines. @param line the line to intersect with @@ -179,6 +165,30 @@ public: return p; } + /** 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 coordinates returned will still + be valid + */ + bool intersects (const Line& line, Point& intersection) const noexcept + { + return findIntersection (start, end, line.start, line.end, intersection); + } + + /** Returns true if this line intersects another. */ + bool intersects (const Line& other) const noexcept + { + Point ignored; + return findIntersection (start, end, other.start, other.end, ignored); + } + //============================================================================== /** Returns the location of the point which is a given distance along this line. @@ -244,7 +254,7 @@ public: @returns the point's distance from the line @see getPositionAlongLineOfNearestPoint */ - ValueType getDistanceFromPoint (const Point& targetPoint, + ValueType getDistanceFromPoint (const Point targetPoint, Point& pointOnLine) const noexcept { const Point delta (end - start); @@ -285,7 +295,7 @@ public: turn this number into a position, use getPointAlongLineProportionally(). @see getDistanceFromPoint, getPointAlongLineProportionally */ - ValueType findNearestProportionalPositionTo (const Point& point) const noexcept + ValueType findNearestProportionalPositionTo (const Point point) const noexcept { const Point delta (end - start); const double length = delta.x * delta.x + delta.y * delta.y; @@ -299,7 +309,7 @@ public: /** Finds the point on this line which is nearest to a given point. @see getDistanceFromPoint, findNearestProportionalPositionTo */ - Point findNearestPointTo (const Point& point) const noexcept + Point findNearestPointTo (const Point point) const noexcept { return getPointAlongLineProportionally (findNearestProportionalPositionTo (point)); } @@ -310,7 +320,7 @@ public: coordinate of this line at the given x (assuming the line extends infinitely in both directions). */ - bool isPointAbove (const Point& point) const noexcept + bool isPointAbove (const Point point) const noexcept { return start.x != end.x && point.y < ((end.y - start.y) @@ -343,8 +353,8 @@ private: //============================================================================== Point start, end; - static bool findIntersection (const Point& p1, const Point& p2, - const Point& p3, const Point& p4, + static bool findIntersection (const Point p1, const Point p2, + const Point p3, const Point p4, Point& intersection) noexcept { if (p2 == p3) @@ -403,4 +413,4 @@ private: }; -#endif // __JUCE_LINE_JUCEHEADER__ +#endif // JUCE_LINE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp index 177605a..5a6b2f8 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -// tests that some co-ords aren't NaNs +// tests that some coordinates aren't NaNs #define JUCE_CHECK_COORDS_ARE_VALID(x, y) \ jassert (x == x && y == y); @@ -251,10 +250,15 @@ Rectangle Path::getBounds() const noexcept Rectangle Path::getBoundsTransformed (const AffineTransform& transform) const noexcept { - return getBounds().transformed (transform); + return getBounds().transformedBy (transform); } //============================================================================== +void Path::preallocateSpace (int numExtraCoordsToMakeSpaceFor) +{ + data.ensureAllocatedSize ((int) numElements + numExtraCoordsToMakeSpaceFor); +} + void Path::startNewSubPath (const float x, const float y) { JUCE_CHECK_COORDS_ARE_VALID (x, y); @@ -264,14 +268,14 @@ void Path::startNewSubPath (const float x, const float y) else bounds.extend (x, y); - data.ensureAllocatedSize ((int) numElements + 3); + preallocateSpace (3); data.elements [numElements++] = moveMarker; data.elements [numElements++] = x; data.elements [numElements++] = y; } -void Path::startNewSubPath (const Point& start) +void Path::startNewSubPath (const Point start) { startNewSubPath (start.x, start.y); } @@ -283,7 +287,7 @@ void Path::lineTo (const float x, const float y) if (numElements == 0) startNewSubPath (0, 0); - data.ensureAllocatedSize ((int) numElements + 3); + preallocateSpace (3); data.elements [numElements++] = lineMarker; data.elements [numElements++] = x; @@ -292,7 +296,7 @@ void Path::lineTo (const float x, const float y) bounds.extend (x, y); } -void Path::lineTo (const Point& end) +void Path::lineTo (const Point end) { lineTo (end.x, end.y); } @@ -306,7 +310,7 @@ void Path::quadraticTo (const float x1, const float y1, if (numElements == 0) startNewSubPath (0, 0); - data.ensureAllocatedSize ((int) numElements + 5); + preallocateSpace (5); data.elements [numElements++] = quadMarker; data.elements [numElements++] = x1; @@ -317,8 +321,8 @@ void Path::quadraticTo (const float x1, const float y1, bounds.extend (x1, y1, x2, y2); } -void Path::quadraticTo (const Point& controlPoint, - const Point& endPoint) +void Path::quadraticTo (const Point controlPoint, + const Point endPoint) { quadraticTo (controlPoint.x, controlPoint.y, endPoint.x, endPoint.y); @@ -335,7 +339,7 @@ void Path::cubicTo (const float x1, const float y1, if (numElements == 0) startNewSubPath (0, 0); - data.ensureAllocatedSize ((int) numElements + 7); + preallocateSpace (7); data.elements [numElements++] = cubicMarker; data.elements [numElements++] = x1; @@ -349,9 +353,9 @@ void Path::cubicTo (const float x1, const float y1, bounds.extend (x3, y3); } -void Path::cubicTo (const Point& controlPoint1, - const Point& controlPoint2, - const Point& endPoint) +void Path::cubicTo (const Point controlPoint1, + const Point controlPoint2, + const Point endPoint) { cubicTo (controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, @@ -363,7 +367,7 @@ void Path::closeSubPath() if (numElements > 0 && data.elements [numElements - 1] != closeSubPathMarker) { - data.ensureAllocatedSize ((int) numElements + 1); + preallocateSpace (1); data.elements [numElements++] = closeSubPathMarker; } } @@ -400,7 +404,7 @@ void Path::addRectangle (const float x, const float y, if (w < 0) std::swap (x1, x2); if (h < 0) std::swap (y1, y2); - data.ensureAllocatedSize ((int) numElements + 13); + preallocateSpace (13); if (numElements == 0) { @@ -432,10 +436,15 @@ void Path::addRectangle (const float x, const float y, data.elements [numElements++] = closeSubPathMarker; } -void Path::addRoundedRectangle (const float x, const float y, - const float w, const float h, - float csx, - float csy) +void Path::addRoundedRectangle (float x, float y, float w, float h, float csx, float csy) +{ + addRoundedRectangle (x, y, w, h, csx, csy, true, true, true, true); +} + +void Path::addRoundedRectangle (const float x, const float y, const float w, const float h, + float csx, float csy, + const bool curveTopLeft, const bool curveTopRight, + const bool curveBottomLeft, const bool curveBottomRight) { csx = jmin (csx, w * 0.5f); csy = jmin (csy, h * 0.5f); @@ -444,21 +453,50 @@ void Path::addRoundedRectangle (const float x, const float y, const float x2 = x + w; const float y2 = y + h; - startNewSubPath (x + csx, y); - lineTo (x2 - csx, y); - cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy); - lineTo (x2, y2 - csy); - cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2); - lineTo (x + csx, y2); - cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy); - lineTo (x, y + csy); - cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y); + if (curveTopLeft) + { + startNewSubPath (x, y + csy); + cubicTo (x, y + cs45y, x + cs45x, y, x + csx, y); + } + else + { + startNewSubPath (x, y); + } + + if (curveTopRight) + { + lineTo (x2 - csx, y); + cubicTo (x2 - cs45x, y, x2, y + cs45y, x2, y + csy); + } + else + { + lineTo (x2, y); + } + + if (curveBottomRight) + { + lineTo (x2, y2 - csy); + cubicTo (x2, y2 - cs45y, x2 - cs45x, y2, x2 - csx, y2); + } + else + { + lineTo (x2, y2); + } + + if (curveBottomLeft) + { + lineTo (x + csx, y2); + cubicTo (x + cs45x, y2, x, y2 - cs45y, x, y2 - csy); + } + else + { + lineTo (x, y2); + } + closeSubPath(); } -void Path::addRoundedRectangle (const float x, const float y, - const float w, const float h, - float cs) +void Path::addRoundedRectangle (float x, float y, float w, float h, float cs) { addRoundedRectangle (x, y, w, h, cs, cs); } @@ -485,15 +523,19 @@ void Path::addQuadrilateral (const float x1, const float y1, closeSubPath(); } -void Path::addEllipse (const float x, const float y, - const float w, const float h) +void Path::addEllipse (float x, float y, float w, float h) { - const float hw = w * 0.5f; + addEllipse (Rectangle (x, y, w, h)); +} + +void Path::addEllipse (Rectangle area) +{ + const float hw = area.getWidth() * 0.5f; const float hw55 = hw * 0.55f; - const float hh = h * 0.5f; + const float hh = area.getHeight() * 0.5f; const float hh55 = hh * 0.55f; - const float cx = x + hw; - const float cy = y + hh; + const float cx = area.getX() + hw; + const float cy = area.getY() + hh; startNewSubPath (cx, cy - hh); cubicTo (cx + hw55, cy - hh, cx + hw, cy - hh55, cx + hw, cy); @@ -638,7 +680,7 @@ void Path::addArrow (const Line& line, float lineThickness, closeSubPath(); } -void Path::addPolygon (const Point& centre, const int numberOfSides, +void Path::addPolygon (const Point centre, const int numberOfSides, const float radius, const float startAngle) { jassert (numberOfSides > 1); // this would be silly. @@ -662,7 +704,7 @@ void Path::addPolygon (const Point& centre, const int numberOfSides, } } -void Path::addStar (const Point& centre, const int numberOfPoints, +void Path::addStar (const Point centre, const int numberOfPoints, const float innerRadius, const float outerRadius, const float startAngle) { jassert (numberOfPoints > 1); // this would be silly. @@ -688,80 +730,69 @@ void Path::addStar (const Point& centre, const int numberOfPoints, } } -void Path::addBubble (float x, float y, - float w, float h, - float cs, - float tipX, - float tipY, - int whichSide, - float arrowPos, - float arrowWidth) +void Path::addBubble (const Rectangle& bodyArea, + const Rectangle& maximumArea, + const Point arrowTip, + const float cornerSize, + const float arrowBaseWidth) { - if (w > 1.0f && h > 1.0f) + const float halfW = bodyArea.getWidth() / 2.0f; + const float halfH = bodyArea.getHeight() / 2.0f; + const float cornerSizeW = jmin (cornerSize, halfW); + const float cornerSizeH = jmin (cornerSize, halfH); + const float cornerSizeW2 = 2.0f * cornerSizeW; + const float cornerSizeH2 = 2.0f * cornerSizeH; + + startNewSubPath (bodyArea.getX() + cornerSizeW, bodyArea.getY()); + + const Rectangle targetLimit (bodyArea.reduced (jmin (halfW - 1.0f, cornerSizeW + arrowBaseWidth), + jmin (halfH - 1.0f, cornerSizeH + arrowBaseWidth))); + + if (Rectangle (targetLimit.getX(), maximumArea.getY(), + targetLimit.getWidth(), bodyArea.getY() - maximumArea.getY()).contains (arrowTip)) { - cs = jmin (cs, w * 0.5f, h * 0.5f); - const float cs2 = 2.0f * cs; - - startNewSubPath (x + cs, y); - - if (whichSide == 0) - { - const float halfArrowW = jmin (arrowWidth, w - cs2) * 0.5f; - const float arrowX1 = x + cs + jmax (0.0f, (w - cs2 - arrowWidth) * arrowPos - halfArrowW); - lineTo (arrowX1, y); - lineTo (tipX, tipY); - lineTo (arrowX1 + halfArrowW * 2.0f, y); - } - - lineTo (x + w - cs, y); - - if (cs > 0.0f) - addArc (x + w - cs2, y, cs2, cs2, 0, float_Pi * 0.5f); - - if (whichSide == 3) - { - const float halfArrowH = jmin (arrowWidth, h - cs2) * 0.5f; - const float arrowY1 = y + cs + jmax (0.0f, (h - cs2 - arrowWidth) * arrowPos - halfArrowH); - lineTo (x + w, arrowY1); - lineTo (tipX, tipY); - lineTo (x + w, arrowY1 + halfArrowH * 2.0f); - } - - lineTo (x + w, y + h - cs); - - if (cs > 0.0f) - addArc (x + w - cs2, y + h - cs2, cs2, cs2, float_Pi * 0.5f, float_Pi); - - if (whichSide == 2) - { - const float halfArrowW = jmin (arrowWidth, w - cs2) * 0.5f; - const float arrowX1 = x + cs + jmax (0.0f, (w - cs2 - arrowWidth) * arrowPos - halfArrowW); - lineTo (arrowX1 + halfArrowW * 2.0f, y + h); - lineTo (tipX, tipY); - lineTo (arrowX1, y + h); - } - - lineTo (x + cs, y + h); - - if (cs > 0.0f) - addArc (x, y + h - cs2, cs2, cs2, float_Pi, float_Pi * 1.5f); - - if (whichSide == 1) - { - const float halfArrowH = jmin (arrowWidth, h - cs2) * 0.5f; - const float arrowY1 = y + cs + jmax (0.0f, (h - cs2 - arrowWidth) * arrowPos - halfArrowH); - lineTo (x, arrowY1 + halfArrowH * 2.0f); - lineTo (tipX, tipY); - lineTo (x, arrowY1); - } - - lineTo (x, y + cs); - - if (cs > 0.0f) - addArc (x, y, cs2, cs2, float_Pi * 1.5f, float_Pi * 2.0f - PathHelpers::ellipseAngularIncrement); - - closeSubPath(); + lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getY()); + lineTo (arrowTip.x, arrowTip.y); + lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getY()); } + + lineTo (bodyArea.getRight() - cornerSizeW, bodyArea.getY()); + addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getY(), cornerSizeW2, cornerSizeH2, 0, float_Pi * 0.5f); + + if (Rectangle (bodyArea.getRight(), targetLimit.getY(), + maximumArea.getRight() - bodyArea.getRight(), targetLimit.getHeight()).contains (arrowTip)) + { + lineTo (bodyArea.getRight(), arrowTip.y - arrowBaseWidth); + lineTo (arrowTip.x, arrowTip.y); + lineTo (bodyArea.getRight(), arrowTip.y + arrowBaseWidth); + } + + lineTo (bodyArea.getRight(), bodyArea.getBottom() - cornerSizeH); + addArc (bodyArea.getRight() - cornerSizeW2, bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, float_Pi * 0.5f, float_Pi); + + if (Rectangle (targetLimit.getX(), bodyArea.getBottom(), + targetLimit.getWidth(), maximumArea.getBottom() - bodyArea.getBottom()).contains (arrowTip)) + { + lineTo (arrowTip.x + arrowBaseWidth, bodyArea.getBottom()); + lineTo (arrowTip.x, arrowTip.y); + lineTo (arrowTip.x - arrowBaseWidth, bodyArea.getBottom()); + } + + lineTo (bodyArea.getX() + cornerSizeW, bodyArea.getBottom()); + addArc (bodyArea.getX(), bodyArea.getBottom() - cornerSizeH2, cornerSizeW2, cornerSizeH2, float_Pi, float_Pi * 1.5f); + + if (Rectangle (maximumArea.getX(), targetLimit.getY(), + bodyArea.getX() - maximumArea.getX(), targetLimit.getHeight()).contains (arrowTip)) + { + lineTo (bodyArea.getX(), arrowTip.y + arrowBaseWidth); + lineTo (arrowTip.x, arrowTip.y); + lineTo (bodyArea.getX(), arrowTip.y - arrowBaseWidth); + } + + lineTo (bodyArea.getX(), bodyArea.getY() + cornerSizeH); + addArc (bodyArea.getX(), bodyArea.getY(), cornerSizeW2, cornerSizeH2, float_Pi * 1.5f, float_Pi * 2.0f - 0.05f); + + closeSubPath(); } void Path::addPath (const Path& other) @@ -912,20 +943,27 @@ void Path::applyTransform (const AffineTransform& transform) noexcept //============================================================================== +AffineTransform Path::getTransformToScaleToFit (const Rectangle& area, + bool preserveProportions, Justification justification) const +{ + return getTransformToScaleToFit (area.getX(), area.getY(), area.getWidth(), area.getHeight(), + preserveProportions, justification); +} + AffineTransform Path::getTransformToScaleToFit (const float x, const float y, const float w, const float h, const bool preserveProportions, - const Justification& justification) const + Justification justification) const { - Rectangle bounds (getBounds()); + Rectangle boundsRect (getBounds()); if (preserveProportions) { - if (w <= 0 || h <= 0 || bounds.isEmpty()) + if (w <= 0 || h <= 0 || boundsRect.isEmpty()) return AffineTransform::identity; float newW, newH; - const float srcRatio = bounds.getHeight() / bounds.getWidth(); + const float srcRatio = boundsRect.getHeight() / boundsRect.getWidth(); if (srcRatio > h / w) { @@ -949,17 +987,17 @@ AffineTransform Path::getTransformToScaleToFit (const float x, const float y, else if (justification.testFlags (Justification::bottom)) newYCentre += h - newH * 0.5f; else newYCentre += h * 0.5f; - return AffineTransform::translation (bounds.getWidth() * -0.5f - bounds.getX(), - bounds.getHeight() * -0.5f - bounds.getY()) - .scaled (newW / bounds.getWidth(), - newH / bounds.getHeight()) + return AffineTransform::translation (boundsRect.getWidth() * -0.5f - boundsRect.getX(), + boundsRect.getHeight() * -0.5f - boundsRect.getY()) + .scaled (newW / boundsRect.getWidth(), + newH / boundsRect.getHeight()) .translated (newXCentre, newYCentre); } else { - return AffineTransform::translation (-bounds.getX(), -bounds.getY()) - .scaled (w / bounds.getWidth(), - h / bounds.getHeight()) + return AffineTransform::translation (-boundsRect.getX(), -boundsRect.getY()) + .scaled (w / boundsRect.getWidth(), + h / boundsRect.getHeight()) .translated (x, y); } } @@ -996,7 +1034,7 @@ bool Path::contains (const float x, const float y, const float tolerance) const : ((negativeCrossings + positiveCrossings) & 1) != 0; } -bool Path::contains (const Point& point, const float tolerance) const +bool Path::contains (const Point point, const float tolerance) const { return contains (point.x, point.y, tolerance); } @@ -1073,7 +1111,7 @@ Point Path::getPointAlongPath (float distanceFromStart, const AffineTrans return Point (i.x2, i.y2); } -float Path::getNearestPoint (const Point& targetPoint, Point& pointOnPath, +float Path::getNearestPoint (const Point targetPoint, Point& pointOnPath, const AffineTransform& transform) const { PathFlatteningIterator i (*this, transform); @@ -1442,12 +1480,12 @@ String Path::toString() const return s.toUTF8(); } -void Path::restoreFromString (const String& stringVersion) +void Path::restoreFromString (StringRef stringVersion) { clear(); setUsingNonZeroWinding (true); - String::CharPointerType t (stringVersion.getCharPointer()); + String::CharPointerType t (stringVersion.text); juce_wchar marker = 'm'; int numValues = 2; float values [6]; diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h index 1192eb8..ac319f9 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Path.h @@ -1,38 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PATH_JUCEHEADER__ -#define __JUCE_PATH_JUCEHEADER__ - -#include "juce_AffineTransform.h" -#include "juce_Line.h" -#include "juce_Rectangle.h" -#include "../placement/juce_Justification.h" -class Image; -class InputStream; -class OutputStream; +#ifndef JUCE_PATH_H_INCLUDED +#define JUCE_PATH_H_INCLUDED //============================================================================== @@ -76,21 +67,21 @@ public: Path(); /** Creates a copy of another path. */ - Path (const Path& other); + Path (const Path&); /** Destructor. */ ~Path(); /** Copies this path from another one. */ - Path& operator= (const Path& other); + Path& operator= (const Path&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - Path (Path&& other) noexcept; - Path& operator= (Path&& other) noexcept; + Path (Path&&) noexcept; + Path& operator= (Path&&) noexcept; #endif - bool operator== (const Path& other) const noexcept; - bool operator!= (const Path& other) const noexcept; + bool operator== (const Path&) const noexcept; + bool operator!= (const Path&) const noexcept; //============================================================================== /** Returns true if the path doesn't contain any lines or curves. */ @@ -134,7 +125,7 @@ public: @see closeSubPath, setUsingNonZeroWinding */ - bool contains (const Point& point, + bool contains (const Point point, float tolerance = 1.0f) const; /** Checks whether a line crosses the path. @@ -181,7 +172,7 @@ public: This sets pointOnPath to the nearest point, and returns the distance of this point from the start of the path. */ - float getNearestPoint (const Point& targetPoint, + float getNearestPoint (const Point targetPoint, Point& pointOnPath, const AffineTransform& transform = AffineTransform::identity) const; @@ -191,7 +182,7 @@ public: /** Begins a new subpath with a given starting position. - This will move the path's current position to the co-ordinates passed in and + This will move the path's current position to the coordinates 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 @@ -204,7 +195,7 @@ public: /** Begins a new subpath with a given starting position. - This will move the path's current position to the co-ordinates passed in and + This will move the path's current position to the coordinates 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 @@ -213,7 +204,7 @@ public: @see lineTo, quadraticTo, cubicTo, closeSubPath */ - void startNewSubPath (const Point& start); + void startNewSubPath (const Point start); /** Closes a the current sub-path with a line back to its start-point. @@ -249,7 +240,7 @@ public: @see startNewSubPath, quadraticTo, cubicTo, closeSubPath */ - void lineTo (const Point& end); + void lineTo (const Point end); /** Adds a quadratic bezier curve from the shape's last position to a new position. @@ -274,8 +265,8 @@ public: @see startNewSubPath, lineTo, cubicTo, closeSubPath */ - void quadraticTo (const Point& controlPoint, - const Point& endPoint); + void quadraticTo (const Point controlPoint, + const Point endPoint); /** Adds a cubic bezier curve from the shape's last position to a new position. @@ -302,9 +293,9 @@ public: @see startNewSubPath, lineTo, quadraticTo, closeSubPath */ - void cubicTo (const Point& controlPoint1, - const Point& controlPoint2, - const Point& endPoint); + void cubicTo (const Point controlPoint1, + const Point controlPoint2, + const Point endPoint); /** Returns the last point that was added to the path by one of the drawing methods. */ @@ -343,6 +334,15 @@ public: float cornerSizeX, float cornerSizeY); + /** 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, + bool curveTopLeft, bool curveTopRight, + bool curveBottomLeft, bool curveBottomRight); + /** 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 @@ -391,13 +391,17 @@ public: 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 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 (Rectangle area); + /** Adds an elliptical arc to the current path. Note that when specifying the start and end angles, the curve will be drawn either clockwise @@ -503,7 +507,7 @@ public: /** Adds a polygon shape to the path. @see addStar */ - void addPolygon (const Point& centre, + void addPolygon (const Point centre, int numberOfSides, float radius, float startAngle = 0.0f); @@ -511,7 +515,7 @@ public: /** Adds a star shape to the path. @see addPolygon */ - void addStar (const Point& centre, + void addStar (const Point centre, int numberOfPoints, float innerRadius, float outerRadius, @@ -519,26 +523,19 @@ public: /** 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 + @param bodyArea the area of the body of the bubble shape + @param maximumArea an area which encloses the body area and defines the limits within which + the arrow tip can be drawn - if the tip lies outside this area, the bubble + will be drawn without an arrow + @param arrowTipPosition the location of the tip of the arrow + @param cornerSize the size of the rounded corners + @param arrowBaseWidth the width of the base of the arrow where it joins the main rectangle */ - void addBubble (float bodyX, float bodyY, - float bodyW, float bodyH, - float cornerSize, - float arrowTipX, - float arrowTipY, - int whichSide, - float arrowPositionAlongEdgeProportional, - float arrowWidth); + void addBubble (const Rectangle& bodyArea, + const Rectangle& maximumArea, + const Point arrowTipPosition, + const float cornerSize, + const float arrowBaseWidth); /** Adds another path to this one. @@ -565,7 +562,19 @@ public: 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) noexcept; + void swapWithPath (Path&) noexcept; + + //============================================================================== + /** Preallocates enough space for adding the given number of coordinates to the path. + If you're about to add a large number of lines or curves to the path, it can make + the task much more efficient to call this first and avoid costly reallocations + as the structure grows. + The actual value to pass is a bit tricky to calculate because the space required + depends on what you're adding - e.g. each lineTo() or startNewSubPath() will + require 3 coords (x, y and a type marker). Each quadraticTo() will need 5, and + a cubicTo() will require 7. Closing a sub-path will require 1. + */ + void preallocateSpace (int numExtraCoordsToMakeSpaceFor); //============================================================================== /** Applies a 2D transform to all the vertices in the path. @@ -611,7 +620,25 @@ public: */ AffineTransform getTransformToScaleToFit (float x, float y, float width, float height, bool preserveProportions, - const Justification& justificationType = Justification::centred) const; + Justification justificationType = Justification::centred) const; + + /** Returns a transform that can be used to rescale the path to fit into a given space. + + @param area 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 + + */ + AffineTransform getTransformToScaleToFit (const Rectangle& area, + bool preserveProportions, + Justification justificationType = Justification::centred) const; /** Creates a version of this path where all sharp corners have been replaced by curves. @@ -687,7 +714,7 @@ public: const Path& path; size_t index; - JUCE_DECLARE_NON_COPYABLE (Iterator); + JUCE_DECLARE_NON_COPYABLE (Iterator) }; //============================================================================== @@ -729,14 +756,13 @@ public: /** Restores this path from a string that was created with the toString() method. @see toString() */ - void restoreFromString (const String& stringVersion); - + void restoreFromString (StringRef stringVersion); private: //============================================================================== friend class PathFlatteningIterator; friend class Path::Iterator; - ArrayAllocationBase data; + ArrayAllocationBase data; size_t numElements; struct PathBounds @@ -760,7 +786,7 @@ private: static const float cubicMarker; static const float closeSubPathMarker; - JUCE_LEAK_DETECTOR (Path); + JUCE_LEAK_DETECTOR (Path) }; -#endif // __JUCE_PATH_JUCEHEADER__ +#endif // JUCE_PATH_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp index 849708e..0a15581 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -70,48 +69,45 @@ bool PathFlatteningIterator::next() float y3 = 0; float x4 = 0; float y4 = 0; - float type; for (;;) { + float type; + if (stackPos == stackBase) { if (index >= path.numElements) - { return false; - } - else + + type = points [index++]; + + if (type != Path::closeSubPathMarker) { - type = points [index++]; + x2 = points [index++]; + y2 = points [index++]; - if (type != Path::closeSubPathMarker) + if (type == Path::quadMarker) { - x2 = points [index++]; - y2 = points [index++]; + x3 = points [index++]; + y3 = points [index++]; - if (type == Path::quadMarker) - { - x3 = points [index++]; - y3 = points [index++]; + if (! isIdentityTransform) + transform.transformPoints (x2, y2, x3, y3); + } + else if (type == Path::cubicMarker) + { + x3 = points [index++]; + y3 = points [index++]; + x4 = points [index++]; + y4 = points [index++]; - if (! isIdentityTransform) - transform.transformPoints (x2, y2, x3, y3); - } - else if (type == Path::cubicMarker) - { - x3 = points [index++]; - y3 = points [index++]; - x4 = points [index++]; - y4 = points [index++]; - - if (! isIdentityTransform) - transform.transformPoints (x2, y2, x3, y3, x4, y4); - } - else - { - if (! isIdentityTransform) - transform.transformPoint (x2, y2); - } + if (! isIdentityTransform) + transform.transformPoints (x2, y2, x3, y3, x4, y4); + } + else + { + if (! isIdentityTransform) + transform.transformPoint (x2, y2); } } } @@ -151,7 +147,8 @@ bool PathFlatteningIterator::next() return true; } - else if (type == Path::quadMarker) + + if (type == Path::quadMarker) { const size_t offset = (size_t) (stackPos - stackBase); diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.h index 6bb3e86..dcb8b90 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathIterator.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ -#define __JUCE_PATHITERATOR_JUCEHEADER__ - -#include "juce_Path.h" +#ifndef JUCE_PATHITERATOR_H_INCLUDED +#define JUCE_PATHITERATOR_H_INCLUDED //============================================================================== @@ -109,8 +106,8 @@ private: float* stackPos; size_t index, stackSize; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathFlatteningIterator); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathFlatteningIterator) }; -#endif // __JUCE_PATHITERATOR_JUCEHEADER__ +#endif // JUCE_PATHITERATOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp index 3004dbb..bdac333 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.cpp @@ -1,34 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -PathStrokeType::PathStrokeType (const float strokeThickness, - const JointStyle jointStyle_, - const EndCapStyle endStyle_) noexcept - : thickness (strokeThickness), - jointStyle (jointStyle_), - endStyle (endStyle_) +PathStrokeType::PathStrokeType (float strokeThickness) noexcept + : thickness (strokeThickness), jointStyle (mitered), endStyle (butt) +{ +} + +PathStrokeType::PathStrokeType (float strokeThickness, JointStyle joint, EndCapStyle end) noexcept + : thickness (strokeThickness), jointStyle (joint), endStyle (end) { } @@ -455,8 +455,7 @@ namespace PathStrokeHelpers addLineEnd (destPath, endStyle, firstLine.rx2, firstLine.ry2, lastX1, lastY1, width); } - int i; - for (i = 1; i < subPath.size(); ++i) + for (int i = 1; i < subPath.size(); ++i) { const LineSection& l = subPath.getReference (i); @@ -503,7 +502,7 @@ namespace PathStrokeHelpers lastX2 = lastLine.rx2; lastY2 = lastLine.ry2; - for (i = subPath.size() - 1; --i >= 0;) + for (int i = subPath.size() - 1; --i >= 0;) { const LineSection& l = subPath.getReference (i); diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.h index ca23527..6374741 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_PathStrokeType.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_PATHSTROKETYPE_JUCEHEADER__ -#define __JUCE_PATHSTROKETYPE_JUCEHEADER__ - -#include "juce_Path.h" +#ifndef JUCE_PATHSTROKETYPE_H_INCLUDED +#define JUCE_PATHSTROKETYPE_H_INCLUDED //============================================================================== @@ -66,6 +63,9 @@ public: }; //============================================================================== + /** Creates a stroke type with a given line-width, and default joint/end styles. */ + explicit PathStrokeType (float strokeThickness) noexcept; + /** Creates a stroke type. @param strokeThickness the width of the line to use @@ -73,14 +73,14 @@ public: @param endStyle the type of end-caps to use for the ends of open paths. */ PathStrokeType (float strokeThickness, - JointStyle jointStyle = mitered, + JointStyle jointStyle, EndCapStyle endStyle = butt) noexcept; - /** Createes a copy of another stroke type. */ - PathStrokeType (const PathStrokeType& other) noexcept; + /** Creates a copy of another stroke type. */ + PathStrokeType (const PathStrokeType&) noexcept; /** Copies another stroke onto this one. */ - PathStrokeType& operator= (const PathStrokeType& other) noexcept; + PathStrokeType& operator= (const PathStrokeType&) noexcept; /** Destructor. */ ~PathStrokeType() noexcept; @@ -187,10 +187,10 @@ public: //============================================================================== /** Compares the stroke thickness, joint and end styles of two stroke types. */ - bool operator== (const PathStrokeType& other) const noexcept; + bool operator== (const PathStrokeType&) const noexcept; /** Compares the stroke thickness, joint and end styles of two stroke types. */ - bool operator!= (const PathStrokeType& other) const noexcept; + bool operator!= (const PathStrokeType&) const noexcept; private: //============================================================================== @@ -198,7 +198,7 @@ private: JointStyle jointStyle; EndCapStyle endStyle; - JUCE_LEAK_DETECTOR (PathStrokeType); + JUCE_LEAK_DETECTOR (PathStrokeType) }; -#endif // __JUCE_PATHSTROKETYPE_JUCEHEADER__ +#endif // JUCE_PATHSTROKETYPE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h index 1f7b993..ffe80fe 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Point.h @@ -1,37 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_POINT_JUCEHEADER__ -#define __JUCE_POINT_JUCEHEADER__ - -#include "juce_AffineTransform.h" +#ifndef JUCE_POINT_H_INCLUDED +#define JUCE_POINT_H_INCLUDED //============================================================================== /** - A pair of (x, y) co-ordinates. + A pair of (x, y) coordinates. The ValueType template should be a primitive type such as int, float, double, rather than a class. @@ -42,136 +39,179 @@ template class Point { public: - //============================================================================== - /** Creates a point with co-ordinates (0, 0). */ + /** Creates a point at the origin */ Point() noexcept : x(), y() {} /** Creates a copy of another point. */ Point (const Point& other) noexcept : x (other.x), y (other.y) {} /** Creates a point from an (x, y) position. */ - Point (const ValueType initialX, const ValueType initialY) noexcept : x (initialX), y (initialY) {} - - /** Destructor. */ - ~Point() noexcept {} + Point (ValueType initialX, ValueType initialY) noexcept : x (initialX), y (initialY) {} //============================================================================== /** Copies this point from another one. */ - Point& operator= (const Point& other) noexcept { x = other.x; y = other.y; return *this; } + Point& operator= (const Point& other) noexcept { x = other.x; y = other.y; return *this; } - inline bool operator== (const Point& other) const noexcept { return x == other.x && y == other.y; } - inline bool operator!= (const Point& other) const noexcept { return x != other.x || y != other.y; } + inline bool operator== (Point other) const noexcept { return x == other.x && y == other.y; } + inline bool operator!= (Point other) const noexcept { return x != other.x || y != other.y; } /** Returns true if the point is (0, 0). */ - bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); } + bool isOrigin() const noexcept { return x == ValueType() && y == ValueType(); } - /** Returns the point's x co-ordinate. */ - inline ValueType getX() const noexcept { return x; } + /** Returns the point's x coordinate. */ + inline ValueType getX() const noexcept { return x; } - /** Returns the point's y co-ordinate. */ - inline ValueType getY() const noexcept { return y; } + /** Returns the point's y coordinate. */ + inline ValueType getY() const noexcept { return y; } - /** Sets the point's x co-ordinate. */ - inline void setX (const ValueType newX) noexcept { x = newX; } + /** Sets the point's x coordinate. */ + inline void setX (ValueType newX) noexcept { x = newX; } - /** Sets the point's y co-ordinate. */ - inline void setY (const ValueType newY) noexcept { y = newY; } + /** Sets the point's y coordinate. */ + inline void setY (ValueType newY) noexcept { y = newY; } /** Returns a point which has the same Y position as this one, but a new X. */ - Point withX (const ValueType newX) const noexcept { return Point (newX, y); } + Point withX (ValueType newX) const noexcept { return Point (newX, y); } /** Returns a point which has the same X position as this one, but a new Y. */ - Point withY (const ValueType newY) const noexcept { return Point (x, newY); } + Point withY (ValueType newY) const noexcept { return Point (x, newY); } - /** Changes the point's x and y co-ordinates. */ - void setXY (const ValueType newX, const ValueType newY) noexcept { x = newX; y = newY; } + /** Changes the point's x and y coordinates. */ + void setXY (ValueType newX, ValueType newY) noexcept { x = newX; y = newY; } - /** Adds a pair of co-ordinates to this value. */ - void addXY (const ValueType xToAdd, const ValueType yToAdd) noexcept { x += xToAdd; y += yToAdd; } + /** Adds a pair of coordinates to this value. */ + void addXY (ValueType xToAdd, ValueType yToAdd) noexcept { x += xToAdd; y += yToAdd; } + //============================================================================== /** Returns a point with a given offset from this one. */ - Point translated (const ValueType xDelta, const ValueType yDelta) const noexcept { return Point (x + xDelta, y + yDelta); } + Point translated (ValueType deltaX, ValueType deltaY) const noexcept { return Point (x + deltaX, y + deltaY); } - /** Adds two points together. */ - Point operator+ (const Point& other) const noexcept { return Point (x + other.x, y + other.y); } + /** Adds two points together */ + Point operator+ (Point other) const noexcept { return Point (x + other.x, y + other.y); } - /** Adds another point's co-ordinates to this one. */ - Point& operator+= (const Point& other) noexcept { x += other.x; y += other.y; return *this; } + /** Adds another point's coordinates to this one */ + Point& operator+= (Point other) noexcept { x += other.x; y += other.y; return *this; } - /** Subtracts one points from another. */ - Point operator- (const Point& other) const noexcept { return Point (x - other.x, y - other.y); } + /** Subtracts one points from another */ + Point operator- (Point other) const noexcept { return Point (x - other.x, y - other.y); } - /** Subtracts another point's co-ordinates to this one. */ - Point& operator-= (const Point& other) noexcept { x -= other.x; y -= other.y; return *this; } + /** Subtracts another point's coordinates to this one */ + Point& operator-= (Point other) noexcept { x -= other.x; y -= other.y; return *this; } - /** Returns a point whose coordinates are multiplied by a given value. */ - Point operator* (const ValueType multiplier) const noexcept { return Point (x * multiplier, y * multiplier); } + /** Multiplies two points together */ + template + Point operator* (Point other) const noexcept { return Point ((ValueType) (x * other.x), (ValueType) (y * other.y)); } - /** Multiplies the point's co-ordinates by a value. */ - Point& operator*= (const ValueType multiplier) noexcept { x *= multiplier; y *= multiplier; return *this; } + /** Multiplies another point's coordinates to this one */ + template + Point& operator*= (Point other) noexcept { *this = *this * other; return *this; } - /** Returns a point whose coordinates are divided by a given value. */ - Point operator/ (const ValueType divisor) const noexcept { return Point (x / divisor, y / divisor); } + /** Divides one point by another */ + template + Point operator/ (Point other) const noexcept { return Point ((ValueType) (x / other.x), (ValueType) (y / other.y)); } - /** Divides the point's co-ordinates by a value. */ - Point& operator/= (const ValueType divisor) noexcept { x /= divisor; y /= divisor; return *this; } + /** Divides this point's coordinates by another */ + template + Point& operator/= (Point other) noexcept { *this = *this / other; return *this; } + + /** Returns a point whose coordinates are multiplied by a given scalar value. */ + template + Point operator* (FloatType multiplier) const noexcept { return Point ((ValueType) (x * multiplier), (ValueType) (y * multiplier)); } + + /** Returns a point whose coordinates are divided by a given scalar value. */ + template + Point operator/ (FloatType divisor) const noexcept { return Point ((ValueType) (x / divisor), (ValueType) (y / divisor)); } + + /** Multiplies the point's coordinates by a scalar value. */ + template + Point& operator*= (FloatType multiplier) noexcept { x = (ValueType) (x * multiplier); y = (ValueType) (y * multiplier); return *this; } + + /** Divides the point's coordinates by a scalar value. */ + template + Point& operator/= (FloatType divisor) noexcept { x = (ValueType) (x / divisor); y = (ValueType) (y / divisor); return *this; } /** Returns the inverse of this point. */ - Point operator-() const noexcept { return Point (-x, -y); } - - /** Returns the straight-line distance between this point and the origin. */ - ValueType getDistanceFromOrigin() const noexcept { return juce_hypot (x, y); } - - /** Returns the straight-line distance between this point and another one. */ - ValueType getDistanceFrom (const Point& other) const noexcept { return juce_hypot (x - other.x, y - other.y); } + Point operator-() const noexcept { return Point (-x, -y); } + //============================================================================== /** This type will be double if the Point's type is double, otherwise it will be float. */ typedef typename TypeHelpers::SmallestFloatType::type FloatType; + //============================================================================== + /** Returns the straight-line distance between this point and the origin. */ + ValueType getDistanceFromOrigin() const noexcept { return juce_hypot (x, y); } + + /** Returns the straight-line distance between this point and another one. */ + ValueType getDistanceFrom (Point other) const noexcept { return 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 12 o'clock direction, where this point is the centre and the other point is on the circumference. */ - FloatType getAngleToPoint (const Point& other) const noexcept - { return static_cast (std::atan2 (other.x - x, y - other.y)); } + FloatType getAngleToPoint (Point other) const noexcept + { + return static_cast (std::atan2 (static_cast (other.x - x), + static_cast (y - other.y))); + } + + /** Returns the point that would be reached by rotating this point clockwise + about the origin by the specified angle. + */ + Point rotatedAboutOrigin (ValueType angleRadians) const noexcept + { + return Point (x * std::cos (angleRadians) - y * std::sin (angleRadians), + x * std::sin (angleRadians) + y * std::cos (angleRadians)); + } /** Taking this point to be the centre of a circle, this returns a point on its circumference. @param radius the radius of the circle. @param angle the angle of the point, in radians clockwise from the 12 o'clock position. */ - Point getPointOnCircumference (const float radius, const float angle) const noexcept - { return Point (static_cast (x + radius * std::sin (angle)), - static_cast (y - radius * std::cos (angle))); } + Point getPointOnCircumference (float radius, float angle) const noexcept + { + return Point (static_cast (x + radius * std::sin (angle)), + static_cast (y - radius * std::cos (angle))); + } /** Taking this point to be the centre of an ellipse, this returns a point on its circumference. @param radiusX the horizontal radius of the circle. @param radiusY the vertical radius of the circle. @param angle the angle of the point, in radians clockwise from the 12 o'clock position. */ - Point getPointOnCircumference (const float radiusX, const float radiusY, const float angle) const noexcept - { return Point (static_cast (x + radiusX * std::sin (angle)), - static_cast (y - radiusY * std::cos (angle))); } + Point getPointOnCircumference (float radiusX, float radiusY, float angle) const noexcept + { + return Point (static_cast (x + radiusX * std::sin (angle)), + static_cast (y - radiusY * std::cos (angle))); + } - /** Uses a transform to change the point's co-ordinates. + /** Returns the dot-product of two points (x1 * x2 + y1 * y2). */ + FloatType getDotProduct (Point other) const noexcept { return x * other.x + y * other.y; } + + //============================================================================== + /** Uses a transform to change the point's coordinates. This will only compile if ValueType = float! + @see AffineTransform::transformPoint */ void applyTransform (const AffineTransform& transform) noexcept { transform.transformPoint (x, y); } /** Returns the position of this point, if it is transformed by a given AffineTransform. */ Point transformedBy (const AffineTransform& transform) const noexcept - { return Point (transform.mat00 * x + transform.mat01 * y + transform.mat02, - transform.mat10 * x + transform.mat11 * y + transform.mat12); } + { + return Point (static_cast (transform.mat00 * x + transform.mat01 * y + transform.mat02), + static_cast (transform.mat10 * x + transform.mat11 * y + transform.mat12)); + } + //============================================================================== /** Casts this point to a Point object. */ - Point toInt() const noexcept { return Point (static_cast (x), static_cast (y)); } + Point toInt() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Casts this point to a Point object. */ - Point toFloat() const noexcept { return Point (static_cast (x), static_cast (y)); } + Point toFloat() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Casts this point to a Point object. */ - Point toDouble() const noexcept { return Point (static_cast (x), static_cast (y)); } + Point toDouble() const noexcept { return Point (static_cast (x), static_cast (y)); } /** Returns the point as a string in the form "x, y". */ String toString() const { return String (x) + ", " + String (y); } @@ -182,4 +222,4 @@ public: }; -#endif // __JUCE_POINT_JUCEHEADER__ +#endif // JUCE_POINT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h index 9972c35..6191d1a 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_RECTANGLE_JUCEHEADER__ -#define __JUCE_RECTANGLE_JUCEHEADER__ - -#include "juce_Point.h" -class RectangleList; +#ifndef JUCE_RECTANGLE_H_INCLUDED +#define JUCE_RECTANGLE_H_INCLUDED //============================================================================== @@ -42,8 +38,7 @@ class Rectangle public: //============================================================================== /** Creates a rectangle of zero size. - - The default co-ordinates will be (0, 0, 0, 0). + The default coordinates will be (0, 0, 0, 0). */ Rectangle() noexcept : w(), h() @@ -71,7 +66,7 @@ public: } /** Creates a Rectangle from the positions of two opposite corners. */ - Rectangle (const Point& corner1, const Point& corner2) noexcept + Rectangle (const Point corner1, const Point corner2) noexcept : pos (jmin (corner1.x, corner2.x), jmin (corner1.y, corner2.y)), w (corner1.x - corner2.x), @@ -102,13 +97,13 @@ public: ~Rectangle() noexcept {} //============================================================================== - /** Returns true if the rectangle's width and height are both zero or less */ + /** Returns true if the rectangle's width or height are zero or less */ bool isEmpty() const noexcept { return w <= ValueType() || h <= ValueType(); } - /** Returns the x co-ordinate of the rectangle's left-hand-side. */ + /** Returns the x coordinate of the rectangle's left-hand-side. */ inline ValueType getX() const noexcept { return pos.x; } - /** Returns the y co-ordinate of the rectangle's top edge. */ + /** Returns the y coordinate of the rectangle's top edge. */ inline ValueType getY() const noexcept { return pos.y; } /** Returns the width of the rectangle. */ @@ -117,16 +112,16 @@ public: /** Returns the height of the rectangle. */ inline ValueType getHeight() const noexcept { return h; } - /** Returns the x co-ordinate of the rectangle's right-hand-side. */ + /** Returns the x coordinate of the rectangle's right-hand-side. */ inline ValueType getRight() const noexcept { return pos.x + w; } - /** Returns the y co-ordinate of the rectangle's bottom edge. */ + /** Returns the y coordinate of the rectangle's bottom edge. */ inline ValueType getBottom() const noexcept { return pos.y + h; } - /** Returns the x co-ordinate of the rectangle's centre. */ + /** Returns the x coordinate of the rectangle's centre. */ ValueType getCentreX() const noexcept { return pos.x + w / (ValueType) 2; } - /** Returns the y co-ordinate of the rectangle's centre. */ + /** Returns the y coordinate of the rectangle's centre. */ ValueType getCentreY() const noexcept { return pos.y + h / (ValueType) 2; } /** Returns the centre point of the rectangle. */ @@ -140,16 +135,16 @@ public: //============================================================================== /** Returns the rectangle's top-left position as a Point. */ - inline const Point& getPosition() const noexcept { return pos; } + inline Point getPosition() const noexcept { return pos; } /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ - inline void setPosition (const Point& newPos) noexcept { pos = newPos; } + inline void setPosition (const Point newPos) noexcept { pos = newPos; } /** Changes the position of the rectangle's top-left corner (leaving its size unchanged). */ inline void setPosition (const ValueType newX, const ValueType newY) noexcept { pos.setXY (newX, newY); } /** Returns the rectangle's top-left position as a Point. */ - const Point& getTopLeft() const noexcept { return pos; } + Point getTopLeft() const noexcept { return pos; } /** Returns the rectangle's top-right position as a Point. */ Point getTopRight() const noexcept { return Point (pos.x + w, pos.y); } @@ -163,7 +158,7 @@ public: /** Changes the rectangle's size, leaving the position of its top-left corner unchanged. */ void setSize (const ValueType newWidth, const ValueType newHeight) noexcept { w = newWidth; h = newHeight; } - /** Changes all the rectangle's co-ordinates. */ + /** Changes all the rectangle's coordinates. */ void setBounds (const ValueType newX, const ValueType newY, const ValueType newWidth, const ValueType newHeight) noexcept { pos.x = newX; pos.y = newY; w = newWidth; h = newHeight; } @@ -179,6 +174,12 @@ public: /** Changes the rectangle's height */ inline void setHeight (const ValueType newHeight) noexcept { h = newHeight; } + /** Changes the position of the rectangle's centre (leaving its size unchanged). */ + inline void setCentre (const ValueType newCentreX, const ValueType newCentreY) noexcept { pos.x = newCentreX - w / (ValueType) 2; pos.y = newCentreY - h / (ValueType) 2; } + + /** Changes the position of the rectangle's centre (leaving its size unchanged). */ + inline void setCentre (const Point newCentre) noexcept { setCentre (newCentre.x, newCentre.y); } + /** Returns a rectangle which has the same size and y-position as this one, but with a different x-position. */ Rectangle withX (const ValueType newX) const noexcept { return Rectangle (newX, pos.y, w, h); } @@ -189,70 +190,90 @@ public: Rectangle withPosition (const ValueType newX, const ValueType newY) const noexcept { return Rectangle (newX, newY, w, h); } /** Returns a rectangle with the same size as this one, but a new position. */ - Rectangle withPosition (const Point& newPos) const noexcept { return Rectangle (newPos.x, newPos.y, w, h); } + Rectangle withPosition (const Point newPos) const noexcept { return Rectangle (newPos.x, newPos.y, w, h); } /** Returns a rectangle whose size is the same as this one, but whose top-left position is (0, 0). */ Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } + /** Returns a rectangle with the same size as this one, but a new centre position. */ + Rectangle withCentre (const Point newCentre) const noexcept { return Rectangle (newCentre.x - w / (ValueType) 2, + newCentre.y - h / (ValueType) 2, w, h); } + /** Returns a rectangle which has the same position and height as this one, but with a different width. */ - Rectangle withWidth (const ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } + Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } /** Returns a rectangle which has the same position and width as this one, but with a different height. */ - Rectangle withHeight (const ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } + Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } - /** Returns a rectangle with the same position as this one, but a new size. */ - Rectangle withSize (const ValueType newWidth, const ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } + /** Returns a rectangle with the same top-left position as this one, but a new size. */ + Rectangle withSize (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } + + /** Returns a rectangle with the same centre position as this one, but a new size. */ + Rectangle withSizeKeepingCentre (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x + (w - newWidth) / (ValueType) 2, + pos.y + (h - newHeight) / (ValueType) 2, newWidth, 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. @see withLeft */ - void setLeft (const ValueType newLeft) noexcept { w = jmax (ValueType(), pos.x + w - newLeft); pos.x = newLeft; } + void setLeft (ValueType newLeft) noexcept { w = jmax (ValueType(), pos.x + w - newLeft); pos.x = newLeft; } /** Returns a new rectangle with a different x position, but the same right-hand edge as this one. If the new x is beyond the right of the current right-hand edge, the width will be set to zero. @see setLeft */ - Rectangle withLeft (const ValueType newLeft) const noexcept { return Rectangle (newLeft, pos.y, jmax (ValueType(), pos.x + w - newLeft), h); } + Rectangle withLeft (ValueType newLeft) const noexcept { return Rectangle (newLeft, pos.y, jmax (ValueType(), pos.x + w - newLeft), h); } /** 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. @see withTop */ - void setTop (const ValueType newTop) noexcept { h = jmax (ValueType(), pos.y + h - newTop); pos.y = newTop; } + void setTop (ValueType newTop) noexcept { h = jmax (ValueType(), pos.y + h - newTop); pos.y = newTop; } /** Returns a new rectangle with a different y position, but the same bottom edge as this one. If the new y is beyond the bottom of the current rectangle, the height will be set to zero. @see setTop */ - Rectangle withTop (const ValueType newTop) const noexcept { return Rectangle (pos.x, newTop, w, jmax (ValueType(), pos.y + h - newTop)); } + Rectangle withTop (ValueType newTop) const noexcept { return Rectangle (pos.x, newTop, w, jmax (ValueType(), pos.y + h - 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, withRight */ - void setRight (const ValueType newRight) noexcept { pos.x = jmin (pos.x, newRight); w = newRight - pos.x; } + void setRight (ValueType newRight) noexcept { pos.x = jmin (pos.x, newRight); w = newRight - pos.x; } /** Returns a new rectangle with a different right-hand edge position, but the same left-hand edge as this one. If the new right edge is below the current left-hand edge, the width will be set to zero. @see setRight */ - Rectangle withRight (const ValueType newRight) const noexcept { return Rectangle (jmin (pos.x, newRight), pos.y, jmax (ValueType(), newRight - pos.x), h); } + Rectangle withRight (ValueType newRight) const noexcept { return Rectangle (jmin (pos.x, newRight), pos.y, jmax (ValueType(), newRight - pos.x), h); } /** 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, withBottom */ - void setBottom (const ValueType newBottom) noexcept { pos.y = jmin (pos.y, newBottom); h = newBottom - pos.y; } + void setBottom (ValueType newBottom) noexcept { pos.y = jmin (pos.y, newBottom); h = newBottom - pos.y; } /** Returns a new rectangle with a different bottom edge position, but the same top edge as this one. If the new y is beyond the bottom of the current rectangle, the height will be set to zero. @see setBottom */ - Rectangle withBottom (const ValueType newBottom) const noexcept { return Rectangle (pos.x, jmin (pos.y, newBottom), w, jmax (ValueType(), newBottom - pos.y)); } + Rectangle withBottom (ValueType newBottom) const noexcept { return Rectangle (pos.x, jmin (pos.y, newBottom), w, jmax (ValueType(), newBottom - pos.y)); } + + /** Returns a version of this rectangle with the given amount removed from its left-hand edge. */ + Rectangle withTrimmedLeft (ValueType amountToRemove) const noexcept { return withLeft (pos.x + amountToRemove); } + + /** Returns a version of this rectangle with the given amount removed from its right-hand edge. */ + Rectangle withTrimmedRight (ValueType amountToRemove) const noexcept { return withWidth (w - amountToRemove); } + + /** Returns a version of this rectangle with the given amount removed from its top edge. */ + Rectangle withTrimmedTop (ValueType amountToRemove) const noexcept { return withTop (pos.y + amountToRemove); } + + /** Returns a version of this rectangle with the given amount removed from its bottom edge. */ + Rectangle withTrimmedBottom (ValueType amountToRemove) const noexcept { return withHeight (h - amountToRemove); } //============================================================================== - /** Moves the rectangle's position by adding amount to its x and y co-ordinates. */ + /** Moves the rectangle's position by adding amount to its x and y coordinates. */ void translate (const ValueType deltaX, const ValueType deltaY) noexcept { @@ -268,31 +289,105 @@ public: } /** Returns a rectangle which is the same as this one moved by a given amount. */ - Rectangle operator+ (const Point& deltaPosition) const noexcept + Rectangle operator+ (const Point deltaPosition) const noexcept { return Rectangle (pos.x + deltaPosition.x, pos.y + deltaPosition.y, w, h); } /** Moves this rectangle by a given amount. */ - Rectangle& operator+= (const Point& deltaPosition) noexcept + Rectangle& operator+= (const Point deltaPosition) noexcept { pos += deltaPosition; return *this; } /** Returns a rectangle which is the same as this one moved by a given amount. */ - Rectangle operator- (const Point& deltaPosition) const noexcept + Rectangle operator- (const Point deltaPosition) const noexcept { return Rectangle (pos.x - deltaPosition.x, pos.y - deltaPosition.y, w, h); } /** Moves this rectangle by a given amount. */ - Rectangle& operator-= (const Point& deltaPosition) noexcept + Rectangle& operator-= (const Point deltaPosition) noexcept { pos -= deltaPosition; return *this; } + /** Returns a rectangle that has been scaled by the given amount, centred around the origin. + Note that if the rectangle has int coordinates and it's scaled by a + floating-point amount, then the result will be converted back to integer + coordinates using getSmallestIntegerContainer(). + */ + template + Rectangle operator* (FloatType scaleFactor) const noexcept + { + Rectangle r (*this); + r *= scaleFactor; + return r; + } + + /** Scales this rectangle by the given amount, centred around the origin. + Note that if the rectangle has int coordinates and it's scaled by a + floating-point amount, then the result will be converted back to integer + coordinates using getSmallestIntegerContainer(). + */ + template + Rectangle operator*= (FloatType scaleFactor) noexcept + { + Rectangle (pos.x * scaleFactor, + pos.y * scaleFactor, + w * scaleFactor, + h * scaleFactor).copyWithRounding (*this); + return *this; + } + + /** Scales this rectangle by the given X and Y factors, centred around the origin. + Note that if the rectangle has int coordinates and it's scaled by a + floating-point amount, then the result will be converted back to integer + coordinates using getSmallestIntegerContainer(). + */ + template + Rectangle operator*= (Point scaleFactor) noexcept + { + Rectangle (pos.x * scaleFactor.x, + pos.y * scaleFactor.y, + w * scaleFactor.x, + h * scaleFactor.y).copyWithRounding (*this); + return *this; + } + + /** Scales this rectangle by the given amount, centred around the origin. */ + template + Rectangle operator/ (FloatType scaleFactor) const noexcept + { + Rectangle r (*this); + r /= scaleFactor; + return r; + } + + /** Scales this rectangle by the given amount, centred around the origin. */ + template + Rectangle operator/= (FloatType scaleFactor) noexcept + { + Rectangle (pos.x / scaleFactor, + pos.y / scaleFactor, + w / scaleFactor, + h / scaleFactor).copyWithRounding (*this); + return *this; + } + + /** Scales this rectangle by the given X and Y factors, centred around the origin. */ + template + Rectangle operator/= (Point scaleFactor) noexcept + { + Rectangle (pos.x / scaleFactor.x, + pos.y / scaleFactor.y, + w / scaleFactor.x, + h / scaleFactor.y).copyWithRounding (*this); + return *this; + } + /** Expands the rectangle by a given amount. Effectively, its new size is (x - deltaX, y - deltaY, w + deltaX * 2, h + deltaY * 2). @@ -319,6 +414,16 @@ public: return Rectangle (pos.x - deltaX, pos.y - deltaY, nw, nh); } + /** Returns a rectangle that is larger than this one by a given amount. + + Effectively, the rectangle returned is (x - delta, y - delta, w + delta * 2, h + delta * 2). + @see expand, reduce, reduced + */ + Rectangle expanded (const ValueType delta) const noexcept + { + return expanded (delta, delta); + } + /** Shrinks the rectangle by a given amount. Effectively, its new size is (x + deltaX, y + deltaY, w - deltaX * 2, h - deltaY * 2). @@ -341,6 +446,16 @@ public: return expanded (-deltaX, -deltaY); } + /** Returns a rectangle that is smaller than this one by a given amount. + + Effectively, the rectangle returned is (x + delta, y + delta, w - delta * 2, h - delta * 2). + @see reduce, expand, expanded + */ + Rectangle reduced (const ValueType delta) const noexcept + { + return reduced (delta, delta); + } + /** Removes a strip from the top of this rectangle, reducing this rectangle by the specified amount and returning the section that was removed. @@ -414,14 +529,14 @@ public: /** Returns true if the two rectangles are not identical. */ bool operator!= (const Rectangle& other) const noexcept { return pos != other.pos || w != other.w || h != other.h; } - /** Returns true if this co-ordinate is inside the rectangle. */ + /** Returns true if this coordinate is inside the rectangle. */ bool contains (const ValueType xCoord, const ValueType yCoord) const noexcept { return xCoord >= pos.x && yCoord >= pos.y && xCoord < pos.x + w && yCoord < pos.y + h; } - /** Returns true if this co-ordinate is inside the rectangle. */ - bool contains (const Point& point) const noexcept + /** Returns true if this coordinate is inside the rectangle. */ + bool contains (const Point point) const noexcept { return point.x >= pos.x && point.y >= pos.y && point.x < pos.x + w && point.y < pos.y + h; } @@ -434,12 +549,23 @@ public: } /** Returns the nearest point to the specified point that lies within this rectangle. */ - Point getConstrainedPoint (const Point& point) const noexcept + Point getConstrainedPoint (const Point point) const noexcept { return Point (jlimit (pos.x, pos.x + w, point.x), jlimit (pos.y, pos.y + h, point.y)); } + /** Returns a point within this rectangle, specified as proportional coordinates. + The relative X and Y values should be between 0 and 1, where 0 is the left or + top of this rectangle, and 1 is the right or bottom. (Out-of-bounds values + will return a point outside the rectangle). + */ + Point getRelativePoint (double relativeX, double relativeY) const noexcept + { + return Point (pos.x + static_cast (w * relativeX), + pos.y + static_cast (h * relativeY)); + } + /** Returns true if any part of another rectangle overlaps this one. */ bool intersects (const Rectangle& other) const noexcept { @@ -447,7 +573,18 @@ public: && pos.y + h > other.pos.y && pos.x < other.pos.x + other.w && pos.y < other.pos.y + other.h - && w > ValueType() && h > ValueType(); + && w > ValueType() && h > ValueType() + && other.w > ValueType() && other.h > ValueType(); + } + + /** Returns true if any part of the given line lies inside this rectangle. */ + bool intersects (const Line& line) const noexcept + { + return contains (line.getStart()) || contains (line.getEnd()) + || line.intersects (Line (getTopLeft(), getTopRight())) + || line.intersects (Line (getTopRight(), getBottomRight())) + || line.intersects (Line (getBottomRight(), getBottomLeft())) + || line.intersects (Line (getBottomLeft(), getTopLeft())); } /** Returns the region that is the overlap between this and another rectangle. @@ -466,9 +603,9 @@ public: return Rectangle(); } - /** Clips a rectangle so that it lies only within this one. + /** Clips a set of rectangle coordinates so that they lie only within this one. This is a non-static version of intersectRectangles(). - Returns false if the two regions didn't overlap. + Returns false if the two rectangles didn't overlap. */ bool intersectRectangle (ValueType& otherX, ValueType& otherY, ValueType& otherW, ValueType& otherH) const noexcept { @@ -490,6 +627,15 @@ public: return false; } + /** Clips a rectangle so that it lies only within this one. + Returns false if the two rectangles didn't overlap. + */ + bool intersectRectangle (Rectangle& rectangleToClip) const noexcept + { + return intersectRectangle (rectangleToClip.pos.x, rectangleToClip.pos.y, + rectangleToClip.w, rectangleToClip.h); + } + /** Returns the smallest rectangle that contains both this one and the one passed-in. If either this or the other rectangle are empty, they will not be counted as @@ -524,8 +670,9 @@ public: pos.y = newY; return true; } - else if (pos.y == other.pos.y && getBottom() == other.getBottom() - && (other.getRight() >= pos.x && other.pos.x <= getRight())) + + if (pos.y == other.pos.y && getBottom() == other.getBottom() + && (other.getRight() >= pos.x && other.pos.x <= getRight())) { const ValueType newX = jmin (pos.x, other.pos.x); w = jmax (getRight(), other.getRight()) - newX; @@ -565,27 +712,48 @@ public: return false; } + /** Tries to fit this rectangle within a target area, returning the result. + + If this rectangle is not completely inside the target area, then it'll be + shifted (without changing its size) so that it lies within the target. If it + is larger than the target rectangle in either dimension, then that dimension + will be reduced to fit within the target. + */ + Rectangle constrainedWithin (const Rectangle& areaToFitWithin) const noexcept + { + const ValueType newW (jmin (w, areaToFitWithin.getWidth())); + const ValueType newH (jmin (h, areaToFitWithin.getHeight())); + + return Rectangle (jlimit (areaToFitWithin.getX(), areaToFitWithin.getRight() - newW, pos.x), + jlimit (areaToFitWithin.getY(), areaToFitWithin.getBottom() - newH, pos.y), + newW, newH); + } + /** 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. */ - Rectangle transformed (const AffineTransform& transform) const noexcept + Rectangle transformedBy (const AffineTransform& transform) const noexcept { - float x1 = pos.x, y1 = pos.y; - float x2 = pos.x + w, y2 = pos.y; - float x3 = pos.x, y3 = pos.y + h; - float x4 = x2, y4 = y3; + typedef typename TypeHelpers::SmallestFloatType::type FloatType; + + FloatType x1 = static_cast (pos.x), y1 = static_cast (pos.y); + FloatType x2 = static_cast (pos.x + w), y2 = static_cast (pos.y); + FloatType x3 = static_cast (pos.x), y3 = static_cast (pos.y + h); + FloatType x4 = static_cast (x2), y4 = static_cast (y3); transform.transformPoints (x1, y1, x2, y2); transform.transformPoints (x3, y3, x4, y4); - const float rx = jmin (x1, x2, x3, x4); - const float ry = jmin (y1, y2, y3, y4); + const FloatType rx1 = jmin (x1, x2, x3, x4); + const FloatType rx2 = jmax (x1, x2, x3, x4); + const FloatType ry1 = jmin (y1, y2, y3, y4); + const FloatType ry2 = jmax (y1, y2, y3, y4); - return Rectangle (rx, ry, - jmax (x1, x2, x3, x4) - rx, - jmax (y1, y2, y3, y4) - ry); + Rectangle r; + Rectangle (rx1, ry1, rx2 - rx1, ry2 - ry1).copyWithRounding (r); + return r; } /** Returns the smallest integer-aligned rectangle that completely contains this one. @@ -594,14 +762,44 @@ public: */ Rectangle getSmallestIntegerContainer() const noexcept { - const int x1 = static_cast (std::floor (static_cast (pos.x))); - const int y1 = static_cast (std::floor (static_cast (pos.y))); - const int x2 = static_cast (std::ceil (static_cast (pos.x + w))); - const int y2 = static_cast (std::ceil (static_cast (pos.y + h))); + const int x1 = floorAsInt (pos.x); + const int y1 = floorAsInt (pos.y); + const int x2 = ceilAsInt (pos.x + w); + const int y2 = ceilAsInt (pos.y + h); return Rectangle (x1, y1, x2 - x1, y2 - y1); } + /** Casts this rectangle to a Rectangle. + @see getSmallestIntegerContainer + */ + Rectangle toFloat() const noexcept + { + return Rectangle (static_cast (pos.x), static_cast (pos.y), + static_cast (w), static_cast (h)); + } + + /** Casts this rectangle to a Rectangle. + @see getSmallestIntegerContainer + */ + Rectangle toDouble() const noexcept + { + return Rectangle (static_cast (pos.x), static_cast (pos.y), + static_cast (w), static_cast (h)); + } + + /** Casts this rectangle to a Rectangle with the given type. + If the target type is a conversion from float to int, then the conversion + will be done using getSmallestIntegerContainer(). + */ + template + Rectangle toType() const noexcept + { + Rectangle r; + copyWithRounding (r); + return r; + } + /** Returns the smallest Rectangle that can contain a set of points. */ static Rectangle findAreaContainingPoints (const Point* const points, const int numPoints) noexcept { @@ -624,18 +822,8 @@ public: return Rectangle (minX, minY, maxX - minX, maxY - minY); } - /** Casts this rectangle to a Rectangle. - Obviously this is mainly useful for rectangles that use integer types. - @see getSmallestIntegerContainer - */ - Rectangle toFloat() const noexcept - { - return Rectangle (static_cast (pos.x), static_cast (pos.y), - static_cast (w), static_cast (h)); - } - //============================================================================== - /** Static utility to intersect two sets of rectangular co-ordinates. + /** Static utility to intersect two sets of rectangular coordinates. Returns false if the two regions didn't overlap. @see intersectRectangle */ @@ -688,10 +876,10 @@ public: @see toString */ - static Rectangle fromString (const String& stringVersion) + static Rectangle fromString (StringRef stringVersion) { StringArray toks; - toks.addTokens (stringVersion.trim(), ",; \t\r\n", String::empty); + toks.addTokens (stringVersion.text.findEndOfWhitespace(), ",; \t\r\n", ""); return Rectangle (parseIntAfterSpace (toks[0]), parseIntAfterSpace (toks[1]), @@ -699,14 +887,31 @@ public: parseIntAfterSpace (toks[3])); } + #ifndef DOXYGEN + // This has been renamed by transformedBy, in order to match the method names used in the Point class. + JUCE_DEPRECATED_WITH_BODY (Rectangle transformed (const AffineTransform& t) const noexcept, { return transformedBy (t); }) + #endif + private: - friend class RectangleList; + template friend class Rectangle; + Point pos; ValueType w, h; - static int parseIntAfterSpace (const String& s) noexcept - { return s.getCharPointer().findEndOfWhitespace().getIntValue32(); } + static int parseIntAfterSpace (StringRef s) noexcept + { return s.text.findEndOfWhitespace().getIntValue32(); } + + void copyWithRounding (Rectangle& result) const noexcept { result = getSmallestIntegerContainer(); } + void copyWithRounding (Rectangle& result) const noexcept { result = toFloat(); } + void copyWithRounding (Rectangle& result) const noexcept { result = toDouble(); } + + static int floorAsInt (int n) noexcept { return n; } + static int floorAsInt (float n) noexcept { return (int) std::floor (n); } + static int floorAsInt (double n) noexcept { return (int) std::floor (n); } + static int ceilAsInt (int n) noexcept { return n; } + static int ceilAsInt (float n) noexcept { return (int) std::ceil (n); } + static int ceilAsInt (double n) noexcept { return (int) std::ceil (n); } }; -#endif // __JUCE_RECTANGLE_JUCEHEADER__ +#endif // JUCE_RECTANGLE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.cpp b/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.cpp deleted file mode 100644 index f5b9dc7..0000000 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.cpp +++ /dev/null @@ -1,515 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -RectangleList::RectangleList() noexcept -{ -} - -RectangleList::RectangleList (const Rectangle& rect) -{ - addWithoutMerging (rect); -} - -RectangleList::RectangleList (const RectangleList& other) - : rects (other.rects) -{ -} - -RectangleList& RectangleList::operator= (const RectangleList& other) -{ - rects = other.rects; - return *this; -} - -#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS -RectangleList::RectangleList (RectangleList&& other) noexcept - : rects (static_cast >&&> (other.rects)) -{ -} - -RectangleList& RectangleList::operator= (RectangleList&& other) noexcept -{ - rects = static_cast >&&> (other.rects); - return *this; -} -#endif - -RectangleList::~RectangleList() -{ -} - -//============================================================================== -void RectangleList::clear() -{ - rects.clearQuick(); -} - -Rectangle RectangleList::getRectangle (const int index) const noexcept -{ - if (isPositiveAndBelow (index, rects.size())) - return rects.getReference (index); - - return Rectangle(); -} - -bool RectangleList::isEmpty() const noexcept -{ - return rects.size() == 0; -} - -//============================================================================== -RectangleList::Iterator::Iterator (const RectangleList& list) noexcept - : current (nullptr), - owner (list), - index (list.rects.size()) -{ -} - -RectangleList::Iterator::~Iterator() -{ -} - -bool RectangleList::Iterator::next() noexcept -{ - if (--index >= 0) - { - current = &(owner.rects.getReference (index)); - return true; - } - - return false; -} - - -//============================================================================== -void RectangleList::add (const Rectangle& rect) -{ - if (! rect.isEmpty()) - { - if (rects.size() == 0) - { - rects.add (rect); - } - else - { - bool anyOverlaps = false; - - for (int j = rects.size(); --j >= 0;) - { - Rectangle& ourRect = rects.getReference (j); - - if (rect.intersects (ourRect)) - { - if (rect.contains (ourRect)) - rects.remove (j); - else if (! ourRect.reduceIfPartlyContainedIn (rect)) - anyOverlaps = true; - } - } - - if (anyOverlaps && rects.size() > 0) - { - RectangleList r (rect); - - for (int i = rects.size(); --i >= 0;) - { - const Rectangle& ourRect = rects.getReference (i); - - if (rect.intersects (ourRect)) - { - r.subtract (ourRect); - - if (r.rects.size() == 0) - return; - } - } - - rects.addArray (r.rects); - } - else - { - rects.add (rect); - } - } - } -} - -void RectangleList::addWithoutMerging (const Rectangle& rect) -{ - if (! rect.isEmpty()) - rects.add (rect); -} - -void RectangleList::add (const int x, const int y, const int w, const int h) -{ - add (Rectangle (x, y, w, h)); -} - -void RectangleList::add (const RectangleList& other) -{ - for (int i = 0; i < other.rects.size(); ++i) - add (other.rects.getReference (i)); -} - -void RectangleList::subtract (const Rectangle& rect) -{ - const int originalNumRects = rects.size(); - - if (originalNumRects > 0) - { - const int x1 = rect.pos.x; - const int y1 = rect.pos.y; - const int x2 = x1 + rect.w; - const int y2 = y1 + rect.h; - - for (int i = getNumRectangles(); --i >= 0;) - { - Rectangle& r = rects.getReference (i); - - const int rx1 = r.pos.x; - const int ry1 = r.pos.y; - const int rx2 = rx1 + r.w; - const int ry2 = ry1 + r.h; - - if (! (x2 <= rx1 || x1 >= rx2 || y2 <= ry1 || y1 >= ry2)) - { - if (x1 > rx1 && x1 < rx2) - { - if (y1 <= ry1 && y2 >= ry2 && x2 >= rx2) - { - r.w = x1 - rx1; - } - else - { - r.pos.x = x1; - r.w = rx2 - x1; - - rects.insert (++i, Rectangle (rx1, ry1, x1 - rx1, ry2 - ry1)); - ++i; - } - } - else if (x2 > rx1 && x2 < rx2) - { - r.pos.x = x2; - r.w = rx2 - x2; - - if (y1 > ry1 || y2 < ry2 || x1 > rx1) - { - rects.insert (++i, Rectangle (rx1, ry1, x2 - rx1, ry2 - ry1)); - ++i; - } - } - else if (y1 > ry1 && y1 < ry2) - { - if (x1 <= rx1 && x2 >= rx2 && y2 >= ry2) - { - r.h = y1 - ry1; - } - else - { - r.pos.y = y1; - r.h = ry2 - y1; - - rects.insert (++i, Rectangle (rx1, ry1, rx2 - rx1, y1 - ry1)); - ++i; - } - } - else if (y2 > ry1 && y2 < ry2) - { - r.pos.y = y2; - r.h = ry2 - y2; - - if (x1 > rx1 || x2 < rx2 || y1 > ry1) - { - rects.insert (++i, Rectangle (rx1, ry1, rx2 - rx1, y2 - ry1)); - ++i; - } - } - else - { - rects.remove (i); - } - } - } - } -} - -bool RectangleList::subtract (const RectangleList& otherList) -{ - for (int i = otherList.rects.size(); --i >= 0 && rects.size() > 0;) - subtract (otherList.rects.getReference (i)); - - return rects.size() > 0; -} - -bool RectangleList::clipTo (const Rectangle& rect) -{ - bool notEmpty = false; - - if (rect.isEmpty()) - { - clear(); - } - else - { - for (int i = rects.size(); --i >= 0;) - { - Rectangle& r = rects.getReference (i); - - if (! rect.intersectRectangle (r.pos.x, r.pos.y, r.w, r.h)) - rects.remove (i); - else - notEmpty = true; - } - } - - return notEmpty; -} - -bool RectangleList::clipTo (const RectangleList& other) -{ - if (rects.size() == 0) - return false; - - RectangleList result; - - for (int j = 0; j < rects.size(); ++j) - { - const Rectangle& rect = rects.getReference (j); - - for (int i = other.rects.size(); --i >= 0;) - { - Rectangle r (other.rects.getReference (i)); - - if (rect.intersectRectangle (r.pos.x, r.pos.y, r.w, r.h)) - result.rects.add (r); - } - } - - swapWith (result); - - return ! isEmpty(); -} - -bool RectangleList::getIntersectionWith (const Rectangle& rect, RectangleList& destRegion) const -{ - destRegion.clear(); - - if (! rect.isEmpty()) - { - for (int i = rects.size(); --i >= 0;) - { - Rectangle r (rects.getReference (i)); - - if (rect.intersectRectangle (r.pos.x, r.pos.y, r.w, r.h)) - destRegion.rects.add (r); - } - } - - return destRegion.rects.size() > 0; -} - -void RectangleList::swapWith (RectangleList& otherList) noexcept -{ - rects.swapWithArray (otherList.rects); -} - - -//============================================================================== -void RectangleList::consolidate() -{ - int i; - for (i = 0; i < getNumRectangles() - 1; ++i) - { - Rectangle& r = rects.getReference (i); - const int rx1 = r.pos.x; - const int ry1 = r.pos.y; - const int rx2 = rx1 + r.w; - const int ry2 = ry1 + r.h; - - for (int j = rects.size(); --j > i;) - { - Rectangle& r2 = rects.getReference (j); - const int jrx1 = r2.pos.x; - const int jry1 = r2.pos.y; - const int jrx2 = jrx1 + r2.w; - const int jry2 = jry1 + r2.h; - - // if the vertical edges of any blocks are touching and their horizontals don't - // line up, split them horizontally.. - if (jrx1 == rx2 || jrx2 == rx1) - { - if (jry1 > ry1 && jry1 < ry2) - { - r.h = jry1 - ry1; - rects.add (Rectangle (rx1, jry1, rx2 - rx1, ry2 - jry1)); - i = -1; - break; - } - - if (jry2 > ry1 && jry2 < ry2) - { - r.h = jry2 - ry1; - rects.add (Rectangle (rx1, jry2, rx2 - rx1, ry2 - jry2)); - i = -1; - break; - } - else if (ry1 > jry1 && ry1 < jry2) - { - r2.h = ry1 - jry1; - rects.add (Rectangle (jrx1, ry1, jrx2 - jrx1, jry2 - ry1)); - i = -1; - break; - } - else if (ry2 > jry1 && ry2 < jry2) - { - r2.h = ry2 - jry1; - rects.add (Rectangle (jrx1, ry2, jrx2 - jrx1, jry2 - ry2)); - i = -1; - break; - } - } - } - } - - for (i = 0; i < rects.size() - 1; ++i) - { - Rectangle& r = rects.getReference (i); - - for (int j = rects.size(); --j > i;) - { - if (r.enlargeIfAdjacent (rects.getReference (j))) - { - rects.remove (j); - i = -1; - break; - } - } - } -} - -//============================================================================== -bool RectangleList::containsPoint (const int x, const int y) const noexcept -{ - for (int i = getNumRectangles(); --i >= 0;) - if (rects.getReference (i).contains (x, y)) - return true; - - return false; -} - -bool RectangleList::containsRectangle (const Rectangle& rectangleToCheck) const -{ - if (rects.size() > 1) - { - RectangleList r (rectangleToCheck); - - for (int i = rects.size(); --i >= 0;) - { - r.subtract (rects.getReference (i)); - - if (r.rects.size() == 0) - return true; - } - } - else if (rects.size() > 0) - { - return rects.getReference (0).contains (rectangleToCheck); - } - - return false; -} - -bool RectangleList::intersectsRectangle (const Rectangle& rectangleToCheck) const noexcept -{ - for (int i = rects.size(); --i >= 0;) - if (rects.getReference (i).intersects (rectangleToCheck)) - return true; - - return false; -} - -bool RectangleList::intersects (const RectangleList& other) const noexcept -{ - for (int i = rects.size(); --i >= 0;) - if (other.intersectsRectangle (rects.getReference (i))) - return true; - - return false; -} - -Rectangle RectangleList::getBounds() const noexcept -{ - if (rects.size() <= 1) - { - if (rects.size() == 0) - return Rectangle(); - else - return rects.getReference (0); - } - else - { - const Rectangle& r = rects.getReference (0); - - int minX = r.pos.x; - int minY = r.pos.y; - int maxX = minX + r.w; - int maxY = minY + r.h; - - for (int i = rects.size(); --i > 0;) - { - const Rectangle& r2 = rects.getReference (i); - - minX = jmin (minX, r2.pos.x); - minY = jmin (minY, r2.pos.y); - maxX = jmax (maxX, r2.getRight()); - maxY = jmax (maxY, r2.getBottom()); - } - - return Rectangle (minX, minY, maxX - minX, maxY - minY); - } -} - -void RectangleList::offsetAll (const int dx, const int dy) noexcept -{ - for (int i = rects.size(); --i >= 0;) - { - Rectangle& r = rects.getReference (i); - - r.pos.x += dx; - r.pos.y += dy; - } -} - -//============================================================================== -Path RectangleList::toPath() const -{ - Path p; - - for (int i = 0; i < rects.size(); ++i) - p.addRectangle (rects.getReference (i)); - - return p; -} diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.h index c7c4cb8..3637f6a 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_RectangleList.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_RECTANGLELIST_JUCEHEADER__ -#define __JUCE_RECTANGLELIST_JUCEHEADER__ - -#include "juce_Rectangle.h" -#include "juce_Path.h" +#ifndef JUCE_RECTANGLELIST_H_INCLUDED +#define JUCE_RECTANGLELIST_H_INCLUDED //============================================================================== @@ -40,55 +36,65 @@ @see Rectangle */ -class JUCE_API RectangleList +template +class RectangleList { public: + typedef Rectangle RectangleType; + //============================================================================== /** Creates an empty RectangleList */ - RectangleList() noexcept; + RectangleList() noexcept {} /** Creates a copy of another list */ - RectangleList (const RectangleList& other); + RectangleList (const RectangleList& other) : rects (other.rects) + { + } /** Creates a list containing just one rectangle. */ - RectangleList (const Rectangle& rect); + RectangleList (const RectangleType& rect) + { + addWithoutMerging (rect); + } /** Copies this list from another one. */ - RectangleList& operator= (const RectangleList& other); + RectangleList& operator= (const RectangleList& other) + { + rects = other.rects; + return *this; + } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - RectangleList (RectangleList&& other) noexcept; - RectangleList& operator= (RectangleList&& other) noexcept; - #endif + RectangleList (RectangleList&& other) noexcept + : rects (static_cast&&> (other.rects)) + { + } - /** Destructor. */ - ~RectangleList(); + RectangleList& operator= (RectangleList&& other) noexcept + { + rects = static_cast&&> (other.rects); + return *this; + } + #endif //============================================================================== /** Returns true if the region is empty. */ - bool isEmpty() const noexcept; + bool isEmpty() const noexcept { return rects.size() == 0; } /** Returns the number of rectangles in the list. */ int getNumRectangles() const noexcept { 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. + @returns the rectangle at the index, or an empty rectangle if the index is out-of-range. */ - Rectangle getRectangle (int index) const noexcept; - + RectangleType getRectangle (int index) const noexcept { return rects[index]; } //============================================================================== /** 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); + void clear() + { + rects.clearQuick(); + } /** Merges a new rectangle into the list. @@ -96,28 +102,177 @@ public: that overlap existing rectangles in the list, and adjacent rectangles will be merged into it. */ - void add (const Rectangle& rect); + void add (const RectangleType& rect) + { + if (! rect.isEmpty()) + { + if (rects.size() == 0) + { + rects.add (rect); + } + else + { + bool anyOverlaps = false; + + for (int j = rects.size(); --j >= 0;) + { + RectangleType& ourRect = rects.getReference (j); + + if (rect.intersects (ourRect)) + { + if (rect.contains (ourRect)) + rects.remove (j); + else if (! ourRect.reduceIfPartlyContainedIn (rect)) + anyOverlaps = true; + } + } + + if (anyOverlaps && rects.size() > 0) + { + RectangleList r (rect); + + for (int i = rects.size(); --i >= 0;) + { + const RectangleType& ourRect = rects.getReference (i); + + if (rect.intersects (ourRect)) + { + r.subtract (ourRect); + + if (r.rects.size() == 0) + return; + } + } + + rects.addArray (r.rects); + } + else + { + rects.add (rect); + } + } + } + } + + /** 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 (ValueType x, ValueType y, ValueType width, ValueType height) + { + add (RectangleType (x, y, width, height)); + } /** 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); + void addWithoutMerging (const RectangleType& rect) + { + if (! rect.isEmpty()) + rects.add (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); + void add (const RectangleList& other) + { + for (const RectangleType* r = other.begin(), * const e = other.end(); r != e; ++r) + add (*r); + } /** 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); + void subtract (const RectangleType& rect) + { + const int originalNumRects = rects.size(); + + if (originalNumRects > 0) + { + const ValueType x1 = rect.getX(); + const ValueType y1 = rect.getY(); + const ValueType x2 = x1 + rect.getWidth(); + const ValueType y2 = y1 + rect.getHeight(); + + for (int i = getNumRectangles(); --i >= 0;) + { + RectangleType& r = rects.getReference (i); + + const ValueType rx1 = r.getX(); + const ValueType ry1 = r.getY(); + const ValueType rx2 = rx1 + r.getWidth(); + const ValueType ry2 = ry1 + r.getHeight(); + + if (! (x2 <= rx1 || x1 >= rx2 || y2 <= ry1 || y1 >= ry2)) + { + if (x1 > rx1 && x1 < rx2) + { + if (y1 <= ry1 && y2 >= ry2 && x2 >= rx2) + { + r.setWidth (x1 - rx1); + } + else + { + r.setX (x1); + r.setWidth (rx2 - x1); + + rects.insert (++i, RectangleType (rx1, ry1, x1 - rx1, ry2 - ry1)); + ++i; + } + } + else if (x2 > rx1 && x2 < rx2) + { + r.setX (x2); + r.setWidth (rx2 - x2); + + if (y1 > ry1 || y2 < ry2 || x1 > rx1) + { + rects.insert (++i, RectangleType (rx1, ry1, x2 - rx1, ry2 - ry1)); + ++i; + } + } + else if (y1 > ry1 && y1 < ry2) + { + if (x1 <= rx1 && x2 >= rx2 && y2 >= ry2) + { + r.setHeight (y1 - ry1); + } + else + { + r.setY (y1); + r.setHeight (ry2 - y1); + + rects.insert (++i, RectangleType (rx1, ry1, rx2 - rx1, y1 - ry1)); + ++i; + } + } + else if (y2 > ry1 && y2 < ry2) + { + r.setY (y2); + r.setHeight (ry2 - y2); + + if (x1 > rx1 || x2 < rx2 || y1 > ry1) + { + rects.insert (++i, RectangleType (rx1, ry1, rx2 - rx1, y2 - ry1)); + ++i; + } + } + else + { + rects.remove (i); + } + } + } + } + } /** Removes all areas in another RectangleList from this one. @@ -126,7 +281,13 @@ public: @returns true if the resulting list is non-empty. */ - bool subtract (const RectangleList& otherList); + bool subtract (const RectangleList& otherList) + { + for (int i = otherList.rects.size(); --i >= 0 && rects.size() > 0;) + subtract (otherList.rects.getReference (i)); + + return rects.size() > 0; + } /** Removes any areas of the region that lie outside a given rectangle. @@ -137,7 +298,29 @@ public: @see getIntersectionWith */ - bool clipTo (const Rectangle& rect); + bool clipTo (const RectangleType& rect) + { + bool notEmpty = false; + + if (rect.isEmpty()) + { + clear(); + } + else + { + for (int i = rects.size(); --i >= 0;) + { + RectangleType& r = rects.getReference (i); + + if (! rect.intersectRectangle (r)) + rects.remove (i); + else + notEmpty = true; + } + } + + return notEmpty; + } /** Removes any areas of the region that lie outside a given rectangle list. @@ -148,7 +331,30 @@ public: @see getIntersectionWith */ - bool clipTo (const RectangleList& other); + template + bool clipTo (const RectangleList& other) + { + if (rects.size() == 0) + return false; + + RectangleList result; + + for (int j = 0; j < rects.size(); ++j) + { + const RectangleType& rect = rects.getReference (j); + + for (const Rectangle* r = other.begin(), * const e = other.end(); r != e; ++r) + { + RectangleType clipped (r->template toType()); + + if (rect.intersectRectangle (clipped)) + result.rects.add (clipped); + } + } + + swapWith (result); + return ! isEmpty(); + } /** Creates a region which is the result of clipping this one to a given rectangle. @@ -159,21 +365,54 @@ public: @see clipTo */ - bool getIntersectionWith (const Rectangle& rect, RectangleList& destRegion) const; + bool getIntersectionWith (const RectangleType& rect, RectangleList& destRegion) const + { + destRegion.clear(); + + if (! rect.isEmpty()) + { + for (int i = rects.size(); --i >= 0;) + { + RectangleType r (rects.getReference (i)); + + if (rect.intersectRectangle (r)) + destRegion.rects.add (r); + } + } + + return destRegion.rects.size() > 0; + } /** 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) noexcept; + void swapWith (RectangleList& otherList) noexcept + { + rects.swapWith (otherList.rects); + } //============================================================================== /** 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 noexcept; + bool containsPoint (Point point) const noexcept + { + for (const RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) + if (r->contains (point)) + return true; + + return false; + } + + /** Checks whether the region contains a given point. + @returns true if the point lies within one of the rectangles in the list + */ + bool containsPoint (ValueType x, ValueType y) const noexcept + { + return containsPoint (Point (x, y)); + } /** Checks whether the region contains the whole of a given rectangle. @@ -181,7 +420,27 @@ public: defined by this object @see intersectsRectangle, containsPoint */ - bool containsRectangle (const Rectangle& rectangleToCheck) const; + bool containsRectangle (const RectangleType& rectangleToCheck) const + { + if (rects.size() > 1) + { + RectangleList r (rectangleToCheck); + + for (int i = rects.size(); --i >= 0;) + { + r.subtract (rects.getReference (i)); + + if (r.rects.size() == 0) + return true; + } + } + else if (rects.size() > 0) + { + return rects.getReference (0).contains (rectangleToCheck); + } + + return false; + } /** Checks whether the region contains any part of a given rectangle. @@ -189,17 +448,59 @@ public: defined by this object @see containsRectangle */ - bool intersectsRectangle (const Rectangle& rectangleToCheck) const noexcept; + bool intersectsRectangle (const RectangleType& rectangleToCheck) const noexcept + { + for (const RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) + if (r->intersects (rectangleToCheck)) + return true; + + return false; + } /** Checks whether this region intersects any part of another one. @see intersectsRectangle */ - bool intersects (const RectangleList& other) const noexcept; + bool intersects (const RectangleList& other) const noexcept + { + for (const RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) + if (other.intersectsRectangle (*r)) + return true; + + return false; + } //============================================================================== /** Returns the smallest rectangle that can enclose the whole of this region. */ - Rectangle getBounds() const noexcept; + RectangleType getBounds() const noexcept + { + if (rects.size() <= 1) + { + if (rects.size() == 0) + return RectangleType(); + + return rects.getReference (0); + } + + const RectangleType& r = rects.getReference (0); + + ValueType minX = r.getX(); + ValueType minY = r.getY(); + ValueType maxX = minX + r.getWidth(); + ValueType maxY = minY + r.getHeight(); + + for (int i = rects.size(); --i > 0;) + { + const RectangleType& r2 = rects.getReference (i); + + minX = jmin (minX, r2.getX()); + minY = jmin (minY, r2.getY()); + maxX = jmax (maxX, r2.getRight()); + maxY = jmax (maxY, r2.getBottom()); + } + + return RectangleType (minX, minY, maxX - minX, maxY - minY); + } /** Optimises the list into a minimum number of constituent rectangles. @@ -207,50 +508,130 @@ public: possible, to simplify lists that might have been fragmented by repeated add/subtract calls. */ - void consolidate(); + void consolidate() + { + for (int i = 0; i < getNumRectangles() - 1; ++i) + { + RectangleType& r = rects.getReference (i); + const ValueType rx1 = r.getX(); + const ValueType ry1 = r.getY(); + const ValueType rx2 = rx1 + r.getWidth(); + const ValueType ry2 = ry1 + r.getHeight(); - /** Adds an x and y value to all the co-ordinates. */ - void offsetAll (int dx, int dy) noexcept; + for (int j = rects.size(); --j > i;) + { + RectangleType& r2 = rects.getReference (j); + const ValueType jrx1 = r2.getX(); + const ValueType jry1 = r2.getY(); + const ValueType jrx2 = jrx1 + r2.getWidth(); + const ValueType jry2 = jry1 + r2.getHeight(); + + // if the vertical edges of any blocks are touching and their horizontals don't + // line up, split them horizontally.. + if (jrx1 == rx2 || jrx2 == rx1) + { + if (jry1 > ry1 && jry1 < ry2) + { + r.setHeight (jry1 - ry1); + rects.add (RectangleType (rx1, jry1, rx2 - rx1, ry2 - jry1)); + i = -1; + break; + } + + if (jry2 > ry1 && jry2 < ry2) + { + r.setHeight (jry2 - ry1); + rects.add (RectangleType (rx1, jry2, rx2 - rx1, ry2 - jry2)); + i = -1; + break; + } + else if (ry1 > jry1 && ry1 < jry2) + { + r2.setHeight (ry1 - jry1); + rects.add (RectangleType (jrx1, ry1, jrx2 - jrx1, jry2 - ry1)); + i = -1; + break; + } + else if (ry2 > jry1 && ry2 < jry2) + { + r2.setHeight (ry2 - jry1); + rects.add (RectangleType (jrx1, ry2, jrx2 - jrx1, jry2 - ry2)); + i = -1; + break; + } + } + } + } + + for (int i = 0; i < rects.size() - 1; ++i) + { + RectangleType& r = rects.getReference (i); + + for (int j = rects.size(); --j > i;) + { + if (r.enlargeIfAdjacent (rects.getReference (j))) + { + rects.remove (j); + i = -1; + break; + } + } + } + } + + /** Adds an x and y value to all the coordinates. */ + void offsetAll (Point offset) noexcept + { + for (RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) + *r += offset; + } + + /** Adds an x and y value to all the coordinates. */ + void offsetAll (ValueType dx, ValueType dy) noexcept + { + offsetAll (Point (dx, dy)); + } + + /** Scales all the coordinates. */ + template + void scaleAll (ScaleType scaleFactor) noexcept + { + for (RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) + *r *= scaleFactor; + } + + /** Applies a transform to all the rectangles. + Obviously this will create a mess if the transform involves any + rotation or skewing. + */ + void transformAll (const AffineTransform& transform) noexcept + { + for (RectangleType* r = rects.begin(), * const e = rects.end(); r != e; ++r) + *r = r->transformedBy (transform); + } //============================================================================== /** Creates a Path object to represent this region. */ - Path toPath() const; + Path toPath() const + { + Path p; + for (int i = 0; i < rects.size(); ++i) + p.addRectangle (rects.getReference (i)); + + return p; + } //============================================================================== - /** An iterator for accessing all the rectangles in a RectangleList. */ - class JUCE_API Iterator - { - public: - //============================================================================== - Iterator (const RectangleList& list) noexcept; - ~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() noexcept; - - /** Returns the current rectangle. */ - const Rectangle* getRectangle() const noexcept { return current; } - - private: - const Rectangle* current; - const RectangleList& owner; - int index; - - JUCE_DECLARE_NON_COPYABLE (Iterator); - }; + /** Standard method for iterating the rectangles in the list. */ + const RectangleType* begin() const noexcept { return rects.begin(); } + /** Standard method for iterating the rectangles in the list. */ + const RectangleType* end() const noexcept { return rects.end(); } private: //============================================================================== - friend class Iterator; - Array > rects; - - JUCE_LEAK_DETECTOR (RectangleList); + Array rects; }; -#endif // __JUCE_RECTANGLELIST_JUCEHEADER__ +#endif // JUCE_RECTANGLELIST_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/jpglib/jcparam.c b/JuceLibraryCode/modules/juce_graphics/image_formats/jpglib/jcparam.c index a91bc6a..739dcdf 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/jpglib/jcparam.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/jpglib/jcparam.c @@ -592,7 +592,7 @@ jpeg_simple_progression (j_compress_ptr cinfo) 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); + fill_a_scan(scanptr, 0, 1, 63, 1, 0); } else { /* All-purpose script for other color spaces. */ /* Successive approximation first pass */ @@ -603,7 +603,7 @@ jpeg_simple_progression (j_compress_ptr cinfo) 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); + fill_scans(scanptr, ncomps, 1, 63, 1, 0); } } diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp index b97f0a4..2c9066f 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_GIFLoader.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -406,7 +405,7 @@ private: return true; } - JUCE_DECLARE_NON_COPYABLE (GIFLoader); + JUCE_DECLARE_NON_COPYABLE (GIFLoader) }; #endif @@ -415,7 +414,8 @@ private: GIFImageFormat::GIFImageFormat() {} GIFImageFormat::~GIFImageFormat() {} -String GIFImageFormat::getFormatName() { return "GIF"; } +String GIFImageFormat::getFormatName() { return "GIF"; } +bool GIFImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("gif"); } bool GIFImageFormat::canUnderstand (InputStream& in) { diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp index fdd7094..0f56e32 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_JPEGLoader.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -34,6 +33,13 @@ namespace jpeglibNamespace #if JUCE_MINGW typedef unsigned char boolean; #endif + + #if JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wconversion" + #pragma clang diagnostic ignored "-Wdeprecated-register" + #endif + #define JPEG_INTERNALS #undef FAR #include "jpglib/jpeglib.h" @@ -106,6 +112,10 @@ namespace jpeglibNamespace #include "jpglib/jquant2.c" #include "jpglib/jutils.c" #include "jpglib/transupp.c" + + #if JUCE_CLANG + #pragma clang diagnostic pop + #endif #else #define JPEG_INTERNALS #undef FAR @@ -148,6 +158,7 @@ namespace JPEGHelpers } //============================================================================== + #if ! JUCE_USING_COREIMAGE_LOADER static void dummyCallback1 (j_decompress_ptr) {} static void jpegSkip (j_decompress_ptr decompStruct, long num) @@ -155,13 +166,14 @@ namespace JPEGHelpers decompStruct->src->next_input_byte += num; num = jmin (num, (long) decompStruct->src->bytes_in_buffer); - decompStruct->src->bytes_in_buffer -= num; + decompStruct->src->bytes_in_buffer -= (size_t) num; } static boolean jpegFill (j_decompress_ptr) { return 0; } + #endif //============================================================================== const int jpegBufferSize = 512; @@ -179,7 +191,7 @@ namespace JPEGHelpers JuceJpegDest* const dest = static_cast (cinfo->dest); const size_t numToWrite = jpegBufferSize - dest->free_in_buffer; - dest->output->write (dest->buffer, (int) numToWrite); + dest->output->write (dest->buffer, numToWrite); } static boolean jpegWriteFlush (j_compress_ptr cinfo) @@ -191,7 +203,7 @@ namespace JPEGHelpers dest->next_output_byte = reinterpret_cast (dest->buffer); dest->free_in_buffer = jpegBufferSize; - return (boolean) dest->output->write (dest->buffer, numToWrite); + return (boolean) dest->output->write (dest->buffer, (size_t) numToWrite); } } @@ -208,31 +220,27 @@ void JPEGImageFormat::setQuality (const float newQuality) quality = newQuality; } -String JPEGImageFormat::getFormatName() { return "JPEG"; } +String JPEGImageFormat::getFormatName() { return "JPEG"; } +bool JPEGImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("jpeg;jpg"); } bool JPEGImageFormat::canUnderstand (InputStream& in) { const int bytesNeeded = 10; uint8 header [bytesNeeded]; - if (in.read (header, bytesNeeded) == bytesNeeded) - { - return header[0] == 0xff + return in.read (header, bytesNeeded) == bytesNeeded + && header[0] == 0xff && header[1] == 0xd8 - && header[2] == 0xff - && (header[3] == 0xe0 || header[3] == 0xe1); - } - - return false; + && header[2] == 0xff; } -#if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER +#if JUCE_USING_COREIMAGE_LOADER Image juce_loadWithCoreImage (InputStream& input); #endif Image JPEGImageFormat::decodeImage (InputStream& in) { -#if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER +#if JUCE_USING_COREIMAGE_LOADER return juce_loadWithCoreImage (in); #else using namespace jpeglibNamespace; @@ -337,7 +345,8 @@ bool JPEGImageFormat::writeImageToStream (const Image& image, OutputStream& out) using namespace jpeglibNamespace; using namespace JPEGHelpers; - struct jpeg_compress_struct jpegCompStruct; + jpeg_compress_struct jpegCompStruct; + zerostruct (jpegCompStruct); jpeg_create_compress (&jpegCompStruct); struct jpeg_error_mgr jerr; @@ -377,7 +386,7 @@ bool JPEGImageFormat::writeImageToStream (const Image& image, OutputStream& out) jpeg_start_compress (&jpegCompStruct, TRUE); - const int strideBytes = (int) (jpegCompStruct.image_width * jpegCompStruct.input_components); + const int strideBytes = (int) (jpegCompStruct.image_width * (unsigned int) jpegCompStruct.input_components); JSAMPARRAY buffer = (*jpegCompStruct.mem->alloc_sarray) ((j_common_ptr) &jpegCompStruct, JPOOL_IMAGE, (JDIMENSION) strideBytes, 1); diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp index 848cddf..4ee36ab 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/juce_PNGLoader.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -55,10 +54,206 @@ namespace pnglibNamespace using std::free; #endif + #if JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wsign-conversion" + #endif + using std::abs; - #define PNG_INTERNAL #define NO_DUMMY_DECL - #define PNG_SETJMP_NOT_SUPPORTED + #define PNGLCONF_H 1 + + #if JUCE_ANDROID + #define PNG_ARM_NEON_SUPPORTED + #endif + + #define PNG_16BIT_SUPPORTED + #define PNG_ALIGNED_MEMORY_SUPPORTED + #define PNG_BENIGN_ERRORS_SUPPORTED + #define PNG_BENIGN_READ_ERRORS_SUPPORTED + #define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED + #define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + #define PNG_COLORSPACE_SUPPORTED + #define PNG_CONSOLE_IO_SUPPORTED + #define PNG_EASY_ACCESS_SUPPORTED + #define PNG_FIXED_POINT_SUPPORTED + #define PNG_FLOATING_ARITHMETIC_SUPPORTED + #define PNG_FLOATING_POINT_SUPPORTED + #define PNG_FORMAT_AFIRST_SUPPORTED + #define PNG_FORMAT_BGR_SUPPORTED + #define PNG_GAMMA_SUPPORTED + #define PNG_GET_PALETTE_MAX_SUPPORTED + #define PNG_HANDLE_AS_UNKNOWN_SUPPORTED + #define PNG_INCH_CONVERSIONS_SUPPORTED + #define PNG_INFO_IMAGE_SUPPORTED + #define PNG_IO_STATE_SUPPORTED + #define PNG_MNG_FEATURES_SUPPORTED + #define PNG_POINTER_INDEXING_SUPPORTED + #define PNG_PROGRESSIVE_READ_SUPPORTED + #define PNG_READ_16BIT_SUPPORTED + #define PNG_READ_ALPHA_MODE_SUPPORTED + #define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + #define PNG_READ_BACKGROUND_SUPPORTED + #define PNG_READ_BGR_SUPPORTED + #define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + #define PNG_READ_COMPOSITE_NODIV_SUPPORTED + #define PNG_READ_COMPRESSED_TEXT_SUPPORTED + #define PNG_READ_EXPAND_16_SUPPORTED + #define PNG_READ_EXPAND_SUPPORTED + #define PNG_READ_FILLER_SUPPORTED + #define PNG_READ_GAMMA_SUPPORTED + #define PNG_READ_GET_PALETTE_MAX_SUPPORTED + #define PNG_READ_GRAY_TO_RGB_SUPPORTED + #define PNG_READ_INTERLACING_SUPPORTED + #define PNG_READ_INT_FUNCTIONS_SUPPORTED + #define PNG_READ_INVERT_ALPHA_SUPPORTED + #define PNG_READ_INVERT_SUPPORTED + #define PNG_READ_OPT_PLTE_SUPPORTED + #define PNG_READ_PACKSWAP_SUPPORTED + #define PNG_READ_PACK_SUPPORTED + #define PNG_READ_QUANTIZE_SUPPORTED + #define PNG_READ_RGB_TO_GRAY_SUPPORTED + #define PNG_READ_SCALE_16_TO_8_SUPPORTED + #define PNG_READ_SHIFT_SUPPORTED + #define PNG_READ_STRIP_16_TO_8_SUPPORTED + #define PNG_READ_STRIP_ALPHA_SUPPORTED + #define PNG_READ_SUPPORTED + #define PNG_READ_SWAP_ALPHA_SUPPORTED + #define PNG_READ_SWAP_SUPPORTED + #define PNG_READ_TEXT_SUPPORTED + #define PNG_READ_TRANSFORMS_SUPPORTED + #define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + #define PNG_READ_USER_CHUNKS_SUPPORTED + #define PNG_READ_USER_TRANSFORM_SUPPORTED + #define PNG_READ_bKGD_SUPPORTED + #define PNG_READ_cHRM_SUPPORTED + #define PNG_READ_gAMA_SUPPORTED + #define PNG_READ_hIST_SUPPORTED + #define PNG_READ_iCCP_SUPPORTED + #define PNG_READ_iTXt_SUPPORTED + #define PNG_READ_oFFs_SUPPORTED + #define PNG_READ_pCAL_SUPPORTED + #define PNG_READ_pHYs_SUPPORTED + #define PNG_READ_sBIT_SUPPORTED + #define PNG_READ_sCAL_SUPPORTED + #define PNG_READ_sPLT_SUPPORTED + #define PNG_READ_sRGB_SUPPORTED + #define PNG_READ_tEXt_SUPPORTED + #define PNG_READ_tIME_SUPPORTED + #define PNG_READ_tRNS_SUPPORTED + #define PNG_READ_zTXt_SUPPORTED + #define PNG_SAVE_INT_32_SUPPORTED + #define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + #define PNG_SEQUENTIAL_READ_SUPPORTED + #define PNG_SET_CHUNK_CACHE_LIMIT_SUPPORTED + #define PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED + #define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + #define PNG_SET_USER_LIMITS_SUPPORTED + #define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED + #define PNG_SIMPLIFIED_READ_BGR_SUPPORTED + #define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED + #define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED + #define PNG_STDIO_SUPPORTED + #define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + #define PNG_TEXT_SUPPORTED + #define PNG_TIME_RFC1123_SUPPORTED + #define PNG_UNKNOWN_CHUNKS_SUPPORTED + #define PNG_USER_CHUNKS_SUPPORTED + #define PNG_USER_LIMITS_SUPPORTED + #define PNG_USER_TRANSFORM_INFO_SUPPORTED + #define PNG_USER_TRANSFORM_PTR_SUPPORTED + #define PNG_WARNINGS_SUPPORTED + #define PNG_WRITE_16BIT_SUPPORTED + #define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + #define PNG_WRITE_BGR_SUPPORTED + #define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + #define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED + #define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + #define PNG_WRITE_FILLER_SUPPORTED + #define PNG_WRITE_FILTER_SUPPORTED + #define PNG_WRITE_FLUSH_SUPPORTED + #define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED + #define PNG_WRITE_INTERLACING_SUPPORTED + #define PNG_WRITE_INT_FUNCTIONS_SUPPORTED + #define PNG_WRITE_INVERT_ALPHA_SUPPORTED + #define PNG_WRITE_INVERT_SUPPORTED + #define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + #define PNG_WRITE_PACKSWAP_SUPPORTED + #define PNG_WRITE_PACK_SUPPORTED + #define PNG_WRITE_SHIFT_SUPPORTED + #define PNG_WRITE_SUPPORTED + #define PNG_WRITE_SWAP_ALPHA_SUPPORTED + #define PNG_WRITE_SWAP_SUPPORTED + #define PNG_WRITE_TEXT_SUPPORTED + #define PNG_WRITE_TRANSFORMS_SUPPORTED + #define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + #define PNG_WRITE_USER_TRANSFORM_SUPPORTED + #define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + #define PNG_WRITE_bKGD_SUPPORTED + #define PNG_WRITE_cHRM_SUPPORTED + #define PNG_WRITE_gAMA_SUPPORTED + #define PNG_WRITE_hIST_SUPPORTED + #define PNG_WRITE_iCCP_SUPPORTED + #define PNG_WRITE_iTXt_SUPPORTED + #define PNG_WRITE_oFFs_SUPPORTED + #define PNG_WRITE_pCAL_SUPPORTED + #define PNG_WRITE_pHYs_SUPPORTED + #define PNG_WRITE_sBIT_SUPPORTED + #define PNG_WRITE_sCAL_SUPPORTED + #define PNG_WRITE_sPLT_SUPPORTED + #define PNG_WRITE_sRGB_SUPPORTED + #define PNG_WRITE_tEXt_SUPPORTED + #define PNG_WRITE_tIME_SUPPORTED + #define PNG_WRITE_tRNS_SUPPORTED + #define PNG_WRITE_zTXt_SUPPORTED + #define PNG_bKGD_SUPPORTED + #define PNG_cHRM_SUPPORTED + #define PNG_gAMA_SUPPORTED + #define PNG_hIST_SUPPORTED + #define PNG_iCCP_SUPPORTED + #define PNG_iTXt_SUPPORTED + #define PNG_oFFs_SUPPORTED + #define PNG_pCAL_SUPPORTED + #define PNG_pHYs_SUPPORTED + #define PNG_sBIT_SUPPORTED + #define PNG_sCAL_SUPPORTED + #define PNG_sPLT_SUPPORTED + #define PNG_sRGB_SUPPORTED + #define PNG_tEXt_SUPPORTED + #define PNG_tIME_SUPPORTED + #define PNG_tRNS_SUPPORTED + #define PNG_zTXt_SUPPORTED + + #define PNG_STRING_COPYRIGHT ""; + #define PNG_STRING_NEWLINE "\n" + #define PNG_LITERAL_SHARP 0x23 + #define PNG_LITERAL_LEFT_SQUARE_BRACKET 0x5b + #define PNG_LITERAL_RIGHT_SQUARE_BRACKET 0x5d + + #define PNG_API_RULE 0 + #define PNG_CALLOC_SUPPORTED + #define PNG_COST_SHIFT 3 + #define PNG_DEFAULT_READ_MACROS 1 + #define PNG_GAMMA_THRESHOLD_FIXED 5000 + #define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE + #define PNG_INFLATE_BUF_SIZE 1024 + #define PNG_MAX_GAMMA_8 11 + #define PNG_QUANTIZE_BLUE_BITS 5 + #define PNG_QUANTIZE_GREEN_BITS 5 + #define PNG_QUANTIZE_RED_BITS 5 + #define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1) + #define PNG_TEXT_Z_DEFAULT_STRATEGY 0 + #define PNG_WEIGHT_SHIFT 8 + #define PNG_ZBUF_SIZE 8192 + #define PNG_Z_DEFAULT_COMPRESSION (-1) + #define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0 + #define PNG_Z_DEFAULT_STRATEGY 1 + #define PNG_sCAL_PRECISION 5 + #define PNG_sRGB_PROFILE_CHECKS 2 + + #define png_debug(a, b) + #define png_debug1(a, b, c) + #define png_debug2(a, b, c, d) #include "pnglib/png.h" #include "pnglib/pngconf.h" @@ -79,6 +274,10 @@ namespace pnglibNamespace #include "pnglib/pngwrite.c" #include "pnglib/pngwtran.c" #include "pnglib/pngwutil.c" + + #if JUCE_CLANG + #pragma clang diagnostic pop + #endif #else extern "C" { @@ -90,6 +289,7 @@ namespace pnglibNamespace #undef max #undef min +#undef fdopen #if JUCE_MSVC #pragma warning (pop) @@ -100,29 +300,34 @@ namespace PNGHelpers { using namespace pnglibNamespace; + static void JUCE_CDECL writeDataCallback (png_structp png, png_bytep data, png_size_t length) + { + static_cast (png_get_io_ptr (png))->write (data, length); + } + + #if ! JUCE_USING_COREIMAGE_LOADER static void JUCE_CDECL readCallback (png_structp png, png_bytep data, png_size_t length) { static_cast (png_get_io_ptr (png))->read (data, (int) length); } - static void JUCE_CDECL writeDataCallback (png_structp png, png_bytep data, png_size_t length) - { - static_cast (png_get_io_ptr (png))->write (data, (int) length); - } - struct PNGErrorStruct {}; static void JUCE_CDECL errorCallback (png_structp, png_const_charp) { throw PNGErrorStruct(); } + + static void JUCE_CDECL warningCallback (png_structp, png_const_charp) {} + #endif } //============================================================================== PNGImageFormat::PNGImageFormat() {} PNGImageFormat::~PNGImageFormat() {} -String PNGImageFormat::getFormatName() { return "PNG"; } +String PNGImageFormat::getFormatName() { return "PNG"; } +bool PNGImageFormat::usesFileExtension (const File& f) { return f.hasFileExtension ("png"); } bool PNGImageFormat::canUnderstand (InputStream& in) { @@ -135,42 +340,37 @@ bool PNGImageFormat::canUnderstand (InputStream& in) && header[3] == 'G'; } -#if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER +#if JUCE_USING_COREIMAGE_LOADER Image juce_loadWithCoreImage (InputStream& input); #endif Image PNGImageFormat::decodeImage (InputStream& in) { -#if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER +#if JUCE_USING_COREIMAGE_LOADER return juce_loadWithCoreImage (in); #else using namespace pnglibNamespace; Image image; - png_structp pngReadStruct; - png_infop pngInfoStruct; - - pngReadStruct = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0); - - if (pngReadStruct != 0) + if (png_structp pngReadStruct = png_create_read_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0)) { try { - pngInfoStruct = png_create_info_struct (pngReadStruct); + png_infop pngInfoStruct = png_create_info_struct (pngReadStruct); - if (pngInfoStruct == 0) + if (pngInfoStruct == nullptr) { png_destroy_read_struct (&pngReadStruct, 0, 0); return Image::null; } - png_set_error_fn (pngReadStruct, 0, PNGHelpers::errorCallback, PNGHelpers::errorCallback ); + png_set_error_fn (pngReadStruct, 0, PNGHelpers::errorCallback, PNGHelpers::warningCallback); // read the header.. png_set_read_fn (pngReadStruct, &in, PNGHelpers::readCallback); - png_uint_32 width, height; - int bitDepth, colorType, interlaceType; + png_uint_32 width = 0, height = 0; + int bitDepth = 0, colorType = 0, interlaceType; png_read_info (pngReadStruct, pngInfoStruct); @@ -200,21 +400,20 @@ Image PNGImageFormat::decodeImage (InputStream& in) || pngInfoStruct->num_trans > 0; // Load the image into a temp buffer in the pnglib format.. - HeapBlock tempBuffer (height * (width << 2)); + const size_t lineStride = width * 4; + HeapBlock tempBuffer (height * lineStride); + HeapBlock rows (height); + for (size_t y = 0; y < height; ++y) + rows[y] = (png_bytep) (tempBuffer + lineStride * y); + + try { - HeapBlock rows (height); - for (int y = (int) height; --y >= 0;) - rows[y] = (png_bytep) (tempBuffer + (width << 2) * y); - - try - { - png_read_image (pngReadStruct, rows); - png_read_end (pngReadStruct, pngInfoStruct); - } - catch (PNGHelpers::PNGErrorStruct&) - {} + png_read_image (pngReadStruct, rows); + png_read_end (pngReadStruct, pngInfoStruct); } + catch (PNGHelpers::PNGErrorStruct&) + {} png_destroy_read_struct (&pngReadStruct, &pngInfoStruct, 0); @@ -226,15 +425,11 @@ Image PNGImageFormat::decodeImage (InputStream& in) hasAlphaChan = image.hasAlphaChannel(); // (the native image creator may not give back what we expect) const Image::BitmapData destData (image, Image::BitmapData::writeOnly); - uint8* srcRow = tempBuffer; - uint8* destRow = destData.data; for (int y = 0; y < (int) height; ++y) { - const uint8* src = srcRow; - srcRow += (width << 2); - uint8* dest = destRow; - destRow += destData.lineStride; + const uint8* src = rows[y]; + uint8* dest = destData.getLinePointer (y); if (hasAlphaChan) { @@ -273,14 +468,14 @@ bool PNGImageFormat::writeImageToStream (const Image& image, OutputStream& out) png_structp pngWriteStruct = png_create_write_struct (PNG_LIBPNG_VER_STRING, 0, 0, 0); - if (pngWriteStruct == 0) + if (pngWriteStruct == nullptr) return false; png_infop pngInfoStruct = png_create_info_struct (pngWriteStruct); - if (pngInfoStruct == 0) + if (pngInfoStruct == nullptr) { - png_destroy_write_struct (&pngWriteStruct, (png_infopp) 0); + png_destroy_write_struct (&pngWriteStruct, (png_infopp) nullptr); return false; } diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.c index 108de4e..67e7e2e 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.c @@ -1,86 +1,20 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h */ -#define PNG_INTERNAL -#define PNG_NO_EXTERN -#include "png.h" +#include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef version_1_2_21 Your_png_h_is_not_version_1_2_21; - -/* Version information for C files. This had better match the version - * string defined in png.h. */ - -#ifdef PNG_USE_GLOBAL_ARRAYS -/* png_libpng_ver was changed to a function in version 1.0.5c */ -PNG_CONST char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING; - -#ifdef PNG_READ_SUPPORTED - -/* png_sig was changed to a function in version 1.0.5c */ -/* Place to hold the signature string for a PNG file. */ -PNG_CONST png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10}; -#endif /* PNG_READ_SUPPORTED */ - -/* Invoke global declarations for constant strings for known chunk types */ -PNG_IHDR; -PNG_IDAT; -PNG_IEND; -PNG_PLTE; -PNG_bKGD; -PNG_cHRM; -PNG_gAMA; -PNG_hIST; -PNG_iCCP; -PNG_iTXt; -PNG_oFFs; -PNG_pCAL; -PNG_sCAL; -PNG_pHYs; -PNG_sBIT; -PNG_sPLT; -PNG_sRGB; -PNG_tEXt; -PNG_tIME; -PNG_tRNS; -PNG_zTXt; - -#ifdef PNG_READ_SUPPORTED -/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - -/* start of interlace block */ -PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; - -/* offset to next interlace block */ -PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; - -/* start of interlace block in the y direction */ -PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; - -/* offset to next interlace block in the y direction */ -PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; - -/* Height of interlace block. This is not currently used - if you need - * it, uncomment it here and in png.h -PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; -*/ - -/* Mask to determine which pixels are valid in a pass */ -PNG_CONST int FARDATA png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; - -/* Mask to determine which pixels to overwrite while displaying */ -PNG_CONST int FARDATA png_pass_dsp_mask[] - = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; - -#endif /* PNG_READ_SUPPORTED */ -#endif /* PNG_USE_GLOBAL_ARRAYS */ +typedef png_libpng_version_1_6_1 Your_png_h_is_not_version_1_6_1; /* 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 @@ -90,12 +24,15 @@ PNG_CONST int FARDATA png_pass_dsp_mask[] #ifdef PNG_READ_SUPPORTED void PNGAPI -png_set_sig_bytes(png_structp png_ptr, int num_bytes) +png_set_sig_bytes(png_structrp png_ptr, int num_bytes) { - if(png_ptr == NULL) return; - png_debug(1, "in png_set_sig_bytes\n"); + png_debug(1, "in png_set_sig_bytes"); + + if (png_ptr == NULL) + return; + if (num_bytes > 8) - png_error(png_ptr, "Too many bytes for PNG signature."); + png_error(png_ptr, "Too many bytes for PNG signature"); png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); } @@ -106,14 +43,16 @@ png_set_sig_bytes(png_structp png_ptr, int num_bytes) * 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). + * PNG signature (this is the same behavior as strcmp, memcmp, etc). */ int PNGAPI -png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +png_sig_cmp(png_const_bytep sig, png_size_t start, png_size_t num_to_check) { png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) num_to_check = 8; + else if (num_to_check < 1) return (-1); @@ -123,84 +62,47 @@ png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) if (start + num_to_check > 8) num_to_check = 8 - start; - return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); + return ((int)(memcmp(&sig[start], &png_signature[start], num_to_check))); } -#if defined(PNG_1_0_X) || defined(PNG_1_2_X) -/* (Obsolete) function to check signature bytes. It does not allow one - * to check a partial signature. This function might be removed in the - * future - use png_sig_cmp(). Returns true (nonzero) if the file is PNG. - */ -int PNGAPI -png_check_sig(png_bytep sig, int num) -{ - return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); -} -#endif #endif /* PNG_READ_SUPPORTED */ #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -/* Function to allocate memory for zlib and clear it to 0. */ -#ifdef PNG_1_0_X -voidpf PNGAPI -#else -voidpf /* private */ -#endif -png_zalloc(voidpf png_ptr, uInt items, uInt size) +/* Function to allocate memory for zlib */ +PNG_FUNCTION(voidpf /* PRIVATE */, +png_zalloc,(voidpf png_ptr, uInt items, uInt size),PNG_ALLOCATED) { - png_voidp ptr; - png_structp p=(png_structp)png_ptr; - png_uint_32 save_flags=p->flags; - png_uint_32 num_bytes; + png_alloc_size_t num_bytes = size; - if(png_ptr == NULL) return (NULL); - if (items > PNG_UINT_32_MAX/size) + if (png_ptr == NULL) + return NULL; + + if (items >= (~(png_alloc_size_t)0)/size) { - png_warning (p, "Potential overflow in png_zalloc()"); - return (NULL); + png_warning (png_voidcast(png_structrp, png_ptr), + "Potential overflow in png_zalloc()"); + return NULL; } - num_bytes = (png_uint_32)items * size; - p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; - ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); - p->flags=save_flags; - -#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) - if (ptr == NULL) - return ((voidpf)ptr); - - if (num_bytes > (png_uint_32)0x8000L) - { - png_memset(ptr, 0, (png_size_t)0x8000L); - png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, - (png_size_t)(num_bytes - (png_uint_32)0x8000L)); - } - else - { - png_memset(ptr, 0, (png_size_t)num_bytes); - } -#endif - return ((voidpf)ptr); + num_bytes *= items; + return png_malloc_warn(png_voidcast(png_structrp, png_ptr), num_bytes); } -/* function to free memory for zlib */ -#ifdef PNG_1_0_X -void PNGAPI -#else -void /* private */ -#endif +/* Function to free memory for zlib */ +void /* PRIVATE */ png_zfree(voidpf png_ptr, voidpf ptr) { - png_free((png_structp)png_ptr, (png_voidp)ptr); + png_free(png_voidcast(png_const_structrp,png_ptr), 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_reset_crc(png_structrp png_ptr) { - png_ptr->crc = crc32(0, Z_NULL, 0); + /* The cast is safe because the crc is a 32 bit value. */ + png_ptr->crc = (png_uint_32)crc32(0, Z_NULL, 0); } /* Calculate the CRC over a section of data. We can only pass as @@ -209,403 +111,524 @@ png_reset_crc(png_structp png_ptr) * trouble of calculating it. */ void /* PRIVATE */ -png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +png_calculate_crc(png_structrp png_ptr, png_const_bytep ptr, png_size_t length) { int need_crc = 1; - if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + if (PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name)) { if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) need_crc = 0; } - else /* critical */ + + else /* critical */ { if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) need_crc = 0; } - if (need_crc) - png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); + /* 'uLong' is defined in zlib.h as unsigned long; this means that on some + * systems it is a 64 bit value. crc32, however, returns 32 bits so the + * following cast is safe. 'uInt' may be no more than 16 bits, so it is + * necessary to perform a loop here. + */ + if (need_crc && length > 0) + { + uLong crc = png_ptr->crc; /* Should never issue a warning */ + + do + { + uInt safe_length = (uInt)length; + if (safe_length == 0) + safe_length = (uInt)-1; /* evil, but safe */ + + crc = crc32(crc, ptr, safe_length); + + /* The following should never issue compiler warnings; if they do the + * target system has characteristics that will probably violate other + * assumptions within the libpng code. + */ + ptr += safe_length; + length -= safe_length; + } + while (length > 0); + + /* And the following is always safe because the crc is only 32 bits. */ + png_ptr->crc = (png_uint_32)crc; + } } -/* 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. +/* Check a user supplied version number, called from both read and write + * functions that create a png_struct. */ -png_infop PNGAPI -png_create_info_struct(png_structp png_ptr) +int +png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver) { - png_infop info_ptr; + if (user_png_ver) + { + int i = 0; - png_debug(1, "in png_create_info_struct\n"); - if(png_ptr == NULL) return (NULL); -#ifdef PNG_USER_MEM_SUPPORTED - info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, - png_ptr->malloc_fn, png_ptr->mem_ptr); -#else - info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + } + + else + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first and third digits (note that when we reach version + * 1.10 we will need to check the fourth symbol, namely user_png_ver[3]). + */ + 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[3] != png_libpng_ver[3])) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#ifdef PNG_WARNINGS_SUPPORTED + size_t pos = 0; + char m[128]; + + pos = png_safecat(m, (sizeof m), pos, + "Application built with libpng-"); + pos = png_safecat(m, (sizeof m), pos, user_png_ver); + pos = png_safecat(m, (sizeof m), pos, " but running with "); + png_safecat(m, (sizeof m), pos, png_libpng_ver); + + png_warning(png_ptr, m); #endif - if (info_ptr != NULL) - png_info_init_3(&info_ptr, png_sizeof(png_info)); - return (info_ptr); +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags = 0; +#endif + + return 0; + } + } + + /* Success return. */ + return 1; +} + +/* Generic function to create a png_struct for either read or write - this + * contains the common initialization. + */ +PNG_FUNCTION(png_structp /* PRIVATE */, +png_create_png_struct,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp, + png_malloc_ptr, png_free_ptr),PNG_ALLOCATED) +{ + png_struct create_struct; +# ifdef PNG_SETJMP_SUPPORTED + jmp_buf create_jmp_buf; +# endif + + /* This temporary stack-allocated structure is used to provide a place to + * build enough context to allow the user provided memory allocator (if any) + * to be called. + */ + memset(&create_struct, 0, (sizeof create_struct)); + + /* Added at libpng-1.2.6 */ +# ifdef PNG_USER_LIMITS_SUPPORTED + create_struct.user_width_max = PNG_USER_WIDTH_MAX; + create_struct.user_height_max = PNG_USER_HEIGHT_MAX; + +# ifdef PNG_USER_CHUNK_CACHE_MAX + /* Added at libpng-1.2.43 and 1.4.0 */ + create_struct.user_chunk_cache_max = PNG_USER_CHUNK_CACHE_MAX; +# endif + +# ifdef PNG_USER_CHUNK_MALLOC_MAX + /* Added at libpng-1.2.43 and 1.4.1, required only for read but exists + * in png_struct regardless. + */ + create_struct.user_chunk_malloc_max = PNG_USER_CHUNK_MALLOC_MAX; +# endif +# endif + + /* The following two API calls simply set fields in png_struct, so it is safe + * to do them now even though error handling is not yet set up. + */ +# ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(&create_struct, mem_ptr, malloc_fn, free_fn); +# endif + + /* (*error_fn) can return control to the caller after the error_ptr is set, + * this will result in a memory leak unless the error_fn does something + * extremely sophisticated. The design lacks merit but is implicit in the + * API. + */ + png_set_error_fn(&create_struct, error_ptr, error_fn, warn_fn); + +# ifdef PNG_SETJMP_SUPPORTED + if (!setjmp(create_jmp_buf)) + { + /* Temporarily fake out the longjmp information until we have + * successfully completed this function. This only works if we have + * setjmp() support compiled in, but it is safe - this stuff should + * never happen. + */ + create_struct.jmp_buf_ptr = &create_jmp_buf; + create_struct.jmp_buf_size = 0; /*stack allocation*/ + create_struct.longjmp_fn = longjmp; +# else + { +# endif + /* Call the general version checker (shared with read and write code): + */ + if (png_user_version_check(&create_struct, user_png_ver)) + { + png_structrp png_ptr = png_voidcast(png_structrp, + png_malloc_warn(&create_struct, (sizeof *png_ptr))); + + if (png_ptr != NULL) + { + /* png_ptr->zstream holds a back-pointer to the png_struct, so + * this can only be done now: + */ + create_struct.zstream.zalloc = png_zalloc; + create_struct.zstream.zfree = png_zfree; + create_struct.zstream.opaque = png_ptr; + +# ifdef PNG_SETJMP_SUPPORTED + /* Eliminate the local error handling: */ + create_struct.jmp_buf_ptr = NULL; + create_struct.jmp_buf_size = 0; + create_struct.longjmp_fn = 0; +# endif + + *png_ptr = create_struct; + + /* This is the successful return point */ + return png_ptr; + } + } + } + + /* A longjmp because of a bug in the application storage allocator or a + * simple failure to allocate the png_struct. + */ + return NULL; +} + +/* Allocate the memory for an info_struct for the application. */ +PNG_FUNCTION(png_infop,PNGAPI +png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED) +{ + png_inforp info_ptr; + + png_debug(1, "in png_create_info_struct"); + + if (png_ptr == NULL) + return NULL; + + /* Use the internal API that does not (or at least should not) error out, so + * that this call always returns ok. The application typically sets up the + * error handling *after* creating the info_struct because this is the way it + * has always been done in 'example.c'. + */ + info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr, + (sizeof *info_ptr))); + + if (info_ptr != NULL) + memset(info_ptr, 0, (sizeof *info_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. + * useful for some applications. From libpng 1.6.0 this function is also used + * internally to implement the png_info release part of the 'struct' destroy + * APIs. This ensures that all possible approaches free the same data (all of + * it). */ void PNGAPI -png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +png_destroy_info_struct(png_const_structrp png_ptr, png_infopp info_ptr_ptr) { - png_infop info_ptr = NULL; - if(png_ptr == NULL) return; + png_inforp info_ptr = NULL; + + png_debug(1, "in png_destroy_info_struct"); + + if (png_ptr == NULL) + return; - png_debug(1, "in png_destroy_info_struct\n"); if (info_ptr_ptr != NULL) info_ptr = *info_ptr_ptr; if (info_ptr != NULL) { - png_info_destroy(png_ptr, info_ptr); - -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, - png_ptr->mem_ptr); -#else - png_destroy_struct((png_voidp)info_ptr); -#endif + /* Do this first in case of an error below; if the app implements its own + * memory management this can lead to png_free calling png_error, which + * will abort this routine and return control to the app error handler. + * An infinite loop may result if it then tries to free the same info + * ptr. + */ *info_ptr_ptr = NULL; + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + memset(info_ptr, 0, (sizeof *info_ptr)); + png_free(png_ptr, info_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. + * instead. Use deprecated in 1.6.0, internal use removed (used internally it + * is just a memset). + * + * NOTE: it is almost inconceivable that this API is used because it bypasses + * the user-memory mechanism and the user error handling/warning mechanisms in + * those cases where it does anything other than a memset. */ -#if defined(PNG_1_0_X) || defined(PNG_1_2_X) -#undef png_info_init -void PNGAPI -png_info_init(png_infop info_ptr) +PNG_FUNCTION(void,PNGAPI +png_info_init_3,(png_infopp ptr_ptr, png_size_t png_info_struct_size), + PNG_DEPRECATED) { - /* We only come here via pre-1.0.12-compiled applications */ - png_info_init_3(&info_ptr, 0); -} -#endif + png_inforp info_ptr = *ptr_ptr; -void PNGAPI -png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) -{ - png_infop info_ptr = *ptr_ptr; + png_debug(1, "in png_info_init_3"); - if(info_ptr == NULL) return; + if (info_ptr == NULL) + return; - png_debug(1, "in png_info_init_3\n"); + if ((sizeof (png_info)) > png_info_struct_size) + { + *ptr_ptr = NULL; + /* The following line is why this API should not be used: */ + free(info_ptr); + info_ptr = png_voidcast(png_inforp, png_malloc_base(NULL, + (sizeof *info_ptr))); + *ptr_ptr = info_ptr; + } - if(png_sizeof(png_info) > png_info_struct_size) - { - png_destroy_struct(info_ptr); - info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); - *ptr_ptr = info_ptr; - } - - /* set everything to 0 */ - png_memset(info_ptr, 0, png_sizeof (png_info)); + /* Set everything to 0 */ + memset(info_ptr, 0, (sizeof *info_ptr)); } -#ifdef PNG_FREE_ME_SUPPORTED +/* The following API is not called internally */ void PNGAPI -png_data_freer(png_structp png_ptr, png_infop info_ptr, +png_data_freer(png_const_structrp png_ptr, png_inforp info_ptr, int freer, png_uint_32 mask) { - png_debug(1, "in png_data_freer\n"); + png_debug(1, "in png_data_freer"); + if (png_ptr == NULL || info_ptr == NULL) return; - if(freer == PNG_DESTROY_WILL_FREE_DATA) + + if (freer == PNG_DESTROY_WILL_FREE_DATA) info_ptr->free_me |= mask; - else if(freer == PNG_USER_WILL_FREE_DATA) + + else if (freer == PNG_USER_WILL_FREE_DATA) info_ptr->free_me &= ~mask; + else - png_warning(png_ptr, - "Unknown freer parameter in png_data_freer."); + png_error(png_ptr, "Unknown freer parameter in png_data_freer"); } -#endif void PNGAPI -png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, +png_free_data(png_const_structrp png_ptr, png_inforp info_ptr, png_uint_32 mask, int num) { - png_debug(1, "in png_free_data\n"); + png_debug(1, "in png_free_data"); + if (png_ptr == NULL || info_ptr == NULL) return; -#if defined(PNG_TEXT_SUPPORTED) -/* free text item num or (if num == -1) all text items */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) -#else -if (mask & PNG_FREE_TEXT) -#endif -{ - if (num != -1) +#ifdef PNG_TEXT_SUPPORTED + /* Free text item num or (if num == -1) all text items */ + if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) { - if (info_ptr->text && info_ptr->text[num].key) - { - png_free(png_ptr, info_ptr->text[num].key); - info_ptr->text[num].key = NULL; - } - } - else - { - int i; - for (i = 0; i < info_ptr->num_text; i++) - png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); - png_free(png_ptr, info_ptr->text); - info_ptr->text = NULL; - info_ptr->num_text=0; - } -} -#endif - -#if defined(PNG_tRNS_SUPPORTED) -/* free any tRNS entry */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) -#else -if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) -#endif -{ - png_free(png_ptr, info_ptr->trans); - info_ptr->valid &= ~PNG_INFO_tRNS; -#ifndef PNG_FREE_ME_SUPPORTED - png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; -#endif - info_ptr->trans = NULL; -} -#endif - -#if defined(PNG_sCAL_SUPPORTED) -/* free any sCAL entry */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) -#else -if (mask & PNG_FREE_SCAL) -#endif -{ -#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) - png_free(png_ptr, info_ptr->scal_s_width); - png_free(png_ptr, info_ptr->scal_s_height); - info_ptr->scal_s_width = NULL; - info_ptr->scal_s_height = NULL; -#endif - info_ptr->valid &= ~PNG_INFO_sCAL; -} -#endif - -#if defined(PNG_pCAL_SUPPORTED) -/* free any pCAL entry */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) -#else -if (mask & PNG_FREE_PCAL) -#endif -{ - png_free(png_ptr, info_ptr->pcal_purpose); - png_free(png_ptr, info_ptr->pcal_units); - info_ptr->pcal_purpose = NULL; - info_ptr->pcal_units = NULL; - if (info_ptr->pcal_params != NULL) - { - int i; - for (i = 0; i < (int)info_ptr->pcal_nparams; i++) - { - png_free(png_ptr, info_ptr->pcal_params[i]); - info_ptr->pcal_params[i]=NULL; - } - png_free(png_ptr, info_ptr->pcal_params); - info_ptr->pcal_params = NULL; - } - info_ptr->valid &= ~PNG_INFO_pCAL; -} -#endif - -#if defined(PNG_iCCP_SUPPORTED) -/* free any iCCP entry */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) -#else -if (mask & PNG_FREE_ICCP) -#endif -{ - png_free(png_ptr, info_ptr->iccp_name); - png_free(png_ptr, info_ptr->iccp_profile); - info_ptr->iccp_name = NULL; - info_ptr->iccp_profile = NULL; - info_ptr->valid &= ~PNG_INFO_iCCP; -} -#endif - -#if defined(PNG_sPLT_SUPPORTED) -/* free a given sPLT entry, or (if num == -1) all sPLT entries */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) -#else -if (mask & PNG_FREE_SPLT) -#endif -{ - if (num != -1) - { - if(info_ptr->splt_palettes) + if (num != -1) { - png_free(png_ptr, info_ptr->splt_palettes[num].name); - png_free(png_ptr, info_ptr->splt_palettes[num].entries); - info_ptr->splt_palettes[num].name = NULL; - info_ptr->splt_palettes[num].entries = NULL; + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; } } - else +#endif + +#ifdef PNG_tRNS_SUPPORTED + /* Free any tRNS entry */ + if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) { - if(info_ptr->splt_palettes_num) - { + png_free(png_ptr, info_ptr->trans_alpha); + info_ptr->trans_alpha = NULL; + info_ptr->valid &= ~PNG_INFO_tRNS; + } +#endif + +#ifdef PNG_sCAL_SUPPORTED + /* Free any sCAL entry */ + if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; + info_ptr->valid &= ~PNG_INFO_sCAL; + } +#endif + +#ifdef PNG_pCAL_SUPPORTED + /* Free any pCAL entry */ + if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + unsigned int i; + for (i = 0; i < info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i] = NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; + } +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* Free any profile entry */ + if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; + } +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Free a given sPLT entry, or (if num == -1) all sPLT entries */ + if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + + else + { + if (info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, (int)i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } + } +#endif + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) + { + if (num != -1) + { + if (info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + + else + { int i; - for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) - png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); - png_free(png_ptr, info_ptr->splt_palettes); - info_ptr->splt_palettes = NULL; - info_ptr->splt_palettes_num = 0; - } - info_ptr->valid &= ~PNG_INFO_sPLT; + if (info_ptr->unknown_chunks_num) + { + for (i = 0; i < info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, (int)i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } } -} #endif -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) - if(png_ptr->unknown_chunk.data) - { - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; - } -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) -#else -if (mask & PNG_FREE_UNKN) +#ifdef PNG_hIST_SUPPORTED + /* Free any hIST entry */ + if ((mask & PNG_FREE_HIST) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; + } #endif -{ + + /* Free any PLTE entry that was internally allocated */ + if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) + { + png_free(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; + info_ptr->num_palette = 0; + } + +#ifdef PNG_INFO_IMAGE_SUPPORTED + /* Free any image bits attached to the info structure */ + if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) + { + if (info_ptr->row_pointers) + { + png_uint_32 row; + for (row = 0; row < info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row] = NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers = NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; + } +#endif + if (num != -1) - { - if(info_ptr->unknown_chunks) - { - png_free(png_ptr, info_ptr->unknown_chunks[num].data); - info_ptr->unknown_chunks[num].data = NULL; - } - } - else - { - int i; + mask &= ~PNG_FREE_MUL; - if(info_ptr->unknown_chunks_num) - { - for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) - png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); - - png_free(png_ptr, info_ptr->unknown_chunks); - info_ptr->unknown_chunks = NULL; - info_ptr->unknown_chunks_num = 0; - } - } -} -#endif - -#if defined(PNG_hIST_SUPPORTED) -/* free any hIST entry */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_HIST) & info_ptr->free_me) -#else -if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) -#endif -{ - png_free(png_ptr, info_ptr->hist); - info_ptr->hist = NULL; - info_ptr->valid &= ~PNG_INFO_hIST; -#ifndef PNG_FREE_ME_SUPPORTED - png_ptr->flags &= ~PNG_FLAG_FREE_HIST; -#endif -} -#endif - -/* free any PLTE entry that was internally allocated */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) -#else -if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) -#endif -{ - png_zfree(png_ptr, info_ptr->palette); - info_ptr->palette = NULL; - info_ptr->valid &= ~PNG_INFO_PLTE; -#ifndef PNG_FREE_ME_SUPPORTED - png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; -#endif - info_ptr->num_palette = 0; -} - -#if defined(PNG_INFO_IMAGE_SUPPORTED) -/* free any image bits attached to the info structure */ -#ifdef PNG_FREE_ME_SUPPORTED -if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) -#else -if (mask & PNG_FREE_ROWS) -#endif -{ - if(info_ptr->row_pointers) - { - int row; - for (row = 0; row < (int)info_ptr->height; row++) - { - png_free(png_ptr, info_ptr->row_pointers[row]); - info_ptr->row_pointers[row]=NULL; - } - png_free(png_ptr, info_ptr->row_pointers); - info_ptr->row_pointers=NULL; - } - info_ptr->valid &= ~PNG_INFO_IDAT; -} -#endif - -#ifdef PNG_FREE_ME_SUPPORTED - if(num == -1) - info_ptr->free_me &= ~mask; - else - info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); -#endif -} - -/* This is an internal routine to free any memory that the info struct is - * pointing to before re-using it or freeing the struct itself. Recall - * that png_free() checks for NULL pointers for us. - */ -void /* PRIVATE */ -png_info_destroy(png_structp png_ptr, png_infop info_ptr) -{ - png_debug(1, "in png_info_destroy\n"); - - png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); - -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) - if (png_ptr->num_chunk_list) - { - png_free(png_ptr, png_ptr->chunk_list); - png_ptr->chunk_list=NULL; - png_ptr->num_chunk_list=0; - } -#endif - - png_info_init_3(&info_ptr, png_sizeof(png_info)); + info_ptr->free_me &= ~mask; } #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ @@ -614,89 +637,149 @@ png_info_destroy(png_structp png_ptr, png_infop info_ptr) * pointer before png_write_destroy() or png_read_destroy() are called. */ png_voidp PNGAPI -png_get_io_ptr(png_structp png_ptr) +png_get_io_ptr(png_const_structrp png_ptr) { - if(png_ptr == NULL) return (NULL); + if (png_ptr == NULL) + return (NULL); + return (png_ptr->io_ptr); } #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -#if !defined(PNG_NO_STDIO) +# ifdef PNG_STDIO_SUPPORTED /* Initialize the default input/output functions for the PNG file. If you * use your own read or write routines, you can call either png_set_read_fn() * or png_set_write_fn() instead of png_init_io(). If you have defined - * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't - * necessarily available. + * PNG_NO_STDIO or otherwise disabled PNG_STDIO_SUPPORTED, you must use a + * function of your own because "FILE *" isn't necessarily available. */ void PNGAPI -png_init_io(png_structp png_ptr, png_FILE_p fp) +png_init_io(png_structrp png_ptr, png_FILE_p fp) { - png_debug(1, "in png_init_io\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_init_io"); + + if (png_ptr == NULL) + return; + png_ptr->io_ptr = (png_voidp)fp; } +# endif + +#ifdef PNG_SAVE_INT_32_SUPPORTED +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. Note that, + * the following works correctly even if png_int_32 has more than 32 bits + * (compare the more complex code required on read for sign extension.) + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} #endif -#if defined(PNG_TIME_RFC1123_SUPPORTED) +# ifdef PNG_TIME_RFC1123_SUPPORTED /* Convert the supplied time into an RFC 1123 string suitable for use in * a "Creation Time" or other text-based time string. */ -png_charp PNGAPI -png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +int PNGAPI +png_convert_to_rfc1123_buffer(char out[29], png_const_timep ptime) { static PNG_CONST char short_months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; - if(png_ptr == NULL) return (NULL); - if (png_ptr->time_buffer == NULL) + if (out == NULL) + return 0; + + if (ptime->year > 9999 /* RFC1123 limitation */ || + ptime->month == 0 || ptime->month > 12 || + ptime->day == 0 || ptime->day > 31 || + ptime->hour > 23 || ptime->minute > 59 || + ptime->second > 60) + return 0; + { - png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* - png_sizeof(char))); + size_t pos = 0; + char number_buf[5]; /* enough for a four-digit year */ + +# define APPEND_STRING(string) pos = png_safecat(out, 29, pos, (string)) +# define APPEND_NUMBER(format, value)\ + APPEND_STRING(PNG_FORMAT_NUMBER(number_buf, format, (value))) +# define APPEND(ch) if (pos < 28) out[pos++] = (ch) + + APPEND_NUMBER(PNG_NUMBER_FORMAT_u, (unsigned)ptime->day); + APPEND(' '); + APPEND_STRING(short_months[(ptime->month - 1)]); + APPEND(' '); + APPEND_NUMBER(PNG_NUMBER_FORMAT_u, ptime->year); + APPEND(' '); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->hour); + APPEND(':'); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->minute); + APPEND(':'); + APPEND_NUMBER(PNG_NUMBER_FORMAT_02u, (unsigned)ptime->second); + APPEND_STRING(" +0000"); /* This reliably terminates the buffer */ + +# undef APPEND +# undef APPEND_NUMBER +# undef APPEND_STRING } -#if defined(_WIN32_WCE) - { - wchar_t time_buf[29]; - wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), - ptime->day % 32, short_months[(ptime->month - 1) % 12], - ptime->year, ptime->hour % 24, ptime->minute % 60, - ptime->second % 61); - WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29, - NULL, NULL); - } -#else -#ifdef USE_FAR_KEYWORD - { - char near_time_buf[29]; - png_snprintf6(near_time_buf,29,"%d %s %d %02d:%02d:%02d +0000", - ptime->day % 32, short_months[(ptime->month - 1) % 12], - ptime->year, ptime->hour % 24, ptime->minute % 60, - ptime->second % 61); - png_memcpy(png_ptr->time_buffer, near_time_buf, - 29*png_sizeof(char)); - } -#else - png_snprintf6(png_ptr->time_buffer,29,"%d %s %d %02d:%02d:%02d +0000", - ptime->day % 32, short_months[(ptime->month - 1) % 12], - ptime->year, ptime->hour % 24, ptime->minute % 60, - ptime->second % 61); -#endif -#endif /* _WIN32_WCE */ - return ((png_charp)png_ptr->time_buffer); + return 1; } -#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +# if PNG_LIBPNG_VER < 10700 +/* To do: remove the following from libpng-1.7 */ +/* Original API that uses a private buffer in png_struct. + * Deprecated because it causes png_struct to carry a spurious temporary + * buffer (png_struct::time_buffer), better to have the caller pass this in. + */ +png_const_charp PNGAPI +png_convert_to_rfc1123(png_structrp png_ptr, png_const_timep ptime) +{ + if (png_ptr != NULL) + { + /* The only failure above if png_ptr != NULL is from an invalid ptime */ + if (!png_convert_to_rfc1123_buffer(png_ptr->time_buffer, ptime)) + png_warning(png_ptr, "Ignoring invalid time value"); + + else + return png_ptr->time_buffer; + } + + return NULL; +} +# endif +# endif /* PNG_TIME_RFC1123_SUPPORTED */ #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ -png_charp PNGAPI -png_get_copyright(png_structp png_ptr) +png_const_charp PNGAPI +png_get_copyright(png_const_structrp png_ptr) { - png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ - return ((png_charp) "\n libpng version 1.2.21 - October 4, 2007\n\ - Copyright (c) 1998-2007 Glenn Randers-Pehrson\n\ - Copyright (c) 1996-1997 Andreas Dilger\n\ - Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n"); + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef PNG_STRING_COPYRIGHT + return PNG_STRING_COPYRIGHT +#else +# ifdef __STDC__ + return PNG_STRING_NEWLINE \ + "libpng version 1.6.1 - March 28, 2013" PNG_STRING_NEWLINE \ + "Copyright (c) 1998-2013 Glenn Randers-Pehrson" PNG_STRING_NEWLINE \ + "Copyright (c) 1996-1997 Andreas Dilger" PNG_STRING_NEWLINE \ + "Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc." \ + PNG_STRING_NEWLINE; +# else + return "libpng version 1.6.1 - March 28, 2013\ + Copyright (c) 1998-2013 Glenn Randers-Pehrson\ + Copyright (c) 1996-1997 Andreas Dilger\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc."; +# endif +#endif } /* The following return the library version as a short string in the @@ -707,92 +790,3506 @@ png_get_copyright(png_structp png_ptr) * 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) +png_const_charp PNGAPI +png_get_libpng_ver(png_const_structrp 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); + return png_get_header_ver(png_ptr); } -png_charp PNGAPI -png_get_header_ver(png_structp png_ptr) +png_const_charp PNGAPI +png_get_header_ver(png_const_structrp png_ptr) { /* Version of *.h files used when building libpng */ - png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ - return ((png_charp) PNG_LIBPNG_VER_STRING); + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ + return PNG_LIBPNG_VER_STRING; } -png_charp PNGAPI -png_get_header_version(png_structp png_ptr) +png_const_charp PNGAPI +png_get_header_version(png_const_structrp 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 + PNG_UNUSED(png_ptr) /* Silence compiler warning about unused png_ptr */ +#ifdef __STDC__ + return PNG_HEADER_VERSION_STRING +# ifndef PNG_READ_SUPPORTED " (NO READ SUPPORT)" +# endif + PNG_STRING_NEWLINE; +#else + return PNG_HEADER_VERSION_STRING; #endif - "\n"); } -#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED int PNGAPI -png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +png_handle_as_unknown(png_const_structrp png_ptr, png_const_bytep chunk_name) { - /* check chunk_name and return "keep" value if it's on the list, else 0 */ - int i; - png_bytep p; - if(png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0) - return 0; - p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5; - for (i = png_ptr->num_chunk_list; i; i--, p-=5) - if (!png_memcmp(chunk_name, p, 4)) - return ((int)*(p+4)); - return 0; -} -#endif + /* Check chunk_name and return "keep" value if it's on the list, else 0 */ + png_const_bytep p, p_end; + if (png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list == 0) + return PNG_HANDLE_CHUNK_AS_DEFAULT; + + p_end = png_ptr->chunk_list; + p = p_end + png_ptr->num_chunk_list*5; /* beyond end */ + + /* The code is the fifth byte after each four byte string. Historically this + * code was always searched from the end of the list, this is no longer + * necessary because the 'set' routine handles duplicate entries correcty. + */ + do /* num_chunk_list > 0, so at least one */ + { + p -= 5; + + if (!memcmp(chunk_name, p, 4)) + return p[4]; + } + while (p > p_end); + + /* This means that known chunks should be processed and unknown chunks should + * be handled according to the value of png_ptr->unknown_default; this can be + * confusing because, as a result, there are two levels of defaulting for + * unknown chunks. + */ + return PNG_HANDLE_CHUNK_AS_DEFAULT; +} + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +int /* PRIVATE */ +png_chunk_unknown_handling(png_const_structrp png_ptr, png_uint_32 chunk_name) +{ + png_byte chunk_string[5]; + + PNG_CSTRING_FROM_CHUNK(chunk_string, chunk_name); + return png_handle_as_unknown(png_ptr, chunk_string); +} +#endif /* READ_UNKNOWN_CHUNKS */ +#endif /* SET_UNKNOWN_CHUNKS */ + +#ifdef PNG_READ_SUPPORTED /* This function, added to libpng-1.0.6g, is untested. */ int PNGAPI -png_reset_zstream(png_structp png_ptr) +png_reset_zstream(png_structrp png_ptr) { - if (png_ptr == NULL) return Z_STREAM_ERROR; + if (png_ptr == NULL) + return Z_STREAM_ERROR; + + /* WARNING: this resets the window bits to the maximum! */ return (inflateReset(&png_ptr->zstream)); } -#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ +#endif /* PNG_READ_SUPPORTED */ /* This function was added to libpng-1.0.7 */ png_uint_32 PNGAPI png_access_version_number(void) { /* Version of *.c files used when building libpng */ - return((png_uint_32) PNG_LIBPNG_VER); + return((png_uint_32)PNG_LIBPNG_VER); } -#if defined(PNG_READ_SUPPORTED) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) -#if !defined(PNG_1_0_X) -/* this function was added to libpng 1.2.0 */ -int PNGAPI -png_mmx_support(void) -{ - /* obsolete, to be removed from libpng-1.4.0 */ - return -1; -} -#endif /* PNG_1_0_X */ -#endif /* PNG_READ_SUPPORTED && PNG_ASSEMBLER_CODE_SUPPORTED */ #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -#ifdef PNG_SIZE_T -/* Added at libpng version 1.2.6 */ - PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); -png_size_t PNGAPI -png_convert_size(size_t size) +/* Ensure that png_ptr->zstream.msg holds some appropriate error message string. + * If it doesn't 'ret' is used to set it to something appropriate, even in cases + * like Z_OK or Z_STREAM_END where the error code is apparently a success code. + */ +void /* PRIVATE */ +png_zstream_error(png_structrp png_ptr, int ret) { - if (size > (png_size_t)-1) - PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ - return ((png_size_t)size); + /* Translate 'ret' into an appropriate error string, priority is given to the + * one in zstream if set. This always returns a string, even in cases like + * Z_OK or Z_STREAM_END where the error code is a success code. + */ + if (png_ptr->zstream.msg == NULL) switch (ret) + { + default: + case Z_OK: + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return code"); + break; + + case Z_STREAM_END: + /* Normal exit */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected end of LZ stream"); + break; + + case Z_NEED_DICT: + /* This means the deflate stream did not have a dictionary; this + * indicates a bogus PNG. + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("missing LZ dictionary"); + break; + + case Z_ERRNO: + /* gz APIs only: should not happen */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("zlib IO error"); + break; + + case Z_STREAM_ERROR: + /* internal libpng error */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("bad parameters to zlib"); + break; + + case Z_DATA_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("damaged LZ stream"); + break; + + case Z_MEM_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("insufficient memory"); + break; + + case Z_BUF_ERROR: + /* End of input or output; not a problem if the caller is doing + * incremental read or write. + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("truncated"); + break; + + case Z_VERSION_ERROR: + png_ptr->zstream.msg = PNGZ_MSG_CAST("unsupported zlib version"); + break; + + case PNG_UNEXPECTED_ZLIB_RETURN: + /* Compile errors here mean that zlib now uses the value co-opted in + * pngpriv.h for PNG_UNEXPECTED_ZLIB_RETURN; update the switch above + * and change pngpriv.h. Note that this message is "... return", + * whereas the default/Z_OK one is "... return code". + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("unexpected zlib return"); + break; + } } -#endif /* PNG_SIZE_T */ + +/* png_convert_size: a PNGAPI but no longer in png.h, so deleted + * at libpng 1.5.5! + */ + +/* Added at libpng version 1.2.34 and 1.4.0 (moved from pngset.c) */ +#ifdef PNG_GAMMA_SUPPORTED /* always set if COLORSPACE */ +static int +png_colorspace_check_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA, int from) + /* This is called to check a new gamma value against an existing one. The + * routine returns false if the new gamma value should not be written. + * + * 'from' says where the new gamma value comes from: + * + * 0: the new gamma value is the libpng estimate for an ICC profile + * 1: the new gamma value comes from a gAMA chunk + * 2: the new gamma value comes from an sRGB chunk + */ +{ + png_fixed_point gtest; + + if ((colorspace->flags & PNG_COLORSPACE_HAVE_GAMMA) != 0 && + (!png_muldiv(>est, colorspace->gamma, PNG_FP_1, gAMA) || + png_gamma_significant(gtest))) + { + /* Either this is an sRGB image, in which case the calculated gamma + * approximation should match, or this is an image with a profile and the + * value libpng calculates for the gamma of the profile does not match the + * value recorded in the file. The former, sRGB, case is an error, the + * latter is just a warning. + */ + if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0 || from == 2) + { + png_chunk_report(png_ptr, "gamma value does not match sRGB", + PNG_CHUNK_ERROR); + /* Do not overwrite an sRGB value */ + return from == 2; + } + + else /* sRGB tag not involved */ + { + png_chunk_report(png_ptr, "gamma value does not match libpng estimate", + PNG_CHUNK_WARNING); + return from == 1; + } + } + + return 1; +} + +void /* PRIVATE */ +png_colorspace_set_gamma(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA) +{ + /* Changed in libpng-1.5.4 to limit the values to ensure overflow can't + * occur. Since the fixed point representation is assymetrical it is + * possible for 1/gamma to overflow the limit of 21474 and this means the + * gamma value must be at least 5/100000 and hence at most 20000.0. For + * safety the limits here are a little narrower. The values are 0.00016 to + * 6250.0, which are truly ridiculous gamma values (and will produce + * displays that are all black or all white.) + * + * In 1.6.0 this test replaces the ones in pngrutil.c, in the gAMA chunk + * handling code, which only required the value to be >0. + */ + png_const_charp errmsg; + + if (gAMA < 16 || gAMA > 625000000) + errmsg = "gamma value out of range"; + +# ifdef PNG_READ_gAMA_SUPPORTED + /* Allow the application to set the gamma value more than once */ + else if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + (colorspace->flags & PNG_COLORSPACE_FROM_gAMA) != 0) + errmsg = "duplicate"; +# endif + + /* Do nothing if the colorspace is already invalid */ + else if (colorspace->flags & PNG_COLORSPACE_INVALID) + return; + + else + { + if (png_colorspace_check_gamma(png_ptr, colorspace, gAMA, 1/*from gAMA*/)) + { + /* Store this gamma value. */ + colorspace->gamma = gAMA; + colorspace->flags |= + (PNG_COLORSPACE_HAVE_GAMMA | PNG_COLORSPACE_FROM_gAMA); + } + + /* At present if the check_gamma test fails the gamma of the colorspace is + * not updated however the colorspace is not invalidated. This + * corresponds to the case where the existing gamma comes from an sRGB + * chunk or profile. An error message has already been output. + */ + return; + } + + /* Error exit - errmsg has been set. */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_chunk_report(png_ptr, errmsg, PNG_CHUNK_WRITE_ERROR); +} + +void /* PRIVATE */ +png_colorspace_sync_info(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if (info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + { + /* Everything is invalid */ + info_ptr->valid &= ~(PNG_INFO_gAMA|PNG_INFO_cHRM|PNG_INFO_sRGB| + PNG_INFO_iCCP); + +# ifdef PNG_COLORSPACE_SUPPORTED + /* Clean up the iCCP profile now if it won't be used. */ + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, -1/*not used*/); +# else + PNG_UNUSED(png_ptr) +# endif + } + + else + { +# ifdef PNG_COLORSPACE_SUPPORTED + /* Leave the INFO_iCCP flag set if the pngset.c code has already set + * it; this allows a PNG to contain a profile which matches sRGB and + * yet still have that profile retrievable by the application. + */ + if (info_ptr->colorspace.flags & PNG_COLORSPACE_MATCHES_sRGB) + info_ptr->valid |= PNG_INFO_sRGB; + + else + info_ptr->valid &= ~PNG_INFO_sRGB; + + if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) + info_ptr->valid |= PNG_INFO_cHRM; + + else + info_ptr->valid &= ~PNG_INFO_cHRM; +# endif + + if (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) + info_ptr->valid |= PNG_INFO_gAMA; + + else + info_ptr->valid &= ~PNG_INFO_gAMA; + } +} + +#ifdef PNG_READ_SUPPORTED +void /* PRIVATE */ +png_colorspace_sync(png_const_structrp png_ptr, png_inforp info_ptr) +{ + if (info_ptr == NULL) /* reduce code size; check here not in the caller */ + return; + + info_ptr->colorspace = png_ptr->colorspace; + png_colorspace_sync_info(png_ptr, info_ptr); +} +#endif +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED +/* Added at libpng-1.5.5 to support read and write of true CIEXYZ values for + * cHRM, as opposed to using chromaticities. These internal APIs return + * non-zero on a parameter error. The X, Y and Z values are required to be + * positive and less than 1.0. + */ +static int +png_xy_from_XYZ(png_xy *xy, const png_XYZ *XYZ) +{ + png_int_32 d, dwhite, whiteX, whiteY; + + d = XYZ->red_X + XYZ->red_Y + XYZ->red_Z; + if (!png_muldiv(&xy->redx, XYZ->red_X, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->redy, XYZ->red_Y, PNG_FP_1, d)) return 1; + dwhite = d; + whiteX = XYZ->red_X; + whiteY = XYZ->red_Y; + + d = XYZ->green_X + XYZ->green_Y + XYZ->green_Z; + if (!png_muldiv(&xy->greenx, XYZ->green_X, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->greeny, XYZ->green_Y, PNG_FP_1, d)) return 1; + dwhite += d; + whiteX += XYZ->green_X; + whiteY += XYZ->green_Y; + + d = XYZ->blue_X + XYZ->blue_Y + XYZ->blue_Z; + if (!png_muldiv(&xy->bluex, XYZ->blue_X, PNG_FP_1, d)) return 1; + if (!png_muldiv(&xy->bluey, XYZ->blue_Y, PNG_FP_1, d)) return 1; + dwhite += d; + whiteX += XYZ->blue_X; + whiteY += XYZ->blue_Y; + + /* The reference white is simply the sum of the end-point (X,Y,Z) vectors, + * thus: + */ + if (!png_muldiv(&xy->whitex, whiteX, PNG_FP_1, dwhite)) return 1; + if (!png_muldiv(&xy->whitey, whiteY, PNG_FP_1, dwhite)) return 1; + + return 0; +} + +static int +png_XYZ_from_xy(png_XYZ *XYZ, const png_xy *xy) +{ + png_fixed_point red_inverse, green_inverse, blue_scale; + png_fixed_point left, right, denominator; + + /* Check xy and, implicitly, z. Note that wide gamut color spaces typically + * have end points with 0 tristimulus values (these are impossible end + * points, but they are used to cover the possible colors.) + */ + if (xy->redx < 0 || xy->redx > PNG_FP_1) return 1; + if (xy->redy < 0 || xy->redy > PNG_FP_1-xy->redx) return 1; + if (xy->greenx < 0 || xy->greenx > PNG_FP_1) return 1; + if (xy->greeny < 0 || xy->greeny > PNG_FP_1-xy->greenx) return 1; + if (xy->bluex < 0 || xy->bluex > PNG_FP_1) return 1; + if (xy->bluey < 0 || xy->bluey > PNG_FP_1-xy->bluex) return 1; + if (xy->whitex < 0 || xy->whitex > PNG_FP_1) return 1; + if (xy->whitey < 0 || xy->whitey > PNG_FP_1-xy->whitex) return 1; + + /* The reverse calculation is more difficult because the original tristimulus + * value had 9 independent values (red,green,blue)x(X,Y,Z) however only 8 + * derived values were recorded in the cHRM chunk; + * (red,green,blue,white)x(x,y). This loses one degree of freedom and + * therefore an arbitrary ninth value has to be introduced to undo the + * original transformations. + * + * Think of the original end-points as points in (X,Y,Z) space. The + * chromaticity values (c) have the property: + * + * C + * c = --------- + * X + Y + Z + * + * For each c (x,y,z) from the corresponding original C (X,Y,Z). Thus the + * three chromaticity values (x,y,z) for each end-point obey the + * relationship: + * + * x + y + z = 1 + * + * This describes the plane in (X,Y,Z) space that intersects each axis at the + * value 1.0; call this the chromaticity plane. Thus the chromaticity + * calculation has scaled each end-point so that it is on the x+y+z=1 plane + * and chromaticity is the intersection of the vector from the origin to the + * (X,Y,Z) value with the chromaticity plane. + * + * To fully invert the chromaticity calculation we would need the three + * end-point scale factors, (red-scale, green-scale, blue-scale), but these + * were not recorded. Instead we calculated the reference white (X,Y,Z) and + * recorded the chromaticity of this. The reference white (X,Y,Z) would have + * given all three of the scale factors since: + * + * color-C = color-c * color-scale + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * But cHRM records only white-x and white-y, so we have lost the white scale + * factor: + * + * white-C = white-c*white-scale + * + * To handle this the inverse transformation makes an arbitrary assumption + * about white-scale: + * + * Assume: white-Y = 1.0 + * Hence: white-scale = 1/white-y + * Or: red-Y + green-Y + blue-Y = 1.0 + * + * Notice the last statement of the assumption gives an equation in three of + * the nine values we want to calculate. 8 more equations come from the + * above routine as summarised at the top above (the chromaticity + * calculation): + * + * Given: color-x = color-X / (color-X + color-Y + color-Z) + * Hence: (color-x - 1)*color-X + color.x*color-Y + color.x*color-Z = 0 + * + * This is 9 simultaneous equations in the 9 variables "color-C" and can be + * solved by Cramer's rule. Cramer's rule requires calculating 10 9x9 matrix + * determinants, however this is not as bad as it seems because only 28 of + * the total of 90 terms in the various matrices are non-zero. Nevertheless + * Cramer's rule is notoriously numerically unstable because the determinant + * calculation involves the difference of large, but similar, numbers. It is + * difficult to be sure that the calculation is stable for real world values + * and it is certain that it becomes unstable where the end points are close + * together. + * + * So this code uses the perhaps slightly less optimal but more + * understandable and totally obvious approach of calculating color-scale. + * + * This algorithm depends on the precision in white-scale and that is + * (1/white-y), so we can immediately see that as white-y approaches 0 the + * accuracy inherent in the cHRM chunk drops off substantially. + * + * libpng arithmetic: a simple invertion of the above equations + * ------------------------------------------------------------ + * + * white_scale = 1/white-y + * white-X = white-x * white-scale + * white-Y = 1.0 + * white-Z = (1 - white-x - white-y) * white_scale + * + * white-C = red-C + green-C + blue-C + * = red-c*red-scale + green-c*green-scale + blue-c*blue-scale + * + * This gives us three equations in (red-scale,green-scale,blue-scale) where + * all the coefficients are now known: + * + * red-x*red-scale + green-x*green-scale + blue-x*blue-scale + * = white-x/white-y + * red-y*red-scale + green-y*green-scale + blue-y*blue-scale = 1 + * red-z*red-scale + green-z*green-scale + blue-z*blue-scale + * = (1 - white-x - white-y)/white-y + * + * In the last equation color-z is (1 - color-x - color-y) so we can add all + * three equations together to get an alternative third: + * + * red-scale + green-scale + blue-scale = 1/white-y = white-scale + * + * So now we have a Cramer's rule solution where the determinants are just + * 3x3 - far more tractible. Unfortunately 3x3 determinants still involve + * multiplication of three coefficients so we can't guarantee to avoid + * overflow in the libpng fixed point representation. Using Cramer's rule in + * floating point is probably a good choice here, but it's not an option for + * fixed point. Instead proceed to simplify the first two equations by + * eliminating what is likely to be the largest value, blue-scale: + * + * blue-scale = white-scale - red-scale - green-scale + * + * Hence: + * + * (red-x - blue-x)*red-scale + (green-x - blue-x)*green-scale = + * (white-x - blue-x)*white-scale + * + * (red-y - blue-y)*red-scale + (green-y - blue-y)*green-scale = + * 1 - blue-y*white-scale + * + * And now we can trivially solve for (red-scale,green-scale): + * + * green-scale = + * (white-x - blue-x)*white-scale - (red-x - blue-x)*red-scale + * ----------------------------------------------------------- + * green-x - blue-x + * + * red-scale = + * 1 - blue-y*white-scale - (green-y - blue-y) * green-scale + * --------------------------------------------------------- + * red-y - blue-y + * + * Hence: + * + * red-scale = + * ( (green-x - blue-x) * (white-y - blue-y) - + * (green-y - blue-y) * (white-x - blue-x) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * green-scale = + * ( (red-y - blue-y) * (white-x - blue-x) - + * (red-x - blue-x) * (white-y - blue-y) ) / white-y + * ------------------------------------------------------------------------- + * (green-x - blue-x)*(red-y - blue-y)-(green-y - blue-y)*(red-x - blue-x) + * + * Accuracy: + * The input values have 5 decimal digits of accuracy. The values are all in + * the range 0 < value < 1, so simple products are in the same range but may + * need up to 10 decimal digits to preserve the original precision and avoid + * underflow. Because we are using a 32-bit signed representation we cannot + * match this; the best is a little over 9 decimal digits, less than 10. + * + * The approach used here is to preserve the maximum precision within the + * signed representation. Because the red-scale calculation above uses the + * difference between two products of values that must be in the range -1..+1 + * it is sufficient to divide the product by 7; ceil(100,000/32767*2). The + * factor is irrelevant in the calculation because it is applied to both + * numerator and denominator. + * + * Note that the values of the differences of the products of the + * chromaticities in the above equations tend to be small, for example for + * the sRGB chromaticities they are: + * + * red numerator: -0.04751 + * green numerator: -0.08788 + * denominator: -0.2241 (without white-y multiplication) + * + * The resultant Y coefficients from the chromaticities of some widely used + * color space definitions are (to 15 decimal places): + * + * sRGB + * 0.212639005871510 0.715168678767756 0.072192315360734 + * Kodak ProPhoto + * 0.288071128229293 0.711843217810102 0.000085653960605 + * Adobe RGB + * 0.297344975250536 0.627363566255466 0.075291458493998 + * Adobe Wide Gamut RGB + * 0.258728243040113 0.724682314948566 0.016589442011321 + */ + /* By the argument, above overflow should be impossible here. The return + * value of 2 indicates an internal error to the caller. + */ + if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->redy - xy->bluey, 7)) + return 2; + if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->redx - xy->bluex, 7)) + return 2; + denominator = left - right; + + /* Now find the red numerator. */ + if (!png_muldiv(&left, xy->greenx-xy->bluex, xy->whitey-xy->bluey, 7)) + return 2; + if (!png_muldiv(&right, xy->greeny-xy->bluey, xy->whitex-xy->bluex, 7)) + return 2; + + /* Overflow is possible here and it indicates an extreme set of PNG cHRM + * chunk values. This calculation actually returns the reciprocal of the + * scale value because this allows us to delay the multiplication of white-y + * into the denominator, which tends to produce a small number. + */ + if (!png_muldiv(&red_inverse, xy->whitey, denominator, left-right) || + red_inverse <= xy->whitey /* r+g+b scales = white scale */) + return 1; + + /* Similarly for green_inverse: */ + if (!png_muldiv(&left, xy->redy-xy->bluey, xy->whitex-xy->bluex, 7)) + return 2; + if (!png_muldiv(&right, xy->redx-xy->bluex, xy->whitey-xy->bluey, 7)) + return 2; + if (!png_muldiv(&green_inverse, xy->whitey, denominator, left-right) || + green_inverse <= xy->whitey) + return 1; + + /* And the blue scale, the checks above guarantee this can't overflow but it + * can still produce 0 for extreme cHRM values. + */ + blue_scale = png_reciprocal(xy->whitey) - png_reciprocal(red_inverse) - + png_reciprocal(green_inverse); + if (blue_scale <= 0) return 1; + + + /* And fill in the png_XYZ: */ + if (!png_muldiv(&XYZ->red_X, xy->redx, PNG_FP_1, red_inverse)) return 1; + if (!png_muldiv(&XYZ->red_Y, xy->redy, PNG_FP_1, red_inverse)) return 1; + if (!png_muldiv(&XYZ->red_Z, PNG_FP_1 - xy->redx - xy->redy, PNG_FP_1, + red_inverse)) + return 1; + + if (!png_muldiv(&XYZ->green_X, xy->greenx, PNG_FP_1, green_inverse)) + return 1; + if (!png_muldiv(&XYZ->green_Y, xy->greeny, PNG_FP_1, green_inverse)) + return 1; + if (!png_muldiv(&XYZ->green_Z, PNG_FP_1 - xy->greenx - xy->greeny, PNG_FP_1, + green_inverse)) + return 1; + + if (!png_muldiv(&XYZ->blue_X, xy->bluex, blue_scale, PNG_FP_1)) return 1; + if (!png_muldiv(&XYZ->blue_Y, xy->bluey, blue_scale, PNG_FP_1)) return 1; + if (!png_muldiv(&XYZ->blue_Z, PNG_FP_1 - xy->bluex - xy->bluey, blue_scale, + PNG_FP_1)) + return 1; + + return 0; /*success*/ +} + +static int +png_XYZ_normalize(png_XYZ *XYZ) +{ + png_int_32 Y; + + if (XYZ->red_Y < 0 || XYZ->green_Y < 0 || XYZ->blue_Y < 0 || + XYZ->red_X < 0 || XYZ->green_X < 0 || XYZ->blue_X < 0 || + XYZ->red_Z < 0 || XYZ->green_Z < 0 || XYZ->blue_Z < 0) + return 1; + + /* Normalize by scaling so the sum of the end-point Y values is PNG_FP_1. + * IMPLEMENTATION NOTE: ANSI requires signed overflow not to occur, therefore + * relying on addition of two positive values producing a negative one is not + * safe. + */ + Y = XYZ->red_Y; + if (0x7fffffff - Y < XYZ->green_X) return 1; + Y += XYZ->green_Y; + if (0x7fffffff - Y < XYZ->blue_X) return 1; + Y += XYZ->blue_Y; + + if (Y != PNG_FP_1) + { + if (!png_muldiv(&XYZ->red_X, XYZ->red_X, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->red_Y, XYZ->red_Y, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->red_Z, XYZ->red_Z, PNG_FP_1, Y)) return 1; + + if (!png_muldiv(&XYZ->green_X, XYZ->green_X, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->green_Y, XYZ->green_Y, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->green_Z, XYZ->green_Z, PNG_FP_1, Y)) return 1; + + if (!png_muldiv(&XYZ->blue_X, XYZ->blue_X, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->blue_Y, XYZ->blue_Y, PNG_FP_1, Y)) return 1; + if (!png_muldiv(&XYZ->blue_Z, XYZ->blue_Z, PNG_FP_1, Y)) return 1; + } + + return 0; +} + +static int +png_colorspace_endpoints_match(const png_xy *xy1, const png_xy *xy2, int delta) +{ + /* Allow an error of +/-0.01 (absolute value) on each chromaticity */ + return !(PNG_OUT_OF_RANGE(xy1->whitex, xy2->whitex,delta) || + PNG_OUT_OF_RANGE(xy1->whitey, xy2->whitey,delta) || + PNG_OUT_OF_RANGE(xy1->redx, xy2->redx, delta) || + PNG_OUT_OF_RANGE(xy1->redy, xy2->redy, delta) || + PNG_OUT_OF_RANGE(xy1->greenx, xy2->greenx,delta) || + PNG_OUT_OF_RANGE(xy1->greeny, xy2->greeny,delta) || + PNG_OUT_OF_RANGE(xy1->bluex, xy2->bluex, delta) || + PNG_OUT_OF_RANGE(xy1->bluey, xy2->bluey, delta)); +} + +/* Added in libpng-1.6.0, a different check for the validity of a set of cHRM + * chunk chromaticities. Earlier checks used to simply look for the overflow + * condition (where the determinant of the matrix to solve for XYZ ends up zero + * because the chromaticity values are not all distinct.) Despite this it is + * theoretically possible to produce chromaticities that are apparently valid + * but that rapidly degrade to invalid, potentially crashing, sets because of + * arithmetic inaccuracies when calculations are performed on them. The new + * check is to round-trip xy -> XYZ -> xy and then check that the result is + * within a small percentage of the original. + */ +static int +png_colorspace_check_xy(png_XYZ *XYZ, const png_xy *xy) +{ + int result; + png_xy xy_test; + + /* As a side-effect this routine also returns the XYZ endpoints. */ + result = png_XYZ_from_xy(XYZ, xy); + if (result) return result; + + result = png_xy_from_XYZ(&xy_test, XYZ); + if (result) return result; + + if (png_colorspace_endpoints_match(xy, &xy_test, + 5/*actually, the math is pretty accurate*/)) + return 0; + + /* Too much slip */ + return 1; +} + +/* This is the check going the other way. The XYZ is modified to normalize it + * (another side-effect) and the xy chromaticities are returned. + */ +static int +png_colorspace_check_XYZ(png_xy *xy, png_XYZ *XYZ) +{ + int result; + png_XYZ XYZtemp; + + result = png_XYZ_normalize(XYZ); + if (result) return result; + + result = png_xy_from_XYZ(xy, XYZ); + if (result) return result; + + XYZtemp = *XYZ; + return png_colorspace_check_xy(&XYZtemp, xy); +} + +/* Used to check for an endpoint match against sRGB */ +static const png_xy sRGB_xy = /* From ITU-R BT.709-3 */ +{ + /* color x y */ + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000, + /* white */ 31270, 32900 +}; + +static int +png_colorspace_set_xy_and_XYZ(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, const png_XYZ *XYZ, + int preferred) +{ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + /* The consistency check is performed on the chromaticities; this factors out + * variations because of the normalization (or not) of the end point Y + * values. + */ + if (preferred < 2 && (colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + /* The end points must be reasonably close to any we already have. The + * following allows an error of up to +/-.001 + */ + if (!png_colorspace_endpoints_match(xy, &colorspace->end_points_xy, 100)) + { + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "inconsistent chromaticities"); + return 0; /* failed */ + } + + /* Only overwrite with preferred values */ + if (!preferred) + return 1; /* ok, but no change */ + } + + colorspace->end_points_xy = *xy; + colorspace->end_points_XYZ = *XYZ; + colorspace->flags |= PNG_COLORSPACE_HAVE_ENDPOINTS; + + /* The end points are normally quoted to two decimal digits, so allow +/-0.01 + * on this test. + */ + if (png_colorspace_endpoints_match(xy, &sRGB_xy, 1000)) + colorspace->flags |= PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB; + + else + colorspace->flags &= PNG_COLORSPACE_CANCEL( + PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); + + return 2; /* ok and changed */ +} + +int /* PRIVATE */ +png_colorspace_set_chromaticities(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_xy *xy, int preferred) +{ + /* We must check the end points to ensure they are reasonable - in the past + * color management systems have crashed as a result of getting bogus + * colorant values, while this isn't the fault of libpng it is the + * responsibility of libpng because PNG carries the bomb and libpng is in a + * position to protect against it. + */ + png_XYZ XYZ; + + switch (png_colorspace_check_xy(&XYZ, xy)) + { + case 0: /* success */ + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, xy, &XYZ, + preferred); + + case 1: + /* We can't invert the chromaticities so we can't produce value XYZ + * values. Likely as not a color management system will fail too. + */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid chromaticities"); + break; + + default: + /* libpng is broken; this should be a warning but if it happens we + * want error reports so for the moment it is an error. + */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); + break; + } + + return 0; /* failed */ +} + +int /* PRIVATE */ +png_colorspace_set_endpoints(png_const_structrp png_ptr, + png_colorspacerp colorspace, const png_XYZ *XYZ_in, int preferred) +{ + png_XYZ XYZ = *XYZ_in; + png_xy xy; + + switch (png_colorspace_check_XYZ(&xy, &XYZ)) + { + case 0: + return png_colorspace_set_xy_and_XYZ(png_ptr, colorspace, &xy, &XYZ, + preferred); + + case 1: + /* End points are invalid. */ + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_benign_error(png_ptr, "invalid end points"); + break; + + default: + colorspace->flags |= PNG_COLORSPACE_INVALID; + png_error(png_ptr, "internal error checking chromaticities"); + break; + } + + return 0; /* failed */ +} + +#if defined(PNG_sRGB_SUPPORTED) || defined(PNG_iCCP_SUPPORTED) +/* Error message generation */ +static char +png_icc_tag_char(png_uint_32 byte) +{ + byte &= 0xff; + if (byte >= 32 && byte <= 126) + return (char)byte; + else + return '?'; +} + +static void +png_icc_tag_name(char *name, png_uint_32 tag) +{ + name[0] = '\''; + name[1] = png_icc_tag_char(tag >> 24); + name[2] = png_icc_tag_char(tag >> 16); + name[3] = png_icc_tag_char(tag >> 8); + name[4] = png_icc_tag_char(tag ); + name[5] = '\''; +} + +static int +is_ICC_signature_char(png_alloc_size_t it) +{ + return it == 32 || (it >= 48 && it <= 57) || (it >= 65 && it <= 90) || + (it >= 97 && it <= 122); +} + +static int is_ICC_signature(png_alloc_size_t it) +{ + return is_ICC_signature_char(it >> 24) /* checks all the top bits */ && + is_ICC_signature_char((it >> 16) & 0xff) && + is_ICC_signature_char((it >> 8) & 0xff) && + is_ICC_signature_char(it & 0xff); +} + +static int +png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_alloc_size_t value, png_const_charp reason) +{ + size_t pos; + char message[196]; /* see below for calculation */ + + if (colorspace != NULL) + colorspace->flags |= PNG_COLORSPACE_INVALID; + + pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */ + pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */ + pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */ + if (is_ICC_signature(value)) + { + /* So 'value' is at most 4 bytes and the following cast is safe */ + png_icc_tag_name(message+pos, (png_uint_32)value); + pos += 6; /* total +8; less than the else clause */ + message[pos++] = ':'; + message[pos++] = ' '; + } +# ifdef PNG_WARNINGS_SUPPORTED + else + { + char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/ + + pos = png_safecat(message, (sizeof message), pos, + png_format_number(number, number+(sizeof number), + PNG_NUMBER_FORMAT_x, value)); + pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/ + } +# endif + /* The 'reason' is an arbitrary message, allow +79 maximum 195 */ + png_safecat(message, (sizeof message), pos, reason); + + /* This is recoverable, but make it unconditionally an app_error on write to + * avoid writing invalid ICC profiles into PNG files. (I.e. we handle them + * on read, with a warning, but on write unless the app turns off + * application errors the PNG won't be written.) + */ + png_chunk_report(png_ptr, message, + (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR); + + return 0; +} +#endif /* sRGB || iCCP */ + +#ifdef PNG_sRGB_SUPPORTED +int /* PRIVATE */ +png_colorspace_set_sRGB(png_const_structrp png_ptr, png_colorspacerp colorspace, + int intent) +{ + /* sRGB sets known gamma, end points and (from the chunk) intent. */ + /* IMPORTANT: these are not necessarily the values found in an ICC profile + * because ICC profiles store values adapted to a D50 environment; it is + * expected that the ICC profile mediaWhitePointTag will be D50, see the + * checks and code elsewhere to understand this better. + * + * These XYZ values, which are accurate to 5dp, produce rgb to gray + * coefficients of (6968,23435,2366), which are reduced (because they add up + * to 32769 not 32768) to (6968,23434,2366). These are the values that + * libpng has traditionally used (and are the best values given the 15bit + * algorithm used by the rgb to gray code.) + */ + static const png_XYZ sRGB_XYZ = /* D65 XYZ (*not* the D50 adapted values!) */ + { + /* color X Y Z */ + /* red */ 41239, 21264, 1933, + /* green */ 35758, 71517, 11919, + /* blue */ 18048, 7219, 95053 + }; + + /* Do nothing if the colorspace is already invalidated. */ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + /* Check the intent, then check for existing settings. It is valid for the + * PNG file to have cHRM or gAMA chunks along with sRGB, but the values must + * be consistent with the correct values. If, however, this function is + * called below because an iCCP chunk matches sRGB then it is quite + * conceivable that an older app recorded incorrect gAMA and cHRM because of + * an incorrect calculation based on the values in the profile - this does + * *not* invalidate the profile (though it still produces an error, which can + * be ignored.) + */ + if (intent < 0 || intent >= PNG_sRGB_INTENT_LAST) + return png_icc_profile_error(png_ptr, colorspace, "sRGB", + (unsigned)intent, "invalid sRGB rendering intent"); + + if ((colorspace->flags & PNG_COLORSPACE_HAVE_INTENT) != 0 && + colorspace->rendering_intent != intent) + return png_icc_profile_error(png_ptr, colorspace, "sRGB", + (unsigned)intent, "inconsistent rendering intents"); + + if ((colorspace->flags & PNG_COLORSPACE_FROM_sRGB) != 0) + { + png_benign_error(png_ptr, "duplicate sRGB information ignored"); + return 0; + } + + /* If the standard sRGB cHRM chunk does not match the one from the PNG file + * warn but overwrite the value with the correct one. + */ + if ((colorspace->flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0 && + !png_colorspace_endpoints_match(&sRGB_xy, &colorspace->end_points_xy, + 100)) + png_chunk_report(png_ptr, "cHRM chunk does not match sRGB", + PNG_CHUNK_ERROR); + + /* This check is just done for the error reporting - the routine always + * returns true when the 'from' argument corresponds to sRGB (2). + */ + (void)png_colorspace_check_gamma(png_ptr, colorspace, PNG_GAMMA_sRGB_INVERSE, + 2/*from sRGB*/); + + /* intent: bugs in GCC force 'int' to be used as the parameter type. */ + colorspace->rendering_intent = (png_uint_16)intent; + colorspace->flags |= PNG_COLORSPACE_HAVE_INTENT; + + /* endpoints */ + colorspace->end_points_xy = sRGB_xy; + colorspace->end_points_XYZ = sRGB_XYZ; + colorspace->flags |= + (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB); + + /* gamma */ + colorspace->gamma = PNG_GAMMA_sRGB_INVERSE; + colorspace->flags |= PNG_COLORSPACE_HAVE_GAMMA; + + /* Finally record that we have an sRGB profile */ + colorspace->flags |= + (PNG_COLORSPACE_MATCHES_sRGB|PNG_COLORSPACE_FROM_sRGB); + + return 1; /* set */ +} +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +/* Encoded value of D50 as an ICC XYZNumber. From the ICC 2010 spec the value + * is XYZ(0.9642,1.0,0.8249), which scales to: + * + * (63189.8112, 65536, 54060.6464) + */ +static const png_byte D50_nCIEXYZ[12] = + { 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d }; + +int /* PRIVATE */ +png_icc_check_length(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length) +{ + if (profile_length < 132) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "too short"); + + if (profile_length & 3) + return png_icc_profile_error(png_ptr, colorspace, name, profile_length, + "invalid length"); + + return 1; +} + +int /* PRIVATE */ +png_icc_check_header(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile/* first 132 bytes only */, int color_type) +{ + png_uint_32 temp; + + /* Length check; this cannot be ignored in this code because profile_length + * is used later to check the tag table, so even if the profile seems over + * long profile_length from the caller must be correct. The caller can fix + * this up on read or write by just passing in the profile header length. + */ + temp = png_get_uint_32(profile); + if (temp != profile_length) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "length does not match profile"); + + temp = png_get_uint_32(profile+128); /* tag count: 12 bytes/tag */ + if (temp > 357913930 || /* (2^32-4-132)/12: maxium possible tag count */ + profile_length < 132+12*temp) /* truncated tag table */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "tag count too large"); + + /* The 'intent' must be valid or we can't store it, ICC limits the intent to + * 16 bits. + */ + temp = png_get_uint_32(profile+64); + if (temp >= 0xffff) /* The ICC limit */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid rendering intent"); + + /* This is just a warning because the profile may be valid in future + * versions. + */ + if (temp >= PNG_sRGB_INTENT_LAST) + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "intent outside defined range"); + + /* At this point the tag table can't be checked because it hasn't necessarily + * been loaded; however, various header fields can be checked. These checks + * are for values permitted by the PNG spec in an ICC profile; the PNG spec + * restricts the profiles that can be passed in an iCCP chunk (they must be + * appropriate to processing PNG data!) + */ + + /* Data checks (could be skipped). These checks must be independent of the + * version number; however, the version number doesn't accomodate changes in + * the header fields (just the known tags and the interpretation of the + * data.) + */ + temp = png_get_uint_32(profile+36); /* signature 'ascp' */ + if (temp != 0x61637370) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid signature"); + + /* Currently the PCS illuminant/adopted white point (the computational + * white point) are required to be D50, + * however the profile contains a record of the illuminant so perhaps ICC + * expects to be able to change this in the future (despite the rationale in + * the introduction for using a fixed PCS adopted white.) Consequently the + * following is just a warning. + */ + if (memcmp(profile+68, D50_nCIEXYZ, 12) != 0) + (void)png_icc_profile_error(png_ptr, NULL, name, 0/*no tag value*/, + "PCS illuminant is not D50"); + + /* The PNG spec requires this: + * "If the iCCP chunk is present, the image samples conform to the colour + * space represented by the embedded ICC profile as defined by the + * International Color Consortium [ICC]. The colour space of the ICC profile + * shall be an RGB colour space for colour images (PNG colour types 2, 3, and + * 6), or a greyscale colour space for greyscale images (PNG colour types 0 + * and 4)." + * + * This checking code ensures the embedded profile (on either read or write) + * conforms to the specification requirements. Notice that an ICC 'gray' + * color-space profile contains the information to transform the monochrome + * data to XYZ or L*a*b (according to which PCS the profile uses) and this + * should be used in preference to the standard libpng K channel replication + * into R, G and B channels. + * + * Previously it was suggested that an RGB profile on grayscale data could be + * handled. However it it is clear that using an RGB profile in this context + * must be an error - there is no specification of what it means. Thus it is + * almost certainly more correct to ignore the profile. + */ + temp = png_get_uint_32(profile+16); /* data colour space field */ + switch (temp) + { + case 0x52474220: /* 'RGB ' */ + if (!(color_type & PNG_COLOR_MASK_COLOR)) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "RGB color space not permitted on grayscale PNG"); + break; + + case 0x47524159: /* 'GRAY' */ + if (color_type & PNG_COLOR_MASK_COLOR) + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "Gray color space not permitted on RGB PNG"); + break; + + default: + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid ICC profile color space"); + } + + /* It is up to the application to check that the profile class matches the + * application requirements; the spec provides no guidance, but it's pretty + * weird if the profile is not scanner ('scnr'), monitor ('mntr'), printer + * ('prtr') or 'spac' (for generic color spaces). Issue a warning in these + * cases. Issue an error for device link or abstract profiles - these don't + * contain the records necessary to transform the color-space to anything + * other than the target device (and not even that for an abstract profile). + * Profiles of these classes may not be embedded in images. + */ + temp = png_get_uint_32(profile+12); /* profile/device class */ + switch (temp) + { + case 0x73636E72: /* 'scnr' */ + case 0x6D6E7472: /* 'mntr' */ + case 0x70727472: /* 'prtr' */ + case 0x73706163: /* 'spac' */ + /* All supported */ + break; + + case 0x61627374: /* 'abst' */ + /* May not be embedded in an image */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "invalid embedded Abstract ICC profile"); + + case 0x6C696E6B: /* 'link' */ + /* DeviceLink profiles cannnot be interpreted in a non-device specific + * fashion, if an app uses the AToB0Tag in the profile the results are + * undefined unless the result is sent to the intended device, + * therefore a DeviceLink profile should not be found embedded in a + * PNG. + */ + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "unexpected DeviceLink ICC profile class"); + + case 0x6E6D636C: /* 'nmcl' */ + /* A NamedColor profile is also device specific, however it doesn't + * contain an AToB0 tag that is open to misintrepretation. Almost + * certainly it will fail the tests below. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "unexpected NamedColor ICC profile class"); + break; + + default: + /* To allow for future enhancements to the profile accept unrecognized + * profile classes with a warning, these then hit the test below on the + * tag content to ensure they are backward compatible with one of the + * understood profiles. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, temp, + "unrecognized ICC profile class"); + break; + } + + /* For any profile other than a device link one the PCS must be encoded + * either in XYZ or Lab. + */ + temp = png_get_uint_32(profile+20); + switch (temp) + { + case 0x58595A20: /* 'XYZ ' */ + case 0x4C616220: /* 'Lab ' */ + break; + + default: + return png_icc_profile_error(png_ptr, colorspace, name, temp, + "unexpected ICC PCS encoding"); + } + + return 1; +} + +int /* PRIVATE */ +png_icc_check_tag_table(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */) +{ + png_uint_32 tag_count = png_get_uint_32(profile+128); + png_uint_32 itag; + png_const_bytep tag = profile+132; /* The first tag */ + + /* First scan all the tags in the table and add bits to the icc_info value + * (temporarily in 'tags'). + */ + for (itag=0; itag < tag_count; ++itag, tag += 12) + { + png_uint_32 tag_id = png_get_uint_32(tag+0); + png_uint_32 tag_start = png_get_uint_32(tag+4); /* must be aligned */ + png_uint_32 tag_length = png_get_uint_32(tag+8);/* not padded */ + + /* The ICC specification does not exclude zero length tags, therefore the + * start might actually be anywhere if there is no data, but this would be + * a clear abuse of the intent of the standard so the start is checked for + * being in range. All defined tag types have an 8 byte header - a 4 byte + * type signature then 0. + */ + if ((tag_start & 3) != 0) + { + /* CNHP730S.icc shipped with Microsoft Windows 64 violates this, it is + * only a warning here because libpng does not care about the + * alignment. + */ + (void)png_icc_profile_error(png_ptr, NULL, name, tag_id, + "ICC profile tag start not a multiple of 4"); + } + + /* This is a hard error; potentially it can cause read outside the + * profile. + */ + if (tag_start > profile_length || tag_length > profile_length - tag_start) + return png_icc_profile_error(png_ptr, colorspace, name, tag_id, + "ICC profile tag outside profile"); + } + + return 1; /* success, maybe with warnings */ +} + +#ifdef PNG_sRGB_SUPPORTED +/* Information about the known ICC sRGB profiles */ +static const struct +{ + png_uint_32 adler, crc, length; + png_uint_32 md5[4]; + png_byte have_md5; + png_byte is_broken; + png_uint_16 intent; + +# define PNG_MD5(a,b,c,d) { a, b, c, d }, (a!=0)||(b!=0)||(c!=0)||(d!=0) +# define PNG_ICC_CHECKSUM(adler, crc, md5, intent, broke, date, length, fname)\ + { adler, crc, length, md5, broke, intent }, + +} png_sRGB_checks[] = +{ + /* This data comes from contrib/tools/checksum-icc run on downloads of + * all four ICC sRGB profiles from www.color.org. + */ + /* adler32, crc32, MD5[4], intent, date, length, file-name */ + PNG_ICC_CHECKSUM(0x0a3fd9f6, 0x3b8772b9, + PNG_MD5(0x29f83dde, 0xaff255ae, 0x7842fae4, 0xca83390d), 0, 0, + "2009/03/27 21:36:31", 3048, "sRGB_IEC61966-2-1_black_scaled.icc") + + /* ICC sRGB v2 perceptual no black-compensation: */ + PNG_ICC_CHECKSUM(0x4909e5e1, 0x427ebb21, + PNG_MD5(0xc95bd637, 0xe95d8a3b, 0x0df38f99, 0xc1320389), 1, 0, + "2009/03/27 21:37:45", 3052, "sRGB_IEC61966-2-1_no_black_scaling.icc") + + PNG_ICC_CHECKSUM(0xfd2144a1, 0x306fd8ae, + PNG_MD5(0xfc663378, 0x37e2886b, 0xfd72e983, 0x8228f1b8), 0, 0, + "2009/08/10 17:28:01", 60988, "sRGB_v4_ICC_preference_displayclass.icc") + + /* ICC sRGB v4 perceptual */ + PNG_ICC_CHECKSUM(0x209c35d2, 0xbbef7812, + PNG_MD5(0x34562abf, 0x994ccd06, 0x6d2c5721, 0xd0d68c5d), 0, 0, + "2007/07/25 00:05:37", 60960, "sRGB_v4_ICC_preference.icc") + + /* The following profiles have no known MD5 checksum. If there is a match + * on the (empty) MD5 the other fields are used to attempt a match and + * a warning is produced. The first two of these profiles have a 'cprt' tag + * which suggests that they were also made by Hewlett Packard. + */ + PNG_ICC_CHECKSUM(0xa054d762, 0x5d5129ce, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 0, + "2004/07/21 18:57:42", 3024, "sRGB_IEC61966-2-1_noBPC.icc") + + /* This is a 'mntr' (display) profile with a mediaWhitePointTag that does not + * match the D50 PCS illuminant in the header (it is in fact the D65 values, + * so the white point is recorded as the un-adapted value.) The profiles + * below only differ in one byte - the intent - and are basically the same as + * the previous profile except for the mediaWhitePointTag error and a missing + * chromaticAdaptationTag. + */ + PNG_ICC_CHECKSUM(0xf784f3fb, 0x182ea552, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 0, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 perceptual") + + PNG_ICC_CHECKSUM(0x0398f3fc, 0xf29e526d, + PNG_MD5(0x00000000, 0x00000000, 0x00000000, 0x00000000), 1, 1/*broken*/, + "1998/02/09 06:49:00", 3144, "HP-Microsoft sRGB v2 media-relative") +}; + +static int +png_compare_ICC_profile_with_sRGB(png_const_structrp png_ptr, + png_const_bytep profile, uLong adler) +{ + /* The quick check is to verify just the MD5 signature and trust the + * rest of the data. Because the profile has already been verified for + * correctness this is safe. png_colorspace_set_sRGB will check the 'intent' + * field too, so if the profile has been edited with an intent not defined + * by sRGB (but maybe defined by a later ICC specification) the read of + * the profile will fail at that point. + */ + png_uint_32 length = 0; + png_uint_32 intent = 0x10000; /* invalid */ +#if PNG_sRGB_PROFILE_CHECKS > 1 + uLong crc = 0; /* the value for 0 length data */ +#endif + unsigned int i; + + for (i=0; i < (sizeof png_sRGB_checks) / (sizeof png_sRGB_checks[0]); ++i) + { + if (png_get_uint_32(profile+84) == png_sRGB_checks[i].md5[0] && + png_get_uint_32(profile+88) == png_sRGB_checks[i].md5[1] && + png_get_uint_32(profile+92) == png_sRGB_checks[i].md5[2] && + png_get_uint_32(profile+96) == png_sRGB_checks[i].md5[3]) + { + /* This may be one of the old HP profiles without an MD5, in that + * case we can only use the length and Adler32 (note that these + * are not used by default if there is an MD5!) + */ +# if PNG_sRGB_PROFILE_CHECKS == 0 + if (png_sRGB_checks[i].have_md5) + return 1+png_sRGB_checks[i].is_broken; +# endif + + /* Profile is unsigned or more checks have been configured in. */ + if (length == 0) + { + length = png_get_uint_32(profile); + intent = png_get_uint_32(profile+64); + } + + /* Length *and* intent must match */ + if (length == png_sRGB_checks[i].length && + intent == png_sRGB_checks[i].intent) + { + /* Now calculate the adler32 if not done already. */ + if (adler == 0) + { + adler = adler32(0, NULL, 0); + adler = adler32(adler, profile, length); + } + + if (adler == png_sRGB_checks[i].adler) + { + /* These basic checks suggest that the data has not been + * modified, but if the check level is more than 1 perform + * our own crc32 checksum on the data. + */ +# if PNG_sRGB_PROFILE_CHECKS > 1 + if (crc == 0) + { + crc = crc32(0, NULL, 0); + crc = crc32(crc, profile, length); + } + + /* So this check must pass for the 'return' below to happen. + */ + if (crc == png_sRGB_checks[i].crc) +# endif + { + if (png_sRGB_checks[i].is_broken) + { + /* These profiles are known to have bad data that may cause + * problems if they are used, therefore attempt to + * discourage their use, skip the 'have_md5' warning below, + * which is made irrelevant by this error. + */ + png_chunk_report(png_ptr, "known incorrect sRGB profile", + PNG_CHUNK_ERROR); + } + + /* Warn that this being done; this isn't even an error since + * the profile is perfectly valid, but it would be nice if + * people used the up-to-date ones. + */ + else if (!png_sRGB_checks[i].have_md5) + { + png_chunk_report(png_ptr, + "out-of-date sRGB profile with no signature", + PNG_CHUNK_WARNING); + } + + return 1+png_sRGB_checks[i].is_broken; + } + } + } + +# if PNG_sRGB_PROFILE_CHECKS > 0 + /* The signature matched, but the profile had been changed in some + * way. This is an apparent violation of the ICC terms of use and, + * anyway, probably indicates a data error or uninformed hacking. + */ + if (png_sRGB_checks[i].have_md5) + png_benign_error(png_ptr, + "copyright violation: edited ICC profile ignored"); +# endif + } + } + + return 0; /* no match */ +} +#endif + +#ifdef PNG_sRGB_SUPPORTED +void /* PRIVATE */ +png_icc_set_sRGB(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_bytep profile, uLong adler) +{ + /* Is this profile one of the known ICC sRGB profiles? If it is, just set + * the sRGB information. + */ + if (png_compare_ICC_profile_with_sRGB(png_ptr, profile, adler)) + (void)png_colorspace_set_sRGB(png_ptr, colorspace, + (int)/*already checked*/png_get_uint_32(profile+64)); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +int /* PRIVATE */ +png_colorspace_set_ICC(png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_charp name, png_uint_32 profile_length, png_const_bytep profile, + int color_type) +{ + if (colorspace->flags & PNG_COLORSPACE_INVALID) + return 0; + + if (png_icc_check_length(png_ptr, colorspace, name, profile_length) && + png_icc_check_header(png_ptr, colorspace, name, profile_length, profile, + color_type) && + png_icc_check_tag_table(png_ptr, colorspace, name, profile_length, + profile)) + { + png_icc_set_sRGB(png_ptr, colorspace, profile, 0); + return 1; + } + + /* Failure case */ + return 0; +} +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void /* PRIVATE */ +png_colorspace_set_rgb_coefficients(png_structrp png_ptr) +{ + /* Set the rgb_to_gray coefficients from the colorspace. */ + if (!png_ptr->rgb_to_gray_coefficients_set && + (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS) != 0) + { + /* png_set_background has not been called, get the coefficients from the Y + * values of the colorspace colorants. + */ + png_fixed_point r = png_ptr->colorspace.end_points_XYZ.red_Y; + png_fixed_point g = png_ptr->colorspace.end_points_XYZ.green_Y; + png_fixed_point b = png_ptr->colorspace.end_points_XYZ.blue_Y; + png_fixed_point total = r+g+b; + + if (total > 0 && + r >= 0 && png_muldiv(&r, r, 32768, total) && r >= 0 && r <= 32768 && + g >= 0 && png_muldiv(&g, g, 32768, total) && g >= 0 && g <= 32768 && + b >= 0 && png_muldiv(&b, b, 32768, total) && b >= 0 && b <= 32768 && + r+g+b <= 32769) + { + /* We allow 0 coefficients here. r+g+b may be 32769 if two or + * all of the coefficients were rounded up. Handle this by + * reducing the *largest* coefficient by 1; this matches the + * approach used for the default coefficients in pngrtran.c + */ + int add = 0; + + if (r+g+b > 32768) + add = -1; + else if (r+g+b < 32768) + add = 1; + + if (add != 0) + { + if (g >= r && g >= b) + g += add; + else if (r >= g && r >= b) + r += add; + else + b += add; + } + + /* Check for an internal error. */ + if (r+g+b != 32768) + png_error(png_ptr, + "internal error handling cHRM coefficients"); + + else + { + png_ptr->rgb_to_gray_red_coeff = (png_uint_16)r; + png_ptr->rgb_to_gray_green_coeff = (png_uint_16)g; + } + } + + /* This is a png_error at present even though it could be ignored - + * it should never happen, but it is important that if it does, the + * bug is fixed. + */ + else + png_error(png_ptr, "internal error handling cHRM->XYZ"); + } +} +#endif + +#endif /* COLORSPACE */ + +void /* PRIVATE */ +png_check_IHDR(png_const_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + int error = 0; + + /* Check for width and height valid values */ + if (width == 0) + { + png_warning(png_ptr, "Image width is zero in IHDR"); + error = 1; + } + + if (height == 0) + { + png_warning(png_ptr, "Image height is zero in IHDR"); + error = 1; + } + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max) + +# else + if (width > PNG_USER_WIDTH_MAX) +# endif + { + png_warning(png_ptr, "Image width exceeds user limit in IHDR"); + error = 1; + } + +# ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (height > png_ptr->user_height_max) +# else + if (height > PNG_USER_HEIGHT_MAX) +# endif + { + png_warning(png_ptr, "Image height exceeds user limit in IHDR"); + error = 1; + } + + if (width > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image width in IHDR"); + error = 1; + } + + if (height > PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Invalid image height in IHDR"); + error = 1; + } + + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 48 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* Check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + { + png_warning(png_ptr, "Invalid bit depth in IHDR"); + error = 1; + } + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + { + png_warning(png_ptr, "Invalid color type in IHDR"); + error = 1; + } + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + { + png_warning(png_ptr, "Invalid color type/bit depth combination in IHDR"); + error = 1; + } + + if (interlace_type >= PNG_INTERLACE_LAST) + { + png_warning(png_ptr, "Unknown interlace method in IHDR"); + error = 1; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Unknown compression method in IHDR"); + error = 1; + } + +# ifdef PNG_MNG_FEATURES_SUPPORTED + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) && + png_ptr->mng_features_permitted) + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + + if (filter_type != PNG_FILTER_TYPE_BASE) + { + if (!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } + + if (png_ptr->mode & PNG_HAVE_PNG_SIGNATURE) + { + png_warning(png_ptr, "Invalid filter method in IHDR"); + error = 1; + } + } + +# else + if (filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Unknown filter method in IHDR"); + error = 1; + } +# endif + + if (error == 1) + png_error(png_ptr, "Invalid IHDR data"); +} + +#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) +/* ASCII to fp functions */ +/* Check an ASCII formated floating point value, see the more detailed + * comments in pngpriv.h + */ +/* The following is used internally to preserve the sticky flags */ +#define png_fp_add(state, flags) ((state) |= (flags)) +#define png_fp_set(state, value) ((state) = (value) | ((state) & PNG_FP_STICKY)) + +int /* PRIVATE */ +png_check_fp_number(png_const_charp string, png_size_t size, int *statep, + png_size_tp whereami) +{ + int state = *statep; + png_size_t i = *whereami; + + while (i < size) + { + int type; + /* First find the type of the next character */ + switch (string[i]) + { + case 43: type = PNG_FP_SAW_SIGN; break; + case 45: type = PNG_FP_SAW_SIGN + PNG_FP_NEGATIVE; break; + case 46: type = PNG_FP_SAW_DOT; break; + case 48: type = PNG_FP_SAW_DIGIT; break; + case 49: case 50: case 51: case 52: + case 53: case 54: case 55: case 56: + case 57: type = PNG_FP_SAW_DIGIT + PNG_FP_NONZERO; break; + case 69: + case 101: type = PNG_FP_SAW_E; break; + default: goto PNG_FP_End; + } + + /* Now deal with this type according to the current + * state, the type is arranged to not overlap the + * bits of the PNG_FP_STATE. + */ + switch ((state & PNG_FP_STATE) + (type & PNG_FP_SAW_ANY)) + { + case PNG_FP_INTEGER + PNG_FP_SAW_SIGN: + if (state & PNG_FP_SAW_ANY) + goto PNG_FP_End; /* not a part of the number */ + + png_fp_add(state, type); + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_DOT: + /* Ok as trailer, ok as lead of fraction. */ + if (state & PNG_FP_SAW_DOT) /* two dots */ + goto PNG_FP_End; + + else if (state & PNG_FP_SAW_DIGIT) /* trailing dot? */ + png_fp_add(state, type); + + else + png_fp_set(state, PNG_FP_FRACTION | type); + + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_DIGIT: + if (state & PNG_FP_SAW_DOT) /* delayed fraction */ + png_fp_set(state, PNG_FP_FRACTION | PNG_FP_SAW_DOT); + + png_fp_add(state, type | PNG_FP_WAS_VALID); + + break; + + case PNG_FP_INTEGER + PNG_FP_SAW_E: + if ((state & PNG_FP_SAW_DIGIT) == 0) + goto PNG_FP_End; + + png_fp_set(state, PNG_FP_EXPONENT); + + break; + + /* case PNG_FP_FRACTION + PNG_FP_SAW_SIGN: + goto PNG_FP_End; ** no sign in fraction */ + + /* case PNG_FP_FRACTION + PNG_FP_SAW_DOT: + goto PNG_FP_End; ** Because SAW_DOT is always set */ + + case PNG_FP_FRACTION + PNG_FP_SAW_DIGIT: + png_fp_add(state, type | PNG_FP_WAS_VALID); + break; + + case PNG_FP_FRACTION + PNG_FP_SAW_E: + /* This is correct because the trailing '.' on an + * integer is handled above - so we can only get here + * with the sequence ".E" (with no preceding digits). + */ + if ((state & PNG_FP_SAW_DIGIT) == 0) + goto PNG_FP_End; + + png_fp_set(state, PNG_FP_EXPONENT); + + break; + + case PNG_FP_EXPONENT + PNG_FP_SAW_SIGN: + if (state & PNG_FP_SAW_ANY) + goto PNG_FP_End; /* not a part of the number */ + + png_fp_add(state, PNG_FP_SAW_SIGN); + + break; + + /* case PNG_FP_EXPONENT + PNG_FP_SAW_DOT: + goto PNG_FP_End; */ + + case PNG_FP_EXPONENT + PNG_FP_SAW_DIGIT: + png_fp_add(state, PNG_FP_SAW_DIGIT | PNG_FP_WAS_VALID); + + break; + + /* case PNG_FP_EXPONEXT + PNG_FP_SAW_E: + goto PNG_FP_End; */ + + default: goto PNG_FP_End; /* I.e. break 2 */ + } + + /* The character seems ok, continue. */ + ++i; + } + +PNG_FP_End: + /* Here at the end, update the state and return the correct + * return code. + */ + *statep = state; + *whereami = i; + + return (state & PNG_FP_SAW_DIGIT) != 0; +} + + +/* The same but for a complete string. */ +int +png_check_fp_string(png_const_charp string, png_size_t size) +{ + int state=0; + png_size_t char_index=0; + + if (png_check_fp_number(string, size, &state, &char_index) && + (char_index == size || string[char_index] == 0)) + return state /* must be non-zero - see above */; + + return 0; /* i.e. fail */ +} +#endif /* pCAL or sCAL */ + +#ifdef PNG_sCAL_SUPPORTED +# ifdef PNG_FLOATING_POINT_SUPPORTED +/* Utility used below - a simple accurate power of ten from an integral + * exponent. + */ +static double +png_pow10(int power) +{ + int recip = 0; + double d = 1; + + /* Handle negative exponent with a reciprocal at the end because + * 10 is exact whereas .1 is inexact in base 2 + */ + if (power < 0) + { + if (power < DBL_MIN_10_EXP) return 0; + recip = 1, power = -power; + } + + if (power > 0) + { + /* Decompose power bitwise. */ + double mult = 10; + do + { + if (power & 1) d *= mult; + mult *= mult; + power >>= 1; + } + while (power > 0); + + if (recip) d = 1/d; + } + /* else power is 0 and d is 1 */ + + return d; +} + +/* Function to format a floating point value in ASCII with a given + * precision. + */ +void /* PRIVATE */ +png_ascii_from_fp(png_const_structrp png_ptr, png_charp ascii, png_size_t size, + double fp, unsigned int precision) +{ + /* We use standard functions from math.h, but not printf because + * that would require stdio. The caller must supply a buffer of + * sufficient size or we will png_error. The tests on size and + * the space in ascii[] consumed are indicated below. + */ + if (precision < 1) + precision = DBL_DIG; + + /* Enforce the limit of the implementation precision too. */ + if (precision > DBL_DIG+1) + precision = DBL_DIG+1; + + /* Basic sanity checks */ + if (size >= precision+5) /* See the requirements below. */ + { + if (fp < 0) + { + fp = -fp; + *ascii++ = 45; /* '-' PLUS 1 TOTAL 1 */ + --size; + } + + if (fp >= DBL_MIN && fp <= DBL_MAX) + { + int exp_b10; /* A base 10 exponent */ + double base; /* 10^exp_b10 */ + + /* First extract a base 10 exponent of the number, + * the calculation below rounds down when converting + * from base 2 to base 10 (multiply by log10(2) - + * 0.3010, but 77/256 is 0.3008, so exp_b10 needs to + * be increased. Note that the arithmetic shift + * performs a floor() unlike C arithmetic - using a + * C multiply would break the following for negative + * exponents. + */ + (void)frexp(fp, &exp_b10); /* exponent to base 2 */ + + exp_b10 = (exp_b10 * 77) >> 8; /* <= exponent to base 10 */ + + /* Avoid underflow here. */ + base = png_pow10(exp_b10); /* May underflow */ + + while (base < DBL_MIN || base < fp) + { + /* And this may overflow. */ + double test = png_pow10(exp_b10+1); + + if (test <= DBL_MAX) + ++exp_b10, base = test; + + else + break; + } + + /* Normalize fp and correct exp_b10, after this fp is in the + * range [.1,1) and exp_b10 is both the exponent and the digit + * *before* which the decimal point should be inserted + * (starting with 0 for the first digit). Note that this + * works even if 10^exp_b10 is out of range because of the + * test on DBL_MAX above. + */ + fp /= base; + while (fp >= 1) fp /= 10, ++exp_b10; + + /* Because of the code above fp may, at this point, be + * less than .1, this is ok because the code below can + * handle the leading zeros this generates, so no attempt + * is made to correct that here. + */ + + { + int czero, clead, cdigits; + char exponent[10]; + + /* Allow up to two leading zeros - this will not lengthen + * the number compared to using E-n. + */ + if (exp_b10 < 0 && exp_b10 > -3) /* PLUS 3 TOTAL 4 */ + { + czero = -exp_b10; /* PLUS 2 digits: TOTAL 3 */ + exp_b10 = 0; /* Dot added below before first output. */ + } + else + czero = 0; /* No zeros to add */ + + /* Generate the digit list, stripping trailing zeros and + * inserting a '.' before a digit if the exponent is 0. + */ + clead = czero; /* Count of leading zeros */ + cdigits = 0; /* Count of digits in list. */ + + do + { + double d; + + fp *= 10; + /* Use modf here, not floor and subtract, so that + * the separation is done in one step. At the end + * of the loop don't break the number into parts so + * that the final digit is rounded. + */ + if (cdigits+czero-clead+1 < (int)precision) + fp = modf(fp, &d); + + else + { + d = floor(fp + .5); + + if (d > 9) + { + /* Rounding up to 10, handle that here. */ + if (czero > 0) + { + --czero, d = 1; + if (cdigits == 0) --clead; + } + else + { + while (cdigits > 0 && d > 9) + { + int ch = *--ascii; + + if (exp_b10 != (-1)) + ++exp_b10; + + else if (ch == 46) + { + ch = *--ascii, ++size; + /* Advance exp_b10 to '1', so that the + * decimal point happens after the + * previous digit. + */ + exp_b10 = 1; + } + + --cdigits; + d = ch - 47; /* I.e. 1+(ch-48) */ + } + + /* Did we reach the beginning? If so adjust the + * exponent but take into account the leading + * decimal point. + */ + if (d > 9) /* cdigits == 0 */ + { + if (exp_b10 == (-1)) + { + /* Leading decimal point (plus zeros?), if + * we lose the decimal point here it must + * be reentered below. + */ + int ch = *--ascii; + + if (ch == 46) + ++size, exp_b10 = 1; + + /* Else lost a leading zero, so 'exp_b10' is + * still ok at (-1) + */ + } + else + ++exp_b10; + + /* In all cases we output a '1' */ + d = 1; + } + } + } + fp = 0; /* Guarantees termination below. */ + } + + if (d == 0) + { + ++czero; + if (cdigits == 0) ++clead; + } + else + { + /* Included embedded zeros in the digit count. */ + cdigits += czero - clead; + clead = 0; + + while (czero > 0) + { + /* exp_b10 == (-1) means we just output the decimal + * place - after the DP don't adjust 'exp_b10' any + * more! + */ + if (exp_b10 != (-1)) + { + if (exp_b10 == 0) *ascii++ = 46, --size; + /* PLUS 1: TOTAL 4 */ + --exp_b10; + } + *ascii++ = 48, --czero; + } + + if (exp_b10 != (-1)) + { + if (exp_b10 == 0) *ascii++ = 46, --size; /* counted + above */ + --exp_b10; + } + *ascii++ = (char)(48 + (int)d), ++cdigits; + } + } + while (cdigits+czero-clead < (int)precision && fp > DBL_MIN); + + /* The total output count (max) is now 4+precision */ + + /* Check for an exponent, if we don't need one we are + * done and just need to terminate the string. At + * this point exp_b10==(-1) is effectively if flag - it got + * to '-1' because of the decrement after outputing + * the decimal point above (the exponent required is + * *not* -1!) + */ + if (exp_b10 >= (-1) && exp_b10 <= 2) + { + /* The following only happens if we didn't output the + * leading zeros above for negative exponent, so this + * doest add to the digit requirement. Note that the + * two zeros here can only be output if the two leading + * zeros were *not* output, so this doesn't increase + * the output count. + */ + while (--exp_b10 >= 0) *ascii++ = 48; + + *ascii = 0; + + /* Total buffer requirement (including the '\0') is + * 5+precision - see check at the start. + */ + return; + } + + /* Here if an exponent is required, adjust size for + * the digits we output but did not count. The total + * digit output here so far is at most 1+precision - no + * decimal point and no leading or trailing zeros have + * been output. + */ + size -= cdigits; + + *ascii++ = 69, --size; /* 'E': PLUS 1 TOTAL 2+precision */ + + /* The following use of an unsigned temporary avoids ambiguities in + * the signed arithmetic on exp_b10 and permits GCC at least to do + * better optimization. + */ + { + unsigned int uexp_b10; + + if (exp_b10 < 0) + { + *ascii++ = 45, --size; /* '-': PLUS 1 TOTAL 3+precision */ + uexp_b10 = -exp_b10; + } + + else + uexp_b10 = exp_b10; + + cdigits = 0; + + while (uexp_b10 > 0) + { + exponent[cdigits++] = (char)(48 + uexp_b10 % 10); + uexp_b10 /= 10; + } + } + + /* Need another size check here for the exponent digits, so + * this need not be considered above. + */ + if ((int)size > cdigits) + { + while (cdigits > 0) *ascii++ = exponent[--cdigits]; + + *ascii = 0; + + return; + } + } + } + else if (!(fp >= DBL_MIN)) + { + *ascii++ = 48; /* '0' */ + *ascii = 0; + return; + } + else + { + *ascii++ = 105; /* 'i' */ + *ascii++ = 110; /* 'n' */ + *ascii++ = 102; /* 'f' */ + *ascii = 0; + return; + } + } + + /* Here on buffer too small. */ + png_error(png_ptr, "ASCII conversion buffer too small"); +} + +# endif /* FLOATING_POINT */ + +# ifdef PNG_FIXED_POINT_SUPPORTED +/* Function to format a fixed point value in ASCII. + */ +void /* PRIVATE */ +png_ascii_from_fixed(png_const_structrp png_ptr, png_charp ascii, + png_size_t size, png_fixed_point fp) +{ + /* Require space for 10 decimal digits, a decimal point, a minus sign and a + * trailing \0, 13 characters: + */ + if (size > 12) + { + png_uint_32 num; + + /* Avoid overflow here on the minimum integer. */ + if (fp < 0) + *ascii++ = 45, --size, num = -fp; + else + num = fp; + + if (num <= 0x80000000) /* else overflowed */ + { + unsigned int ndigits = 0, first = 16 /* flag value */; + char digits[10]; + + while (num) + { + /* Split the low digit off num: */ + unsigned int tmp = num/10; + num -= tmp*10; + digits[ndigits++] = (char)(48 + num); + /* Record the first non-zero digit, note that this is a number + * starting at 1, it's not actually the array index. + */ + if (first == 16 && num > 0) + first = ndigits; + num = tmp; + } + + if (ndigits > 0) + { + while (ndigits > 5) *ascii++ = digits[--ndigits]; + /* The remaining digits are fractional digits, ndigits is '5' or + * smaller at this point. It is certainly not zero. Check for a + * non-zero fractional digit: + */ + if (first <= 5) + { + unsigned int i; + *ascii++ = 46; /* decimal point */ + /* ndigits may be <5 for small numbers, output leading zeros + * then ndigits digits to first: + */ + i = 5; + while (ndigits < i) *ascii++ = 48, --i; + while (ndigits >= first) *ascii++ = digits[--ndigits]; + /* Don't output the trailing zeros! */ + } + } + else + *ascii++ = 48; + + /* And null terminate the string: */ + *ascii = 0; + return; + } + } + + /* Here on buffer too small. */ + png_error(png_ptr, "ASCII conversion buffer too small"); +} +# endif /* FIXED_POINT */ +#endif /* READ_SCAL */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ + defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ + (defined(PNG_sCAL_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) +png_fixed_point +png_fixed(png_const_structrp png_ptr, double fp, png_const_charp) +{ + double r = floor(100000 * fp + .5); + + if (r > 2147483647. || r < -2147483648.) + png_fixed_error(png_ptr, text); + + return (png_fixed_point)r; +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || \ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +/* muldiv functions */ +/* This API takes signed arguments and rounds the result to the nearest + * integer (or, for a fixed point number - the standard argument - to + * the nearest .00001). Overflow and divide by zero are signalled in + * the result, a boolean - true on success, false on overflow. + */ +int +png_muldiv(png_fixed_point_p res, png_fixed_point a, png_int_32 times, + png_int_32 divisor) +{ + /* Return a * times / divisor, rounded. */ + if (divisor != 0) + { + if (a == 0 || times == 0) + { + *res = 0; + return 1; + } + else + { +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = a; + r *= times; + r /= divisor; + r = floor(r+.5); + + /* A png_fixed_point is a 32-bit integer. */ + if (r <= 2147483647. && r >= -2147483648.) + { + *res = (png_fixed_point)r; + return 1; + } +#else + int negative = 0; + png_uint_32 A, T, D; + png_uint_32 s16, s32, s00; + + if (a < 0) + negative = 1, A = -a; + else + A = a; + + if (times < 0) + negative = !negative, T = -times; + else + T = times; + + if (divisor < 0) + negative = !negative, D = -divisor; + else + D = divisor; + + /* Following can't overflow because the arguments only + * have 31 bits each, however the result may be 32 bits. + */ + s16 = (A >> 16) * (T & 0xffff) + + (A & 0xffff) * (T >> 16); + /* Can't overflow because the a*times bit is only 30 + * bits at most. + */ + s32 = (A >> 16) * (T >> 16) + (s16 >> 16); + s00 = (A & 0xffff) * (T & 0xffff); + + s16 = (s16 & 0xffff) << 16; + s00 += s16; + + if (s00 < s16) + ++s32; /* carry */ + + if (s32 < D) /* else overflow */ + { + /* s32.s00 is now the 64-bit product, do a standard + * division, we know that s32 < D, so the maximum + * required shift is 31. + */ + int bitshift = 32; + png_fixed_point result = 0; /* NOTE: signed */ + + while (--bitshift >= 0) + { + png_uint_32 d32, d00; + + if (bitshift > 0) + d32 = D >> (32-bitshift), d00 = D << bitshift; + + else + d32 = 0, d00 = D; + + if (s32 > d32) + { + if (s00 < d00) --s32; /* carry */ + s32 -= d32, s00 -= d00, result += 1<= d00) + s32 = 0, s00 -= d00, result += 1<= (D >> 1)) + ++result; + + if (negative) + result = -result; + + /* Check for overflow. */ + if ((negative && result <= 0) || (!negative && result >= 0)) + { + *res = result; + return 1; + } + } +#endif + } + } + + return 0; +} +#endif /* READ_GAMMA || INCH_CONVERSIONS */ + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) +/* The following is for when the caller doesn't much care about the + * result. + */ +png_fixed_point +png_muldiv_warn(png_const_structrp png_ptr, png_fixed_point a, png_int_32 times, + png_int_32 divisor) +{ + png_fixed_point result; + + if (png_muldiv(&result, a, times, divisor)) + return result; + + png_warning(png_ptr, "fixed point overflow ignored"); + return 0; +} +#endif + +#ifdef PNG_GAMMA_SUPPORTED /* more fixed point functions for gamma */ +/* Calculate a reciprocal, return 0 on div-by-zero or overflow. */ +png_fixed_point +png_reciprocal(png_fixed_point a) +{ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(1E10/a+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, 100000, 100000, a)) + return res; +#endif + + return 0; /* error/overflow */ +} + +/* This is the shared test on whether a gamma value is 'significant' - whether + * it is worth doing gamma correction. + */ +int /* PRIVATE */ +png_gamma_significant(png_fixed_point gamma_val) +{ + return gamma_val < PNG_FP_1 - PNG_GAMMA_THRESHOLD_FIXED || + gamma_val > PNG_FP_1 + PNG_GAMMA_THRESHOLD_FIXED; +} +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* A local convenience routine. */ +static png_fixed_point +png_product2(png_fixed_point a, png_fixed_point b) +{ + /* The required result is 1/a * 1/b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = a * 1E-5; + r *= b; + r = floor(r+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + png_fixed_point res; + + if (png_muldiv(&res, a, b, 100000)) + return res; +#endif + + return 0; /* overflow */ +} + +/* The inverse of the above. */ +png_fixed_point +png_reciprocal2(png_fixed_point a, png_fixed_point b) +{ + /* The required result is 1/a * 1/b; the following preserves accuracy. */ +#ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = 1E15/a; + r /= b; + r = floor(r+.5); + + if (r <= 2147483647. && r >= -2147483648.) + return (png_fixed_point)r; +#else + /* This may overflow because the range of png_fixed_point isn't symmetric, + * but this API is only used for the product of file and screen gamma so it + * doesn't matter that the smallest number it can produce is 1/21474, not + * 1/100000 + */ + png_fixed_point res = png_product2(a, b); + + if (res != 0) + return png_reciprocal(res); +#endif + + return 0; /* overflow */ +} +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_GAMMA_SUPPORTED /* gamma table code */ +#ifndef PNG_FLOATING_ARITHMETIC_SUPPORTED +/* Fixed point gamma. + * + * The code to calculate the tables used below can be found in the shell script + * contrib/tools/intgamma.sh + * + * To calculate gamma this code implements fast log() and exp() calls using only + * fixed point arithmetic. This code has sufficient precision for either 8-bit + * or 16-bit sample values. + * + * The tables used here were calculated using simple 'bc' programs, but C double + * precision floating point arithmetic would work fine. + * + * 8-bit log table + * This is a table of -log(value/255)/log(2) for 'value' in the range 128 to + * 255, so it's the base 2 logarithm of a normalized 8-bit floating point + * mantissa. The numbers are 32-bit fractions. + */ +static const png_uint_32 +png_8bit_l2[128] = +{ + 4270715492U, 4222494797U, 4174646467U, 4127164793U, 4080044201U, 4033279239U, + 3986864580U, 3940795015U, 3895065449U, 3849670902U, 3804606499U, 3759867474U, + 3715449162U, 3671346997U, 3627556511U, 3584073329U, 3540893168U, 3498011834U, + 3455425220U, 3413129301U, 3371120137U, 3329393864U, 3287946700U, 3246774933U, + 3205874930U, 3165243125U, 3124876025U, 3084770202U, 3044922296U, 3005329011U, + 2965987113U, 2926893432U, 2888044853U, 2849438323U, 2811070844U, 2772939474U, + 2735041326U, 2697373562U, 2659933400U, 2622718104U, 2585724991U, 2548951424U, + 2512394810U, 2476052606U, 2439922311U, 2404001468U, 2368287663U, 2332778523U, + 2297471715U, 2262364947U, 2227455964U, 2192742551U, 2158222529U, 2123893754U, + 2089754119U, 2055801552U, 2022034013U, 1988449497U, 1955046031U, 1921821672U, + 1888774511U, 1855902668U, 1823204291U, 1790677560U, 1758320682U, 1726131893U, + 1694109454U, 1662251657U, 1630556815U, 1599023271U, 1567649391U, 1536433567U, + 1505374214U, 1474469770U, 1443718700U, 1413119487U, 1382670639U, 1352370686U, + 1322218179U, 1292211689U, 1262349810U, 1232631153U, 1203054352U, 1173618059U, + 1144320946U, 1115161701U, 1086139034U, 1057251672U, 1028498358U, 999877854U, + 971388940U, 943030410U, 914801076U, 886699767U, 858725327U, 830876614U, + 803152505U, 775551890U, 748073672U, 720716771U, 693480120U, 666362667U, + 639363374U, 612481215U, 585715177U, 559064263U, 532527486U, 506103872U, + 479792461U, 453592303U, 427502463U, 401522014U, 375650043U, 349885648U, + 324227938U, 298676034U, 273229066U, 247886176U, 222646516U, 197509248U, + 172473545U, 147538590U, 122703574U, 97967701U, 73330182U, 48790236U, + 24347096U, 0U + +#if 0 + /* The following are the values for 16-bit tables - these work fine for the + * 8-bit conversions but produce very slightly larger errors in the 16-bit + * log (about 1.2 as opposed to 0.7 absolute error in the final value). To + * use these all the shifts below must be adjusted appropriately. + */ + 65166, 64430, 63700, 62976, 62257, 61543, 60835, 60132, 59434, 58741, 58054, + 57371, 56693, 56020, 55352, 54689, 54030, 53375, 52726, 52080, 51439, 50803, + 50170, 49542, 48918, 48298, 47682, 47070, 46462, 45858, 45257, 44661, 44068, + 43479, 42894, 42312, 41733, 41159, 40587, 40020, 39455, 38894, 38336, 37782, + 37230, 36682, 36137, 35595, 35057, 34521, 33988, 33459, 32932, 32408, 31887, + 31369, 30854, 30341, 29832, 29325, 28820, 28319, 27820, 27324, 26830, 26339, + 25850, 25364, 24880, 24399, 23920, 23444, 22970, 22499, 22029, 21562, 21098, + 20636, 20175, 19718, 19262, 18808, 18357, 17908, 17461, 17016, 16573, 16132, + 15694, 15257, 14822, 14390, 13959, 13530, 13103, 12678, 12255, 11834, 11415, + 10997, 10582, 10168, 9756, 9346, 8937, 8531, 8126, 7723, 7321, 6921, 6523, + 6127, 5732, 5339, 4947, 4557, 4169, 3782, 3397, 3014, 2632, 2251, 1872, 1495, + 1119, 744, 372 +#endif +}; + +static png_int_32 +png_log8bit(unsigned int x) +{ + unsigned int lg2 = 0; + /* Each time 'x' is multiplied by 2, 1 must be subtracted off the final log, + * because the log is actually negate that means adding 1. The final + * returned value thus has the range 0 (for 255 input) to 7.994 (for 1 + * input), return -1 for the overflow (log 0) case, - so the result is + * always at most 19 bits. + */ + if ((x &= 0xff) == 0) + return -1; + + if ((x & 0xf0) == 0) + lg2 = 4, x <<= 4; + + if ((x & 0xc0) == 0) + lg2 += 2, x <<= 2; + + if ((x & 0x80) == 0) + lg2 += 1, x <<= 1; + + /* result is at most 19 bits, so this cast is safe: */ + return (png_int_32)((lg2 << 16) + ((png_8bit_l2[x-128]+32768)>>16)); +} + +/* The above gives exact (to 16 binary places) log2 values for 8-bit images, + * for 16-bit images we use the most significant 8 bits of the 16-bit value to + * get an approximation then multiply the approximation by a correction factor + * determined by the remaining up to 8 bits. This requires an additional step + * in the 16-bit case. + * + * We want log2(value/65535), we have log2(v'/255), where: + * + * value = v' * 256 + v'' + * = v' * f + * + * So f is value/v', which is equal to (256+v''/v') since v' is in the range 128 + * to 255 and v'' is in the range 0 to 255 f will be in the range 256 to less + * than 258. The final factor also needs to correct for the fact that our 8-bit + * value is scaled by 255, whereas the 16-bit values must be scaled by 65535. + * + * This gives a final formula using a calculated value 'x' which is value/v' and + * scaling by 65536 to match the above table: + * + * log2(x/257) * 65536 + * + * Since these numbers are so close to '1' we can use simple linear + * interpolation between the two end values 256/257 (result -368.61) and 258/257 + * (result 367.179). The values used below are scaled by a further 64 to give + * 16-bit precision in the interpolation: + * + * Start (256): -23591 + * Zero (257): 0 + * End (258): 23499 + */ +static png_int_32 +png_log16bit(png_uint_32 x) +{ + unsigned int lg2 = 0; + + /* As above, but now the input has 16 bits. */ + if ((x &= 0xffff) == 0) + return -1; + + if ((x & 0xff00) == 0) + lg2 = 8, x <<= 8; + + if ((x & 0xf000) == 0) + lg2 += 4, x <<= 4; + + if ((x & 0xc000) == 0) + lg2 += 2, x <<= 2; + + if ((x & 0x8000) == 0) + lg2 += 1, x <<= 1; + + /* Calculate the base logarithm from the top 8 bits as a 28-bit fractional + * value. + */ + lg2 <<= 28; + lg2 += (png_8bit_l2[(x>>8)-128]+8) >> 4; + + /* Now we need to interpolate the factor, this requires a division by the top + * 8 bits. Do this with maximum precision. + */ + x = ((x << 16) + (x >> 9)) / (x >> 8); + + /* Since we divided by the top 8 bits of 'x' there will be a '1' at 1<<24, + * the value at 1<<16 (ignoring this) will be 0 or 1; this gives us exactly + * 16 bits to interpolate to get the low bits of the result. Round the + * answer. Note that the end point values are scaled by 64 to retain overall + * precision and that 'lg2' is current scaled by an extra 12 bits, so adjust + * the overall scaling by 6-12. Round at every step. + */ + x -= 1U << 24; + + if (x <= 65536U) /* <= '257' */ + lg2 += ((23591U * (65536U-x)) + (1U << (16+6-12-1))) >> (16+6-12); + + else + lg2 -= ((23499U * (x-65536U)) + (1U << (16+6-12-1))) >> (16+6-12); + + /* Safe, because the result can't have more than 20 bits: */ + return (png_int_32)((lg2 + 2048) >> 12); +} + +/* The 'exp()' case must invert the above, taking a 20-bit fixed point + * logarithmic value and returning a 16 or 8-bit number as appropriate. In + * each case only the low 16 bits are relevant - the fraction - since the + * integer bits (the top 4) simply determine a shift. + * + * The worst case is the 16-bit distinction between 65535 and 65534, this + * requires perhaps spurious accuracty in the decoding of the logarithm to + * distinguish log2(65535/65534.5) - 10^-5 or 17 bits. There is little chance + * of getting this accuracy in practice. + * + * To deal with this the following exp() function works out the exponent of the + * frational part of the logarithm by using an accurate 32-bit value from the + * top four fractional bits then multiplying in the remaining bits. + */ +static const png_uint_32 +png_32bit_exp[16] = +{ + /* NOTE: the first entry is deliberately set to the maximum 32-bit value. */ + 4294967295U, 4112874773U, 3938502376U, 3771522796U, 3611622603U, 3458501653U, + 3311872529U, 3171459999U, 3037000500U, 2908241642U, 2784941738U, 2666869345U, + 2553802834U, 2445529972U, 2341847524U, 2242560872U +}; + +/* Adjustment table; provided to explain the numbers in the code below. */ +#if 0 +for (i=11;i>=0;--i){ print i, " ", (1 - e(-(2^i)/65536*l(2))) * 2^(32-i), "\n"} + 11 44937.64284865548751208448 + 10 45180.98734845585101160448 + 9 45303.31936980687359311872 + 8 45364.65110595323018870784 + 7 45395.35850361789624614912 + 6 45410.72259715102037508096 + 5 45418.40724413220722311168 + 4 45422.25021786898173001728 + 3 45424.17186732298419044352 + 2 45425.13273269940811464704 + 1 45425.61317555035558641664 + 0 45425.85339951654943850496 +#endif + +static png_uint_32 +png_exp(png_fixed_point x) +{ + if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */ + { + /* Obtain a 4-bit approximation */ + png_uint_32 e = png_32bit_exp[(x >> 12) & 0xf]; + + /* Incorporate the low 12 bits - these decrease the returned value by + * multiplying by a number less than 1 if the bit is set. The multiplier + * is determined by the above table and the shift. Notice that the values + * converge on 45426 and this is used to allow linear interpolation of the + * low bits. + */ + if (x & 0x800) + e -= (((e >> 16) * 44938U) + 16U) >> 5; + + if (x & 0x400) + e -= (((e >> 16) * 45181U) + 32U) >> 6; + + if (x & 0x200) + e -= (((e >> 16) * 45303U) + 64U) >> 7; + + if (x & 0x100) + e -= (((e >> 16) * 45365U) + 128U) >> 8; + + if (x & 0x080) + e -= (((e >> 16) * 45395U) + 256U) >> 9; + + if (x & 0x040) + e -= (((e >> 16) * 45410U) + 512U) >> 10; + + /* And handle the low 6 bits in a single block. */ + e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9; + + /* Handle the upper bits of x. */ + e >>= x >> 16; + return e; + } + + /* Check for overflow */ + if (x <= 0) + return png_32bit_exp[0]; + + /* Else underflow */ + return 0; +} + +static png_byte +png_exp8bit(png_fixed_point lg2) +{ + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..255 by multiplying by 256-1, note that the + * second, rounding, step can't overflow because of the first, subtraction, + * step. + */ + x -= x >> 8; + return (png_byte)((x + 0x7fffffU) >> 24); +} + +static png_uint_16 +png_exp16bit(png_fixed_point lg2) +{ + /* Get a 32-bit value: */ + png_uint_32 x = png_exp(lg2); + + /* Convert the 32-bit value to 0..65535 by multiplying by 65536-1: */ + x -= x >> 16; + return (png_uint_16)((x + 32767U) >> 16); +} +#endif /* FLOATING_ARITHMETIC */ + +png_byte +png_gamma_8bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value > 0 && value < 255) + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(255*pow(value/255.,gamma_val*.00001)+.5); + return (png_byte)r; +# else + png_int_32 lg2 = png_log8bit(value); + png_fixed_point res; + + if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return png_exp8bit(res); + + /* Overflow. */ + value = 0; +# endif + } + + return (png_byte)value; +} + +png_uint_16 +png_gamma_16bit_correct(unsigned int value, png_fixed_point gamma_val) +{ + if (value > 0 && value < 65535) + { +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + double r = floor(65535*pow(value/65535.,gamma_val*.00001)+.5); + return (png_uint_16)r; +# else + png_int_32 lg2 = png_log16bit(value); + png_fixed_point res; + + if (png_muldiv(&res, gamma_val, lg2, PNG_FP_1)) + return png_exp16bit(res); + + /* Overflow. */ + value = 0; +# endif + } + + return (png_uint_16)value; +} + +/* This does the right thing based on the bit_depth field of the + * png_struct, interpreting values as 8-bit or 16-bit. While the result + * is nominally a 16-bit value if bit depth is 8 then the result is + * 8-bit (as are the arguments.) + */ +png_uint_16 /* PRIVATE */ +png_gamma_correct(png_structrp png_ptr, unsigned int value, + png_fixed_point gamma_val) +{ + if (png_ptr->bit_depth == 8) + return png_gamma_8bit_correct(value, gamma_val); + + else + return png_gamma_16bit_correct(value, gamma_val); +} + +/* Internal function to build a single 16-bit table - the table consists of + * 'num' 256 entry subtables, where 'num' is determined by 'shift' - the amount + * to shift the input values right (or 16-number_of_signifiant_bits). + * + * The caller is responsible for ensuring that the table gets cleaned up on + * png_error (i.e. if one of the mallocs below fails) - i.e. the *table argument + * should be somewhere that will be cleaned. + */ +static void +png_build_16bit_table(png_structrp png_ptr, png_uint_16pp *ptable, + PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val) +{ + /* Various values derived from 'shift': */ + PNG_CONST unsigned int num = 1U << (8U - shift); + PNG_CONST unsigned int max = (1U << (16U - shift))-1U; + PNG_CONST unsigned int max_by_2 = 1U << (15U-shift); + unsigned int i; + + png_uint_16pp table = *ptable = + (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_uint_16p sub_table = table[i] = + (png_uint_16p)png_malloc(png_ptr, 256 * (sizeof (png_uint_16))); + + /* The 'threshold' test is repeated here because it can arise for one of + * the 16-bit tables even if the others don't hit it. + */ + if (png_gamma_significant(gamma_val)) + { + /* The old code would overflow at the end and this would cause the + * 'pow' function to return a result >1, resulting in an + * arithmetic error. This code follows the spec exactly; ig is + * the recovered input sample, it always has 8-16 bits. + * + * We want input * 65535/max, rounded, the arithmetic fits in 32 + * bits (unsigned) so long as max <= 32767. + */ + unsigned int j; + for (j = 0; j < 256; j++) + { + png_uint_32 ig = (j << (8-shift)) + i; +# ifdef PNG_FLOATING_ARITHMETIC_SUPPORTED + /* Inline the 'max' scaling operation: */ + double d = floor(65535*pow(ig/(double)max, gamma_val*.00001)+.5); + sub_table[j] = (png_uint_16)d; +# else + if (shift) + ig = (ig * 65535U + max_by_2)/max; + + sub_table[j] = png_gamma_16bit_correct(ig, gamma_val); +# endif + } + } + else + { + /* We must still build a table, but do it the fast way. */ + unsigned int j; + + for (j = 0; j < 256; j++) + { + png_uint_32 ig = (j << (8-shift)) + i; + + if (shift) + ig = (ig * 65535U + max_by_2)/max; + + sub_table[j] = (png_uint_16)ig; + } + } + } +} + +/* NOTE: this function expects the *inverse* of the overall gamma transformation + * required. + */ +static void +png_build_16to8_table(png_structrp png_ptr, png_uint_16pp *ptable, + PNG_CONST unsigned int shift, PNG_CONST png_fixed_point gamma_val) +{ + PNG_CONST unsigned int num = 1U << (8U - shift); + PNG_CONST unsigned int max = (1U << (16U - shift))-1U; + unsigned int i; + png_uint_32 last; + + png_uint_16pp table = *ptable = + (png_uint_16pp)png_calloc(png_ptr, num * (sizeof (png_uint_16p))); + + /* 'num' is the number of tables and also the number of low bits of low + * bits of the input 16-bit value used to select a table. Each table is + * itself index by the high 8 bits of the value. + */ + for (i = 0; i < num; i++) + table[i] = (png_uint_16p)png_malloc(png_ptr, + 256 * (sizeof (png_uint_16))); + + /* 'gamma_val' is set to the reciprocal of the value calculated above, so + * pow(out,g) is an *input* value. 'last' is the last input value set. + * + * In the loop 'i' is used to find output values. Since the output is + * 8-bit there are only 256 possible values. The tables are set up to + * select the closest possible output value for each input by finding + * the input value at the boundary between each pair of output values + * and filling the table up to that boundary with the lower output + * value. + * + * The boundary values are 0.5,1.5..253.5,254.5. Since these are 9-bit + * values the code below uses a 16-bit value in i; the values start at + * 128.5 (for 0.5) and step by 257, for a total of 254 values (the last + * entries are filled with 255). Start i at 128 and fill all 'last' + * table entries <= 'max' + */ + last = 0; + for (i = 0; i < 255; ++i) /* 8-bit output value */ + { + /* Find the corresponding maximum input value */ + png_uint_16 out = (png_uint_16)(i * 257U); /* 16-bit output value */ + + /* Find the boundary value in 16 bits: */ + png_uint_32 bound = png_gamma_16bit_correct(out+128U, gamma_val); + + /* Adjust (round) to (16-shift) bits: */ + bound = (bound * max + 32768U)/65535U + 1U; + + while (last < bound) + { + table[last & (0xffU >> shift)][last >> (8U - shift)] = out; + last++; + } + } + + /* And fill in the final entries. */ + while (last < (num << 8)) + { + table[last & (0xff >> shift)][last >> (8U - shift)] = 65535U; + last++; + } +} + +/* Build a single 8-bit table: same as the 16-bit case but much simpler (and + * typically much faster). Note that libpng currently does no sBIT processing + * (apparently contrary to the spec) so a 256 entry table is always generated. + */ +static void +png_build_8bit_table(png_structrp png_ptr, png_bytepp ptable, + PNG_CONST png_fixed_point gamma_val) +{ + unsigned int i; + png_bytep table = *ptable = (png_bytep)png_malloc(png_ptr, 256); + + if (png_gamma_significant(gamma_val)) for (i=0; i<256; i++) + table[i] = png_gamma_8bit_correct(i, gamma_val); + + else for (i=0; i<256; ++i) + table[i] = (png_byte)i; +} + +/* Used from png_read_destroy and below to release the memory used by the gamma + * tables. + */ +void /* PRIVATE */ +png_destroy_gamma_table(png_structrp png_ptr) +{ + png_free(png_ptr, png_ptr->gamma_table); + png_ptr->gamma_table = NULL; + + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + png_ptr->gamma_16_table = NULL; + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_ptr->gamma_from_1 = NULL; + png_free(png_ptr, png_ptr->gamma_to_1); + png_ptr->gamma_to_1 = NULL; + + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + png_ptr->gamma_16_from_1 = NULL; + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + png_ptr->gamma_16_to_1 = NULL; + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +} + +/* 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_structrp png_ptr, int bit_depth) +{ + png_debug(1, "in png_build_gamma_table"); + + /* Remove any existing table; this copes with multiple calls to + * png_read_update_info. The warning is because building the gamma tables + * multiple times is a performance hit - it's harmless but the ability to call + * png_read_update_info() multiple times is new in 1.5.6 so it seems sensible + * to warn if the app introduces such a hit. + */ + if (png_ptr->gamma_table != NULL || png_ptr->gamma_16_table != NULL) + { + png_warning(png_ptr, "gamma table being rebuilt"); + png_destroy_gamma_table(png_ptr); + } + + if (bit_depth <= 8) + { + png_build_8bit_table(png_ptr, &png_ptr->gamma_table, + png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) + { + png_build_8bit_table(png_ptr, &png_ptr->gamma_to_1, + png_reciprocal(png_ptr->colorspace.gamma)); + + png_build_8bit_table(png_ptr, &png_ptr->gamma_from_1, + png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ + } + else + { + png_byte shift, sig_bit; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = png_ptr->sig_bit.red; + + if (png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + + if (png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + sig_bit = png_ptr->sig_bit.gray; + + /* 16-bit gamma code uses this equation: + * + * ov = table[(iv & 0xff) >> gamma_shift][iv >> 8] + * + * Where 'iv' is the input color value and 'ov' is the output value - + * pow(iv, gamma). + * + * Thus the gamma table consists of up to 256 256 entry tables. The table + * is selected by the (8-gamma_shift) most significant of the low 8 bits of + * the color value then indexed by the upper 8 bits: + * + * table[low bits][high 8 bits] + * + * So the table 'n' corresponds to all those 'iv' of: + * + * ..<(n+1 << gamma_shift)-1> + * + */ + if (sig_bit > 0 && sig_bit < 16U) + shift = (png_byte)(16U - sig_bit); /* shift == insignificant bits */ + + else + shift = 0; /* keep all 16 bits */ + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) + { + /* PNG_MAX_GAMMA_8 is the number of bits to keep - effectively + * the significant bits in the *input* when the output will + * eventually be 8 bits. By default it is 11. + */ + if (shift < (16U - PNG_MAX_GAMMA_8)) + shift = (16U - PNG_MAX_GAMMA_8); + } + + if (shift > 8U) + shift = 8U; /* Guarantees at least one table! */ + + png_ptr->gamma_shift = shift; + +#ifdef PNG_16BIT_SUPPORTED + /* NOTE: prior to 1.5.4 this test used to include PNG_BACKGROUND (now + * PNG_COMPOSE). This effectively smashed the background calculation for + * 16-bit output because the 8-bit table assumes the result will be reduced + * to 8 bits. + */ + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_SCALE_16_TO_8)) +#endif + png_build_16to8_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_ptr->screen_gamma > 0 ? png_product2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); + +#ifdef PNG_16BIT_SUPPORTED + else + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_table, shift, + png_ptr->screen_gamma > 0 ? png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma) : PNG_FP_1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_COMPOSE | PNG_RGB_TO_GRAY)) + { + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_to_1, shift, + png_reciprocal(png_ptr->colorspace.gamma)); + + /* Notice that the '16 from 1' table should be full precision, however + * the lookup on this table still uses gamma_shift, so it can't be. + * TODO: fix this. + */ + png_build_16bit_table(png_ptr, &png_ptr->gamma_16_from_1, shift, + png_ptr->screen_gamma > 0 ? png_reciprocal(png_ptr->screen_gamma) : + png_ptr->colorspace.gamma/* Probably doing rgb_to_gray */); + } +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ + } +} +#endif /* READ_GAMMA */ + +/* HARDWARE OPTION SUPPORT */ +#ifdef PNG_SET_OPTION_SUPPORTED +int PNGAPI +png_set_option(png_structrp png_ptr, int option, int onoff) +{ + if (png_ptr != NULL && option >= 0 && option < PNG_OPTION_NEXT && + (option & 1) == 0) + { + int mask = 3 << option; + int setting = (2 + (onoff != 0)) << option; + int current = png_ptr->options; + + png_ptr->options = (png_byte)((current & ~mask) | setting); + + return (current & mask) >> option; + } + + return PNG_OPTION_INVALID; +} +#endif + +/* sRGB support */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +/* sRGB conversion tables; these are machine generated with the code in + * contrib/tools/makesRGB.c. The actual sRGB transfer curve defined in the + * specification (see the article at http://en.wikipedia.org/wiki/SRGB) + * is used, not the gamma=1/2.2 approximation use elsewhere in libpng. + * The sRGB to linear table is exact (to the nearest 16 bit linear fraction). + * The inverse (linear to sRGB) table has accuracies as follows: + * + * For all possible (255*65535+1) input values: + * + * error: -0.515566 - 0.625971, 79441 (0.475369%) of readings inexact + * + * For the input values corresponding to the 65536 16-bit values: + * + * error: -0.513727 - 0.607759, 308 (0.469978%) of readings inexact + * + * In all cases the inexact readings are off by one. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* The convert-to-sRGB table is only currently required for read. */ +const png_uint_16 png_sRGB_table[256] = +{ + 0,20,40,60,80,99,119,139, + 159,179,199,219,241,264,288,313, + 340,367,396,427,458,491,526,562, + 599,637,677,718,761,805,851,898, + 947,997,1048,1101,1156,1212,1270,1330, + 1391,1453,1517,1583,1651,1720,1790,1863, + 1937,2013,2090,2170,2250,2333,2418,2504, + 2592,2681,2773,2866,2961,3058,3157,3258, + 3360,3464,3570,3678,3788,3900,4014,4129, + 4247,4366,4488,4611,4736,4864,4993,5124, + 5257,5392,5530,5669,5810,5953,6099,6246, + 6395,6547,6700,6856,7014,7174,7335,7500, + 7666,7834,8004,8177,8352,8528,8708,8889, + 9072,9258,9445,9635,9828,10022,10219,10417, + 10619,10822,11028,11235,11446,11658,11873,12090, + 12309,12530,12754,12980,13209,13440,13673,13909, + 14146,14387,14629,14874,15122,15371,15623,15878, + 16135,16394,16656,16920,17187,17456,17727,18001, + 18277,18556,18837,19121,19407,19696,19987,20281, + 20577,20876,21177,21481,21787,22096,22407,22721, + 23038,23357,23678,24002,24329,24658,24990,25325, + 25662,26001,26344,26688,27036,27386,27739,28094, + 28452,28813,29176,29542,29911,30282,30656,31033, + 31412,31794,32179,32567,32957,33350,33745,34143, + 34544,34948,35355,35764,36176,36591,37008,37429, + 37852,38278,38706,39138,39572,40009,40449,40891, + 41337,41785,42236,42690,43147,43606,44069,44534, + 45002,45473,45947,46423,46903,47385,47871,48359, + 48850,49344,49841,50341,50844,51349,51858,52369, + 52884,53401,53921,54445,54971,55500,56032,56567, + 57105,57646,58190,58737,59287,59840,60396,60955, + 61517,62082,62650,63221,63795,64372,64952,65535 +}; + +#endif /* simplified read only */ + +/* The base/delta tables are required for both read and write (but currently + * only the simplified versions.) + */ +const png_uint_16 png_sRGB_base[512] = +{ + 128,1782,3383,4644,5675,6564,7357,8074, + 8732,9346,9921,10463,10977,11466,11935,12384, + 12816,13233,13634,14024,14402,14769,15125,15473, + 15812,16142,16466,16781,17090,17393,17690,17981, + 18266,18546,18822,19093,19359,19621,19879,20133, + 20383,20630,20873,21113,21349,21583,21813,22041, + 22265,22487,22707,22923,23138,23350,23559,23767, + 23972,24175,24376,24575,24772,24967,25160,25352, + 25542,25730,25916,26101,26284,26465,26645,26823, + 27000,27176,27350,27523,27695,27865,28034,28201, + 28368,28533,28697,28860,29021,29182,29341,29500, + 29657,29813,29969,30123,30276,30429,30580,30730, + 30880,31028,31176,31323,31469,31614,31758,31902, + 32045,32186,32327,32468,32607,32746,32884,33021, + 33158,33294,33429,33564,33697,33831,33963,34095, + 34226,34357,34486,34616,34744,34873,35000,35127, + 35253,35379,35504,35629,35753,35876,35999,36122, + 36244,36365,36486,36606,36726,36845,36964,37083, + 37201,37318,37435,37551,37668,37783,37898,38013, + 38127,38241,38354,38467,38580,38692,38803,38915, + 39026,39136,39246,39356,39465,39574,39682,39790, + 39898,40005,40112,40219,40325,40431,40537,40642, + 40747,40851,40955,41059,41163,41266,41369,41471, + 41573,41675,41777,41878,41979,42079,42179,42279, + 42379,42478,42577,42676,42775,42873,42971,43068, + 43165,43262,43359,43456,43552,43648,43743,43839, + 43934,44028,44123,44217,44311,44405,44499,44592, + 44685,44778,44870,44962,45054,45146,45238,45329, + 45420,45511,45601,45692,45782,45872,45961,46051, + 46140,46229,46318,46406,46494,46583,46670,46758, + 46846,46933,47020,47107,47193,47280,47366,47452, + 47538,47623,47709,47794,47879,47964,48048,48133, + 48217,48301,48385,48468,48552,48635,48718,48801, + 48884,48966,49048,49131,49213,49294,49376,49458, + 49539,49620,49701,49782,49862,49943,50023,50103, + 50183,50263,50342,50422,50501,50580,50659,50738, + 50816,50895,50973,51051,51129,51207,51285,51362, + 51439,51517,51594,51671,51747,51824,51900,51977, + 52053,52129,52205,52280,52356,52432,52507,52582, + 52657,52732,52807,52881,52956,53030,53104,53178, + 53252,53326,53400,53473,53546,53620,53693,53766, + 53839,53911,53984,54056,54129,54201,54273,54345, + 54417,54489,54560,54632,54703,54774,54845,54916, + 54987,55058,55129,55199,55269,55340,55410,55480, + 55550,55620,55689,55759,55828,55898,55967,56036, + 56105,56174,56243,56311,56380,56448,56517,56585, + 56653,56721,56789,56857,56924,56992,57059,57127, + 57194,57261,57328,57395,57462,57529,57595,57662, + 57728,57795,57861,57927,57993,58059,58125,58191, + 58256,58322,58387,58453,58518,58583,58648,58713, + 58778,58843,58908,58972,59037,59101,59165,59230, + 59294,59358,59422,59486,59549,59613,59677,59740, + 59804,59867,59930,59993,60056,60119,60182,60245, + 60308,60370,60433,60495,60558,60620,60682,60744, + 60806,60868,60930,60992,61054,61115,61177,61238, + 61300,61361,61422,61483,61544,61605,61666,61727, + 61788,61848,61909,61969,62030,62090,62150,62211, + 62271,62331,62391,62450,62510,62570,62630,62689, + 62749,62808,62867,62927,62986,63045,63104,63163, + 63222,63281,63340,63398,63457,63515,63574,63632, + 63691,63749,63807,63865,63923,63981,64039,64097, + 64155,64212,64270,64328,64385,64443,64500,64557, + 64614,64672,64729,64786,64843,64900,64956,65013, + 65070,65126,65183,65239,65296,65352,65409,65465 +}; + +const png_byte png_sRGB_delta[512] = +{ + 207,201,158,129,113,100,90,82,77,72,68,64,61,59,56,54, + 52,50,49,47,46,45,43,42,41,40,39,39,38,37,36,36, + 35,34,34,33,33,32,32,31,31,30,30,30,29,29,28,28, + 28,27,27,27,27,26,26,26,25,25,25,25,24,24,24,24, + 23,23,23,23,23,22,22,22,22,22,22,21,21,21,21,21, + 21,20,20,20,20,20,20,20,20,19,19,19,19,19,19,19, + 19,18,18,18,18,18,18,18,18,18,18,17,17,17,17,17, + 17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,15,15,15,15,15,15,15,15,15,15,15,15, + 15,15,15,15,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 +}; +#endif /* SIMPLIFIED READ/WRITE sRGB support */ + +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +static int +png_image_free_function(png_voidp argument) +{ + png_imagep image = png_voidcast(png_imagep, argument); + png_controlp cp = image->opaque; + png_control c; + + /* Double check that we have a png_ptr - it should be impossible to get here + * without one. + */ + if (cp->png_ptr == NULL) + return 0; + + /* First free any data held in the control structure. */ +# ifdef PNG_STDIO_SUPPORTED + if (cp->owned_file) + { + FILE *fp = png_voidcast(FILE*, cp->png_ptr->io_ptr); + cp->owned_file = 0; + + /* Ignore errors here. */ + if (fp != NULL) + { + cp->png_ptr->io_ptr = NULL; + (void)fclose(fp); + } + } +# endif + + /* Copy the control structure so that the original, allocated, version can be + * safely freed. Notice that a png_error here stops the remainder of the + * cleanup, but this is probably fine because that would indicate bad memory + * problems anyway. + */ + c = *cp; + image->opaque = &c; + png_free(c.png_ptr, cp); + + /* Then the structures, calling the correct API. */ + if (c.for_write) + { +# ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED + png_destroy_write_struct(&c.png_ptr, &c.info_ptr); +# else + png_error(c.png_ptr, "simplified write not supported"); +# endif + } + else + { +# ifdef PNG_SIMPLIFIED_READ_SUPPORTED + png_destroy_read_struct(&c.png_ptr, &c.info_ptr, NULL); +# else + png_error(c.png_ptr, "simplified read not supported"); +# endif + } + + /* Success. */ + return 1; +} + +void PNGAPI +png_image_free(png_imagep image) +{ + /* Safely call the real function, but only if doing so is safe at this point + * (if not inside an error handling context). Otherwise assume + * png_safe_execute will call this API after the return. + */ + if (image != NULL && image->opaque != NULL && + image->opaque->error_buf == NULL) + { + /* Ignore errors here: */ + (void)png_safe_execute(image, png_image_free_function, image); + image->opaque = NULL; + } +} + +int /* PRIVATE */ +png_image_error(png_imagep image, png_const_charp error_message) +{ + /* Utility to log an error. */ + png_safecat(image->message, (sizeof image->message), 0, error_message); + image->warning_or_error |= PNG_IMAGE_ERROR; + png_image_free(image); + return 0; +} + +#endif /* SIMPLIFIED READ/WRITE */ #endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.h b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.h index a20bbd4..d447233 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.h +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/png.h @@ -1,24 +1,26 @@ /* png.h - header file for PNG reference library * - * libpng version 1.2.21 - October 4, 2007 - * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * libpng version 1.6.1 - March 28, 2013 + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license (See LICENSE, below) + * * Authors and maintainers: - * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat - * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger - * libpng versions 0.97, January 1998, through 1.2.21 - October 4, 2007: Glenn - * See also "Contributing Authors", below. + * 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.6.1 - March 28, 2013: 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: + * 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 @@ -103,88 +105,94 @@ * 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.0.17rc1 10 10017 12.so.0.1.0.17rc1 * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 - * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.0.17 10 10017 12.so.0.1.0.17 * 1.2.7 13 10207 12.so.0.1.2.7 * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 - * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.0.18rc1-5 10 10018 12.so.0.1.0.18rc1-5 * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 - * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.0.18 10 10018 12.so.0.1.0.18 * 1.2.8 13 10208 12.so.0.1.2.8 * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 * 1.2.9beta4-11 13 10209 12.so.0.9[.0] * 1.2.9rc1 13 10209 12.so.0.9[.0] * 1.2.9 13 10209 12.so.0.9[.0] - * 1.2.10beta1-8 13 10210 12.so.0.10[.0] - * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10beta1-7 13 10210 12.so.0.10[.0] + * 1.2.10rc1-2 13 10210 12.so.0.10[.0] * 1.2.10 13 10210 12.so.0.10[.0] + * 1.4.0beta1-5 14 10400 14.so.0.0[.0] * 1.2.11beta1-4 13 10211 12.so.0.11[.0] - * 1.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.4.0beta7-8 14 10400 14.so.0.0[.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.4.0beta9-14 14 10400 14.so.0.0[.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] + * 1.4.0beta15-36 14 10400 14.so.0.0[.0] + * 1.4.0beta37-87 14 10400 14.so.14.0[.0] + * 1.4.0rc01 14 10400 14.so.14.0[.0] + * 1.4.0beta88-109 14 10400 14.so.14.0[.0] + * 1.4.0rc02-08 14 10400 14.so.14.0[.0] + * 1.4.0 14 10400 14.so.14.0[.0] + * 1.4.1beta01-03 14 10401 14.so.14.1[.0] + * 1.4.1rc01 14 10401 14.so.14.1[.0] + * 1.4.1beta04-12 14 10401 14.so.14.1[.0] + * 1.4.1 14 10401 14.so.14.1[.0] + * 1.4.2 14 10402 14.so.14.2[.0] + * 1.4.3 14 10403 14.so.14.3[.0] + * 1.4.4 14 10404 14.so.14.4[.0] + * 1.5.0beta01-58 15 10500 15.so.15.0[.0] + * 1.5.0rc01-07 15 10500 15.so.15.0[.0] + * 1.5.0 15 10500 15.so.15.0[.0] + * 1.5.1beta01-11 15 10501 15.so.15.1[.0] + * 1.5.1rc01-02 15 10501 15.so.15.1[.0] + * 1.5.1 15 10501 15.so.15.1[.0] + * 1.5.2beta01-03 15 10502 15.so.15.2[.0] + * 1.5.2rc01-03 15 10502 15.so.15.2[.0] + * 1.5.2 15 10502 15.so.15.2[.0] + * 1.5.3beta01-10 15 10503 15.so.15.3[.0] + * 1.5.3rc01-02 15 10503 15.so.15.3[.0] + * 1.5.3beta11 15 10503 15.so.15.3[.0] + * 1.5.3 [omitted] + * 1.5.4beta01-08 15 10504 15.so.15.4[.0] + * 1.5.4rc01 15 10504 15.so.15.4[.0] + * 1.5.4 15 10504 15.so.15.4[.0] + * 1.5.5beta01-08 15 10505 15.so.15.5[.0] + * 1.5.5rc01 15 10505 15.so.15.5[.0] + * 1.5.5 15 10505 15.so.15.5[.0] + * 1.5.6beta01-07 15 10506 15.so.15.6[.0] + * 1.5.6rc01-03 15 10506 15.so.15.6[.0] + * 1.5.6 15 10506 15.so.15.6[.0] + * 1.5.7beta01-05 15 10507 15.so.15.7[.0] + * 1.5.7rc01-03 15 10507 15.so.15.7[.0] + * 1.5.7 15 10507 15.so.15.7[.0] + * 1.6.0beta01-40 16 10600 16.so.16.0[.0] + * 1.6.0rc01-08 16 10600 16.so.16.0[.0] + * 1.6.0 16 10600 16.so.16.0[.0] + * 1.6.1beta01-10 16 10601 16.so.16.1[.0] + * 1.6.1rc01 16 10601 16.so.16.1[.0] + * 1.6.1 16 10601 16.so.16.1[.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". + * 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 "rcNN". * - * 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. + * 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). + * 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, - * , and the application + * must include this before png.h to obtain the definition of jmp_buf. The + * function is required to be PNG_NORETURN, but this is not checked. If the + * function does return the application will crash via an abort() or similar + * system level call. + * + * If you get a warning here while building the library you may need to make + * changes to ensure that pnglibconf.h records the calling convention used by + * your compiler. This may be very difficult - try using a different compiler + * to build the library! + */ +PNG_FUNCTION(void, (PNGCAPI *png_longjmp_ptr), PNGARG((jmp_buf, int)), typedef); #endif /* Transform masks for the high-level interface */ @@ -1109,666 +933,663 @@ typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); #define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ #define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ #define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ -#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* write only */ +/* Added to libpng-1.2.34 */ +#define PNG_TRANSFORM_STRIP_FILLER_BEFORE PNG_TRANSFORM_STRIP_FILLER +#define PNG_TRANSFORM_STRIP_FILLER_AFTER 0x1000 /* write only */ +/* Added to libpng-1.4.0 */ +#define PNG_TRANSFORM_GRAY_TO_RGB 0x2000 /* read only */ +/* Added to libpng-1.5.4 */ +#define PNG_TRANSFORM_EXPAND_16 0x4000 /* read only */ +#define PNG_TRANSFORM_SCALE_16 0x8000 /* read only */ /* Flags for MNG supported features */ #define PNG_FLAG_MNG_EMPTY_PLTE 0x01 #define PNG_FLAG_MNG_FILTER_64 0x04 #define PNG_ALL_MNG_FEATURES 0x05 -typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_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. +/* NOTE: prior to 1.5 these functions had no 'API' style declaration, + * this allowed the zlib default functions to be used on Windows + * platforms. In 1.5 the zlib default malloc (which just calls malloc and + * ignores the first argument) should be completely compatible with the + * following. */ +typedef PNG_CALLBACK(png_voidp, *png_malloc_ptr, (png_structp, + png_alloc_size_t)); +typedef PNG_CALLBACK(void, *png_free_ptr, (png_structp, png_voidp)); -struct png_struct_def -{ -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf jmpbuf; /* used in png_error */ -#endif - png_error_ptr error_fn; /* function for printing errors and aborting */ - png_error_ptr warning_fn; /* function for printing warnings */ - png_voidp error_ptr; /* user supplied struct for error functions */ - png_rw_ptr write_data_fn; /* function for writing output data */ - png_rw_ptr read_data_fn; /* function for reading input data */ - png_voidp io_ptr; /* ptr to application struct for I/O functions */ - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) - png_user_transform_ptr read_user_transform_fn; /* user read transform */ -#endif - -#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) - png_user_transform_ptr write_user_transform_fn; /* user write transform */ -#endif - -/* These were added in libpng-1.0.2 */ -#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) - png_voidp user_transform_ptr; /* user supplied struct for user transform */ - png_byte user_transform_depth; /* bit depth of user transformed pixels */ - png_byte user_transform_channels; /* channels in user transformed pixels */ -#endif -#endif - - png_uint_32 mode; /* tells us where we are in the PNG file */ - png_uint_32 flags; /* flags indicating various things to libpng */ - png_uint_32 transformations; /* which transformations to perform */ - - z_stream zstream; /* pointer to decompression structure (below) */ - png_bytep zbuf; /* buffer for zlib */ - png_size_t zbuf_size; /* size of zbuf */ - int zlib_level; /* holds zlib compression level */ - int zlib_method; /* holds zlib compression method */ - int zlib_window_bits; /* holds zlib compression window bits */ - int zlib_mem_level; /* holds zlib compression memory level */ - int zlib_strategy; /* holds zlib compression strategy */ - - png_uint_32 width; /* width of image in pixels */ - png_uint_32 height; /* height of image in pixels */ - png_uint_32 num_rows; /* number of rows in current pass */ - png_uint_32 usr_width; /* width of row at start of write */ - png_uint_32 rowbytes; /* size of row in bytes */ - png_uint_32 irowbytes; /* size of current interlaced row in bytes */ - png_uint_32 iwidth; /* width of current interlaced row in pixels */ - png_uint_32 row_number; /* current row in interlace pass */ - png_bytep prev_row; /* buffer to save previous (unfiltered) row */ - png_bytep row_buf; /* buffer to save current (unfiltered) row */ - png_bytep sub_row; /* buffer to save "sub" row when filtering */ - png_bytep up_row; /* buffer to save "up" row when filtering */ - png_bytep avg_row; /* buffer to save "avg" row when filtering */ - png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ - png_row_info row_info; /* used for transformation routines */ - - png_uint_32 idat_size; /* current IDAT size for read */ - png_uint_32 crc; /* current chunk CRC value */ - png_colorp palette; /* palette from the input file */ - png_uint_16 num_palette; /* number of color entries in palette */ - png_uint_16 num_trans; /* number of transparency values */ - png_byte chunk_name[5]; /* null-terminated name of current chunk */ - png_byte compression; /* file compression type (always 0) */ - png_byte filter; /* file filter type (always 0) */ - png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ - png_byte pass; /* current interlace pass (0 - 6) */ - png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ - png_byte color_type; /* color type of file */ - png_byte bit_depth; /* bit depth of file */ - png_byte usr_bit_depth; /* bit depth of users row */ - png_byte pixel_depth; /* number of bits per pixel */ - png_byte channels; /* number of channels in file */ - png_byte usr_channels; /* channels at start of write */ - png_byte sig_bytes; /* magic bytes read/written from start of file */ - -#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) -#ifdef PNG_LEGACY_SUPPORTED - png_byte filler; /* filler byte for pixel expansion */ -#else - png_uint_16 filler; /* filler bytes for pixel expansion */ -#endif -#endif - -#if defined(PNG_bKGD_SUPPORTED) - png_byte background_gamma_type; -# ifdef PNG_FLOATING_POINT_SUPPORTED - float background_gamma; -# endif - png_color_16 background; /* background color in screen gamma space */ -#if defined(PNG_READ_GAMMA_SUPPORTED) - png_color_16 background_1; /* background normalized to gamma 1.0 */ -#endif -#endif /* PNG_bKGD_SUPPORTED */ - -#if defined(PNG_WRITE_FLUSH_SUPPORTED) - png_flush_ptr output_flush_fn;/* Function for flushing output */ - png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ - png_uint_32 flush_rows; /* number of rows written since last flush */ -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ -#ifdef PNG_FLOATING_POINT_SUPPORTED - float gamma; /* file gamma value */ - float screen_gamma; /* screen gamma value (display_exponent) */ -#endif -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - png_bytep gamma_table; /* gamma table for 8-bit depth files */ - png_bytep gamma_from_1; /* converts from 1.0 to screen */ - png_bytep gamma_to_1; /* converts from file to 1.0 */ - png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ - png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ - png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ -#endif - -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) - png_color_8 sig_bit; /* significant bits in each available channel */ -#endif - -#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) - png_color_8 shift; /* shift for significant bit tranformation */ -#endif - -#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ - || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - png_bytep trans; /* transparency values for paletted files */ - png_color_16 trans_values; /* transparency values for non-paletted files */ -#endif - - png_read_status_ptr read_row_fn; /* called after each row is decoded */ - png_write_status_ptr write_row_fn; /* called after each row is encoded */ -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED - png_progressive_info_ptr info_fn; /* called after header data fully read */ - png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ - png_progressive_end_ptr end_fn; /* called after image is complete */ - png_bytep save_buffer_ptr; /* current location in save_buffer */ - png_bytep save_buffer; /* buffer for previously read data */ - png_bytep current_buffer_ptr; /* current location in current_buffer */ - png_bytep current_buffer; /* buffer for recently used data */ - png_uint_32 push_length; /* size of current input chunk */ - png_uint_32 skip_length; /* bytes to skip in input data */ - png_size_t save_buffer_size; /* amount of data now in save_buffer */ - png_size_t save_buffer_max; /* total size of save_buffer */ - png_size_t buffer_size; /* total amount of available input data */ - png_size_t current_buffer_size; /* amount of data now in current_buffer */ - int process_mode; /* what push library is currently doing */ - int cur_palette; /* current push library palette index */ - -# if defined(PNG_TEXT_SUPPORTED) - png_size_t current_text_size; /* current size of text input data */ - png_size_t current_text_left; /* how much text left to read in input */ - png_charp current_text; /* current text chunk buffer */ - png_charp current_text_ptr; /* current location in current_text */ -# endif /* PNG_TEXT_SUPPORTED */ -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ - -#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) -/* for the Borland special 64K segment handler */ - png_bytepp offset_table_ptr; - png_bytep offset_table; - png_uint_16 offset_table_number; - png_uint_16 offset_table_count; - png_uint_16 offset_table_count_free; -#endif - -#if defined(PNG_READ_DITHER_SUPPORTED) - png_bytep palette_lookup; /* lookup table for dithering */ - png_bytep dither_index; /* index translation for palette files */ -#endif - -#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) - png_uint_16p hist; /* histogram */ -#endif - -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) - png_byte heuristic_method; /* heuristic for row filter selection */ - png_byte num_prev_filters; /* number of weights for previous rows */ - png_bytep prev_filters; /* filter type(s) of previous row(s) */ - png_uint_16p filter_weights; /* weight(s) for previous line(s) */ - png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ - png_uint_16p filter_costs; /* relative filter calculation cost */ - png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ -#endif - -#if defined(PNG_TIME_RFC1123_SUPPORTED) - png_charp time_buffer; /* String to hold RFC 1123 time text */ -#endif - -/* New members added in libpng-1.0.6 */ - -#ifdef PNG_FREE_ME_SUPPORTED - png_uint_32 free_me; /* flags items libpng is responsible for freeing */ -#endif - -#if defined(PNG_USER_CHUNKS_SUPPORTED) - png_voidp user_chunk_ptr; - png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ -#endif - -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) - int num_chunk_list; - png_bytep chunk_list; -#endif - -/* New members added in libpng-1.0.3 */ -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - png_byte rgb_to_gray_status; - /* These were changed from png_byte in libpng-1.0.6 */ - png_uint_16 rgb_to_gray_red_coeff; - png_uint_16 rgb_to_gray_green_coeff; - png_uint_16 rgb_to_gray_blue_coeff; -#endif - -/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ -#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ - defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ - defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) -/* changed from png_byte to png_uint_32 at version 1.2.0 */ -#ifdef PNG_1_0_X - png_byte mng_features_permitted; -#else - png_uint_32 mng_features_permitted; -#endif /* PNG_1_0_X */ -#endif - -/* New member added in libpng-1.0.7 */ -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - png_fixed_point int_gamma; -#endif - -/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ -#if defined(PNG_MNG_FEATURES_SUPPORTED) - png_byte filter_type; -#endif - -#if defined(PNG_1_0_X) -/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ - png_uint_32 row_buf_size; -#endif - -/* New members added in libpng-1.2.0 */ -#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) -# if !defined(PNG_1_0_X) -# if defined(PNG_MMX_CODE_SUPPORTED) - png_byte mmx_bitdepth_threshold; - png_uint_32 mmx_rowbytes_threshold; -# endif - png_uint_32 asm_flags; -# endif -#endif - -/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ -#ifdef PNG_USER_MEM_SUPPORTED - png_voidp mem_ptr; /* user supplied struct for mem functions */ - png_malloc_ptr malloc_fn; /* function for allocating memory */ - png_free_ptr free_fn; /* function for freeing memory */ -#endif - -/* New member added in libpng-1.0.13 and 1.2.0 */ - png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ - -#if defined(PNG_READ_DITHER_SUPPORTED) -/* The following three members were added at version 1.0.14 and 1.2.4 */ - png_bytep dither_sort; /* working sort array */ - png_bytep index_to_palette; /* where the original index currently is */ - /* in the palette */ - png_bytep palette_to_index; /* which original index points to this */ - /* palette color */ -#endif - -/* New members added in libpng-1.0.16 and 1.2.6 */ - png_byte compression_type; - -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - png_uint_32 user_width_max; - png_uint_32 user_height_max; -#endif - -/* New member added in libpng-1.0.25 and 1.2.17 */ -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) - /* storage for unknown chunk that the library doesn't recognize. */ - png_unknown_chunk unknown_chunk; -#endif -}; - - -/* This triggers a compiler error in png.c, if png.c and png.h - * do not agree upon the version number. - */ -typedef png_structp version_1_2_21; - -typedef png_struct FAR * FAR * png_structpp; - -/* Here are the function definitions most commonly used. This is not - * the place to find out how to use libpng. See libpng.txt for the +/* Section 3: exported functions + * Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng-manual.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. + * + * The PNG_EXPORT() and PNG_EXPORTA() macros used below are defined in + * pngconf.h and in the *.dfn files in the scripts directory. + * + * PNG_EXPORT(ordinal, type, name, (args)); + * + * ordinal: ordinal that is used while building + * *.def files. The ordinal value is only + * relevant when preprocessing png.h with + * the *.dfn files for building symbol table + * entries, and are removed by pngconf.h. + * type: return type of the function + * name: function name + * args: function arguments, with types + * + * When we wish to append attributes to a function prototype we use + * the PNG_EXPORTA() macro instead. + * + * PNG_EXPORTA(ordinal, type, name, (args), attributes); + * + * ordinal, type, name, and args: same as in PNG_EXPORT(). + * attributes: function attributes */ /* Returns the version number of the library */ -extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); +PNG_EXPORT(1, png_uint_32, png_access_version_number, (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)); +PNG_EXPORT(2, void, png_set_sig_bytes, (png_structrp 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)); +PNG_EXPORT(3, int, png_sig_cmp, (png_const_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)); +#define png_check_sig(sig, n) !png_sig_cmp((sig), 0, (n)) /* 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)); +PNG_EXPORTA(4, png_structp, png_create_read_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn), + PNG_ALLOCATED); /* Allocate and initialize png_ptr struct for writing, and any other memory */ -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)); +PNG_EXPORTA(5, png_structp, png_create_write_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn), + PNG_ALLOCATED); -#ifdef PNG_WRITE_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) - PNGARG((png_structp png_ptr)); -#endif - -#ifdef PNG_WRITE_SUPPORTED -extern PNG_EXPORT(void,png_set_compression_buffer_size) - PNGARG((png_structp png_ptr, png_uint_32 size)); +PNG_EXPORT(6, png_size_t, png_get_compression_buffer_size, + (png_const_structrp png_ptr)); + +PNG_EXPORT(7, void, png_set_compression_buffer_size, (png_structrp png_ptr, + png_size_t size)); + +/* Moved from pngconf.h in 1.4.0 and modified to ensure setjmp/longjmp + * match up. + */ +#ifdef PNG_SETJMP_SUPPORTED +/* This function returns the jmp_buf built in to *png_ptr. It must be + * supplied with an appropriate 'longjmp' function to use on that jmp_buf + * unless the default error function is overridden in which case NULL is + * acceptable. The size of the jmp_buf is checked against the actual size + * allocated by the library - the call will return NULL on a mismatch + * indicating an ABI mismatch. + */ +PNG_EXPORT(8, jmp_buf*, png_set_longjmp_fn, (png_structrp png_ptr, + png_longjmp_ptr longjmp_fn, size_t jmp_buf_size)); +# define png_jmpbuf(png_ptr) \ + (*png_set_longjmp_fn((png_ptr), longjmp, (sizeof (jmp_buf)))) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP) #endif +/* This function should be used by libpng applications in place of + * longjmp(png_ptr->jmpbuf, val). If longjmp_fn() has been set, it + * will use it; otherwise it will call PNG_ABORT(). This function was + * added in libpng-1.5.0. + */ +PNG_EXPORTA(9, void, png_longjmp, (png_const_structrp png_ptr, int val), + PNG_NORETURN); +#ifdef PNG_READ_SUPPORTED /* Reset the compression stream */ -extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); +PNG_EXPORTA(10, int, png_reset_zstream, (png_structrp png_ptr), PNG_DEPRECATED); +#endif /* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ #ifdef PNG_USER_MEM_SUPPORTED -extern PNG_EXPORT(png_structp,png_create_read_struct_2) - PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, - png_malloc_ptr malloc_fn, png_free_ptr free_fn)); -extern PNG_EXPORT(png_structp,png_create_write_struct_2) - PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, - png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +PNG_EXPORTA(11, png_structp, png_create_read_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); +PNG_EXPORTA(12, png_structp, png_create_write_struct_2, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn), + PNG_ALLOCATED); #endif +/* Write the PNG file signature. */ +PNG_EXPORT(13, void, png_write_sig, (png_structrp png_ptr)); + /* 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)); +PNG_EXPORT(14, void, png_write_chunk, (png_structrp png_ptr, png_const_bytep + chunk_name, png_const_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)); +PNG_EXPORT(15, void, png_write_chunk_start, (png_structrp png_ptr, + png_const_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)); +PNG_EXPORT(16, void, png_write_chunk_data, (png_structrp png_ptr, + png_const_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)); +PNG_EXPORT(17, void, png_write_chunk_end, (png_structrp png_ptr)); /* Allocate and initialize the info structure */ -extern PNG_EXPORT(png_infop,png_create_info_struct) - PNGARG((png_structp png_ptr)); +PNG_EXPORTA(18, png_infop, png_create_info_struct, (png_const_structrp png_ptr), + PNG_ALLOCATED); -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Initialize the info structure (old interface - DEPRECATED) */ -extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); -#undef png_info_init -#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ - png_sizeof(png_info)); -#endif - -extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, - png_size_t png_info_struct_size)); +/* DEPRECATED: this function allowed init structures to be created using the + * default allocation method (typically malloc). Use is deprecated in 1.6.0 and + * the API will be removed in the future. + */ +PNG_EXPORTA(19, void, png_info_init_3, (png_infopp info_ptr, + png_size_t png_info_struct_size), PNG_DEPRECATED); /* 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)); +PNG_EXPORT(20, void, png_write_info_before_PLTE, + (png_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(21, void, png_write_info, + (png_structrp png_ptr, png_const_inforp 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)); +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. */ +PNG_EXPORT(22, void, png_read_info, + (png_structrp png_ptr, png_inforp info_ptr)); #endif -#if defined(PNG_TIME_RFC1123_SUPPORTED) -extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) - PNGARG((png_structp png_ptr, png_timep ptime)); +#ifdef PNG_TIME_RFC1123_SUPPORTED + /* Convert to a US string format: there is no localization support in this + * routine. The original implementation used a 29 character buffer in + * png_struct, this will be removed in future versions. + */ +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng17 (and from libpng17/png.c and pngstruct.h) */ +PNG_EXPORTA(23, png_const_charp, png_convert_to_rfc1123, (png_structrp png_ptr, + png_const_timep ptime),PNG_DEPRECATED); +#endif +PNG_EXPORT(241, int, png_convert_to_rfc1123_buffer, (char out[29], + png_const_timep ptime)); #endif -#if !defined(_WIN32_WCE) -/* "time.h" functions are not supported on WindowsCE */ -#if defined(PNG_WRITE_tIME_SUPPORTED) -/* convert from a struct tm to png_time */ -extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, - struct tm FAR * ttime)); +#ifdef PNG_CONVERT_tIME_SUPPORTED +/* Convert from a struct tm to png_time */ +PNG_EXPORT(24, void, png_convert_from_struct_tm, (png_timep ptime, + const struct tm * 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 */ +/* Convert from time_t to png_time. Uses gmtime() */ +PNG_EXPORT(25, void, png_convert_from_time_t, (png_timep ptime, time_t ttime)); +#endif /* PNG_CONVERT_tIME_SUPPORTED */ -#if defined(PNG_READ_EXPAND_SUPPORTED) +#ifdef PNG_READ_EXPAND_SUPPORTED /* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ -extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); -#if !defined(PNG_1_0_X) -extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp - png_ptr)); -#endif -extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Deprecated */ -extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +PNG_EXPORT(26, void, png_set_expand, (png_structrp png_ptr)); +PNG_EXPORT(27, void, png_set_expand_gray_1_2_4_to_8, (png_structrp png_ptr)); +PNG_EXPORT(28, void, png_set_palette_to_rgb, (png_structrp png_ptr)); +PNG_EXPORT(29, void, png_set_tRNS_to_alpha, (png_structrp png_ptr)); #endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, forces conversion of palette to RGB and expansion + * of a tRNS chunk if present. + */ +PNG_EXPORT(221, void, png_set_expand_16, (png_structrp png_ptr)); #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)); +PNG_EXPORT(30, void, png_set_bgr, (png_structrp png_ptr)); #endif -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +#ifdef 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)); +PNG_EXPORT(31, void, png_set_gray_to_rgb, (png_structrp png_ptr)); #endif -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED /* Reduce RGB to grayscale. */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, - int error_action, double red, double green )); -#endif -extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, - int error_action, png_fixed_point red, png_fixed_point green )); -extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp - png_ptr)); +#define PNG_ERROR_ACTION_NONE 1 +#define PNG_ERROR_ACTION_WARN 2 +#define PNG_ERROR_ACTION_ERROR 3 +#define PNG_RGB_TO_GRAY_DEFAULT (-1)/*for red/green coefficients*/ + +PNG_FP_EXPORT(32, void, png_set_rgb_to_gray, (png_structrp png_ptr, + int error_action, double red, double green)) +PNG_FIXED_EXPORT(33, void, png_set_rgb_to_gray_fixed, (png_structrp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green)) + +PNG_EXPORT(34, png_byte, png_get_rgb_to_gray_status, (png_const_structrp + png_ptr)); #endif -extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, - png_colorp palette)); +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED +PNG_EXPORT(35, void, png_build_grayscale_palette, (int bit_depth, + png_colorp palette)); +#endif -#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* How the alpha channel is interpreted - this affects how the color channels of + * a PNG file are returned when an alpha channel, or tRNS chunk in a palette + * file, is present. + * + * This has no effect on the way pixels are written into a PNG output + * datastream. The color samples in a PNG datastream are never premultiplied + * with the alpha samples. + * + * The default is to return data according to the PNG specification: the alpha + * channel is a linear measure of the contribution of the pixel to the + * corresponding composited pixel. The gamma encoded color channels must be + * scaled according to the contribution and to do this it is necessary to undo + * the encoding, scale the color values, perform the composition and reencode + * the values. This is the 'PNG' mode. + * + * The alternative is to 'associate' the alpha with the color information by + * storing color channel values that have been scaled by the alpha. The + * advantage is that the color channels can be resampled (the image can be + * scaled) in this form. The disadvantage is that normal practice is to store + * linear, not (gamma) encoded, values and this requires 16-bit channels for + * still images rather than the 8-bit channels that are just about sufficient if + * gamma encoding is used. In addition all non-transparent pixel values, + * including completely opaque ones, must be gamma encoded to produce the final + * image. This is the 'STANDARD', 'ASSOCIATED' or 'PREMULTIPLIED' mode (the + * latter being the two common names for associated alpha color channels.) + * + * Since it is not necessary to perform arithmetic on opaque color values so + * long as they are not to be resampled and are in the final color space it is + * possible to optimize the handling of alpha by storing the opaque pixels in + * the PNG format (adjusted for the output color space) while storing partially + * opaque pixels in the standard, linear, format. The accuracy required for + * standard alpha composition is relatively low, because the pixels are + * isolated, therefore typically the accuracy loss in storing 8-bit linear + * values is acceptable. (This is not true if the alpha channel is used to + * simulate transparency over large areas - use 16 bits or the PNG mode in + * this case!) This is the 'OPTIMIZED' mode. For this mode a pixel is + * treated as opaque only if the alpha value is equal to the maximum value. + * + * The final choice is to gamma encode the alpha channel as well. This is + * broken because, in practice, no implementation that uses this choice + * correctly undoes the encoding before handling alpha composition. Use this + * choice only if other serious errors in the software or hardware you use + * mandate it; the typical serious error is for dark halos to appear around + * opaque areas of the composited PNG image because of arithmetic overflow. + * + * The API function png_set_alpha_mode specifies which of these choices to use + * with an enumerated 'mode' value and the gamma of the required output: + */ +#define PNG_ALPHA_PNG 0 /* according to the PNG standard */ +#define PNG_ALPHA_STANDARD 1 /* according to Porter/Duff */ +#define PNG_ALPHA_ASSOCIATED 1 /* as above; this is the normal practice */ +#define PNG_ALPHA_PREMULTIPLIED 1 /* as above */ +#define PNG_ALPHA_OPTIMIZED 2 /* 'PNG' for opaque pixels, else 'STANDARD' */ +#define PNG_ALPHA_BROKEN 3 /* the alpha channel is gamma encoded */ + +PNG_FP_EXPORT(227, void, png_set_alpha_mode, (png_structrp png_ptr, int mode, + double output_gamma)) +PNG_FIXED_EXPORT(228, void, png_set_alpha_mode_fixed, (png_structrp png_ptr, + int mode, png_fixed_point output_gamma)) +#endif + +#if defined(PNG_GAMMA_SUPPORTED) || defined(PNG_READ_ALPHA_MODE_SUPPORTED) +/* The output_gamma value is a screen gamma in libpng terminology: it expresses + * how to decode the output values, not how they are encoded. The values used + * correspond to the normal numbers used to describe the overall gamma of a + * computer display system; for example 2.2 for an sRGB conformant system. The + * values are scaled by 100000 in the _fixed version of the API (so 220000 for + * sRGB.) + * + * The inverse of the value is always used to provide a default for the PNG file + * encoding if it has no gAMA chunk and if png_set_gamma() has not been called + * to override the PNG gamma information. + * + * When the ALPHA_OPTIMIZED mode is selected the output gamma is used to encode + * opaque pixels however pixels with lower alpha values are not encoded, + * regardless of the output gamma setting. + * + * When the standard Porter Duff handling is requested with mode 1 the output + * encoding is set to be linear and the output_gamma value is only relevant + * as a default for input data that has no gamma information. The linear output + * encoding will be overridden if png_set_gamma() is called - the results may be + * highly unexpected! + * + * The following numbers are derived from the sRGB standard and the research + * behind it. sRGB is defined to be approximated by a PNG gAMA chunk value of + * 0.45455 (1/2.2) for PNG. The value implicitly includes any viewing + * correction required to take account of any differences in the color + * environment of the original scene and the intended display environment; the + * value expresses how to *decode* the image for display, not how the original + * data was *encoded*. + * + * sRGB provides a peg for the PNG standard by defining a viewing environment. + * sRGB itself, and earlier TV standards, actually use a more complex transform + * (a linear portion then a gamma 2.4 power law) than PNG can express. (PNG is + * limited to simple power laws.) By saying that an image for direct display on + * an sRGB conformant system should be stored with a gAMA chunk value of 45455 + * (11.3.3.2 and 11.3.3.5 of the ISO PNG specification) the PNG specification + * makes it possible to derive values for other display systems and + * environments. + * + * The Mac value is deduced from the sRGB based on an assumption that the actual + * extra viewing correction used in early Mac display systems was implemented as + * a power 1.45 lookup table. + * + * Any system where a programmable lookup table is used or where the behavior of + * the final display device characteristics can be changed requires system + * specific code to obtain the current characteristic. However this can be + * difficult and most PNG gamma correction only requires an approximate value. + * + * By default, if png_set_alpha_mode() is not called, libpng assumes that all + * values are unencoded, linear, values and that the output device also has a + * linear characteristic. This is only very rarely correct - it is invariably + * better to call png_set_alpha_mode() with PNG_DEFAULT_sRGB than rely on the + * default if you don't know what the right answer is! + * + * The special value PNG_GAMMA_MAC_18 indicates an older Mac system (pre Mac OS + * 10.6) which used a correction table to implement a somewhat lower gamma on an + * otherwise sRGB system. + * + * Both these values are reserved (not simple gamma values) in order to allow + * more precise correction internally in the future. + * + * NOTE: the following values can be passed to either the fixed or floating + * point APIs, but the floating point API will also accept floating point + * values. + */ +#define PNG_DEFAULT_sRGB -1 /* sRGB gamma and color space */ +#define PNG_GAMMA_MAC_18 -2 /* Old Mac '1.8' gamma and color space */ +#define PNG_GAMMA_sRGB 220000 /* Television standards--matches sRGB gamma */ +#define PNG_GAMMA_LINEAR PNG_FP_1 /* Linear */ +#endif + +/* The following are examples of calls to png_set_alpha_mode to achieve the + * required overall gamma correction and, where necessary, alpha + * premultiplication. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * This is the default libpng handling of the alpha channel - it is not + * pre-multiplied into the color components. In addition the call states + * that the output is for a sRGB system and causes all PNG files without gAMA + * chunks to be assumed to be encoded using sRGB. + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * In this case the output is assumed to be something like an sRGB conformant + * display preceeded by a power-law lookup table of power 1.45. This is how + * early Mac systems behaved. + * + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_GAMMA_LINEAR); + * This is the classic Jim Blinn approach and will work in academic + * environments where everything is done by the book. It has the shortcoming + * of assuming that input PNG data with no gamma information is linear - this + * is unlikely to be correct unless the PNG files where generated locally. + * Most of the time the output precision will be so low as to show + * significant banding in dark areas of the image. + * + * png_set_expand_16(pp); + * png_set_alpha_mode(pp, PNG_ALPHA_STANDARD, PNG_DEFAULT_sRGB); + * This is a somewhat more realistic Jim Blinn inspired approach. PNG files + * are assumed to have the sRGB encoding if not marked with a gamma value and + * the output is always 16 bits per component. This permits accurate scaling + * and processing of the data. If you know that your input PNG files were + * generated locally you might need to replace PNG_DEFAULT_sRGB with the + * correct value for your system. + * + * png_set_alpha_mode(pp, PNG_ALPHA_OPTIMIZED, PNG_DEFAULT_sRGB); + * If you just need to composite the PNG image onto an existing background + * and if you control the code that does this you can use the optimization + * setting. In this case you just copy completely opaque pixels to the + * output. For pixels that are not completely transparent (you just skip + * those) you do the composition math using png_composite or png_composite_16 + * below then encode the resultant 8-bit or 16-bit values to match the output + * encoding. + * + * Other cases + * If neither the PNG nor the standard linear encoding work for you because + * of the software or hardware you use then you have a big problem. The PNG + * case will probably result in halos around the image. The linear encoding + * will probably result in a washed out, too bright, image (it's actually too + * contrasty.) Try the ALPHA_OPTIMIZED mode above - this will probably + * substantially reduce the halos. Alternatively try: + * + * png_set_alpha_mode(pp, PNG_ALPHA_BROKEN, PNG_DEFAULT_sRGB); + * This option will also reduce the halos, but there will be slight dark + * halos round the opaque parts of the image where the background is light. + * In the OPTIMIZED mode the halos will be light halos where the background + * is dark. Take your pick - the halos are unavoidable unless you can get + * your hardware/software fixed! (The OPTIMIZED approach is slightly + * faster.) + * + * When the default gamma of PNG files doesn't match the output gamma. + * If you have PNG files with no gamma information png_set_alpha_mode allows + * you to provide a default gamma, but it also sets the ouput gamma to the + * matching value. If you know your PNG files have a gamma that doesn't + * match the output you can take advantage of the fact that + * png_set_alpha_mode always sets the output gamma but only sets the PNG + * default if it is not already set: + * + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB); + * png_set_alpha_mode(pp, PNG_ALPHA_PNG, PNG_GAMMA_MAC); + * The first call sets both the default and the output gamma values, the + * second call overrides the output gamma without changing the default. This + * is easier than achieving the same effect with png_set_gamma. You must use + * PNG_ALPHA_PNG for the first call - internal checking in png_set_alpha will + * fire if more than one call to png_set_alpha_mode and png_set_background is + * made in the same read operation, however multiple calls with PNG_ALPHA_PNG + * are ignored. + */ + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED +PNG_EXPORT(36, void, png_set_strip_alpha, (png_structrp png_ptr)); #endif #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) -extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +PNG_EXPORT(37, void, png_set_swap_alpha, (png_structrp png_ptr)); #endif #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) -extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +PNG_EXPORT(38, void, png_set_invert_alpha, (png_structrp 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)); +PNG_EXPORT(39, void, png_set_filler, (png_structrp 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 +# define PNG_FILLER_BEFORE 0 +# define PNG_FILLER_AFTER 1 /* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ -#if !defined(PNG_1_0_X) -extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, - png_uint_32 filler, int flags)); -#endif +PNG_EXPORT(40, void, png_set_add_alpha, (png_structrp png_ptr, + png_uint_32 filler, int flags)); #endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) /* Swap bytes in 16-bit depth files. */ -extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +PNG_EXPORT(41, void, png_set_swap, (png_structrp 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)); +PNG_EXPORT(42, void, png_set_packing, (png_structrp png_ptr)); #endif -#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +#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)); +PNG_EXPORT(43, void, png_set_packswap, (png_structrp 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)); +PNG_EXPORT(44, void, png_set_shift, (png_structrp png_ptr, png_const_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)); +/* Have the code handle the interlacing. Returns the number of passes. + * MUST be called before png_read_update_info or png_start_read_image, + * otherwise it will not have the desired effect. Note that it is still + * necessary to call png_read_row or png_read_rows png_get_image_height + * times for each pass. +*/ +PNG_EXPORT(45, int, png_set_interlace_handling, (png_structrp 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)); +PNG_EXPORT(46, void, png_set_invert_mono, (png_structrp png_ptr)); #endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) -/* Handle alpha and tRNS by replacing with a background color. */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, - png_color_16p background_color, int background_gamma_code, - int need_expand, double background_gamma)); +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS by replacing with a background color. Prior to + * libpng-1.5.4 this API must not be called before the PNG file header has been + * read. Doing so will result in unexpected behavior and possible warnings or + * errors if the PNG file contains a bKGD chunk. + */ +PNG_FP_EXPORT(47, void, png_set_background, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)) +PNG_FIXED_EXPORT(215, void, png_set_background_fixed, (png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma)) #endif -#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 -#define PNG_BACKGROUND_GAMMA_SCREEN 1 -#define PNG_BACKGROUND_GAMMA_FILE 2 -#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#ifdef PNG_READ_BACKGROUND_SUPPORTED +# define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +# define PNG_BACKGROUND_GAMMA_SCREEN 1 +# define PNG_BACKGROUND_GAMMA_FILE 2 +# define PNG_BACKGROUND_GAMMA_UNIQUE 3 #endif -#if defined(PNG_READ_16_TO_8_SUPPORTED) -/* strip the second byte of information from a 16-bit depth file. */ -extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale a 16-bit depth file down to 8-bit, accurately. */ +PNG_EXPORT(229, void, png_set_scale_16, (png_structrp 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)); +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +#define PNG_READ_16_TO_8 SUPPORTED /* Name prior to 1.5.4 */ +/* Strip the second byte of information from a 16-bit depth file. */ +PNG_EXPORT(48, void, png_set_strip_16, (png_structrp png_ptr)); #endif -#if defined(PNG_READ_GAMMA_SUPPORTED) -/* Handle gamma correction. Screen_gamma=(display_exponent) */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, - double screen_gamma, double default_file_gamma)); -#endif +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* Turn on quantizing, and reduce the palette to the number of colors + * available. + */ +PNG_EXPORT(49, void, png_set_quantize, (png_structrp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_const_uint_16p histogram, int full_quantize)); #endif -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ - defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) -/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ -/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ -extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, - int empty_plte_permitted)); -#endif +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The threshold on gamma processing is configurable but hard-wired into the + * library. The following is the floating point variant. + */ +#define PNG_GAMMA_THRESHOLD (PNG_GAMMA_THRESHOLD_FIXED*.00001) + +/* Handle gamma correction. Screen_gamma=(display_exponent). + * NOTE: this API simply sets the screen and file gamma values. It will + * therefore override the value for gamma in a PNG file if it is called after + * the file header has been read - use with care - call before reading the PNG + * file for best results! + * + * These routines accept the same gamma values as png_set_alpha_mode (described + * above). The PNG_GAMMA_ defines and PNG_DEFAULT_sRGB can be passed to either + * API (floating point or fixed.) Notice, however, that the 'file_gamma' value + * is the inverse of a 'screen gamma' value. + */ +PNG_FP_EXPORT(50, void, png_set_gamma, (png_structrp png_ptr, + double screen_gamma, double override_file_gamma)) +PNG_FIXED_EXPORT(208, void, png_set_gamma_fixed, (png_structrp png_ptr, + png_fixed_point screen_gamma, png_fixed_point override_file_gamma)) #endif -#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#ifdef 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)); +PNG_EXPORT(51, void, png_set_flush, (png_structrp png_ptr, int nrows)); /* Flush the current PNG output buffer */ -extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +PNG_EXPORT(52, void, png_write_flush, (png_structrp png_ptr)); #endif -/* optional update palette with requested transformations */ -extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); +/* Optional update palette with requested transformations */ +PNG_EXPORT(53, void, png_start_read_image, (png_structrp 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)); +/* Optional call to update the users info structure */ +PNG_EXPORT(54, void, png_read_update_info, (png_structrp png_ptr, + png_inforp 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)); +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. */ +PNG_EXPORT(55, void, png_read_rows, (png_structrp 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)); +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read a row of data. */ +PNG_EXPORT(56, void, png_read_row, (png_structrp 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)); +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the whole image into memory at once. */ +PNG_EXPORT(57, void, png_read_image, (png_structrp 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 row of image data */ +PNG_EXPORT(58, void, png_write_row, (png_structrp png_ptr, + png_const_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 a few rows of image data: (*row) is not written; however, the type + * is declared as writeable to maintain compatibility with previous versions + * of libpng and to allow the 'display_row' array from read_rows to be passed + * unchanged to write_rows. + */ +PNG_EXPORT(59, void, png_write_rows, (png_structrp 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)); +/* Write the image data */ +PNG_EXPORT(60, void, png_write_image, (png_structrp 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)); +/* Write the end of the PNG file. */ +PNG_EXPORT(61, void, png_write_end, (png_structrp png_ptr, + png_inforp 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)); +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. */ +PNG_EXPORT(62, void, png_read_end, (png_structrp png_ptr, png_inforp 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_info_struct */ +PNG_EXPORT(63, void, png_destroy_info_struct, (png_const_structrp 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 any memory associated with the png_struct and the png_info_structs */ +PNG_EXPORT(64, void, png_destroy_read_struct, (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 */ +PNG_EXPORT(65, void, png_destroy_write_struct, (png_structpp png_ptr_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_write_struct) - PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); +/* Set the libpng method of handling chunk CRC errors */ +PNG_EXPORT(66, void, png_set_crc_action, (png_structrp png_ptr, int crit_action, + int ancil_action)); -/* 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 +/* Values for png_set_crc_action() 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, @@ -1792,11 +1613,11 @@ extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, * header file (zlib.h) for an explination of the compression functions. */ -/* set the filtering method(s) used by libpng. Currently, the only valid +/* 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)); +PNG_EXPORT(67, void, png_set_filter, (png_structrp 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 @@ -1822,7 +1643,7 @@ extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, #define PNG_FILTER_VALUE_PAETH 4 #define PNG_FILTER_VALUE_LAST 5 -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* EXPERIMENTAL */ /* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ * defines, either the default (minimum-sum-of-absolute-differences), or * the experimental method (weighted-minimum-sum-of-absolute-differences). @@ -1851,11 +1672,13 @@ extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, * the weights and costs are set to 1.0, this degenerates the WEIGHTED method * to the UNWEIGHTED method, but with added encoding time/computation. */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, - int heuristic_method, int num_weights, png_doublep filter_weights, - png_doublep filter_costs)); -#endif +PNG_FP_EXPORT(68, void, png_set_filter_heuristics, (png_structrp png_ptr, + int heuristic_method, int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs)) +PNG_FIXED_EXPORT(209, void, png_set_filter_heuristics_fixed, + (png_structrp png_ptr, int heuristic_method, int num_weights, + png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs)) #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ /* Heuristic used for row filter selection. These defines should NOT be @@ -1866,6 +1689,7 @@ extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, #define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ #define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ +#ifdef PNG_WRITE_SUPPORTED /* 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 @@ -1873,33 +1697,58 @@ extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, * 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)); +PNG_EXPORT(69, void, png_set_compression_level, (png_structrp png_ptr, + int level)); -extern PNG_EXPORT(void,png_set_compression_mem_level) - PNGARG((png_structp png_ptr, int mem_level)); +PNG_EXPORT(70, void, png_set_compression_mem_level, (png_structrp png_ptr, + int mem_level)); -extern PNG_EXPORT(void,png_set_compression_strategy) - PNGARG((png_structp png_ptr, int strategy)); +PNG_EXPORT(71, void, png_set_compression_strategy, (png_structrp png_ptr, + int strategy)); -extern PNG_EXPORT(void,png_set_compression_window_bits) - PNGARG((png_structp png_ptr, int window_bits)); +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(72, void, png_set_compression_window_bits, (png_structrp png_ptr, + int window_bits)); -extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, - int method)); +PNG_EXPORT(73, void, png_set_compression_method, (png_structrp png_ptr, + int method)); +#endif + +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED +/* Also set zlib parameters for compressing non-IDAT chunks */ +PNG_EXPORT(222, void, png_set_text_compression_level, (png_structrp png_ptr, + int level)); + +PNG_EXPORT(223, void, png_set_text_compression_mem_level, (png_structrp png_ptr, + int mem_level)); + +PNG_EXPORT(224, void, png_set_text_compression_strategy, (png_structrp png_ptr, + int strategy)); + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +PNG_EXPORT(225, void, png_set_text_compression_window_bits, + (png_structrp png_ptr, int window_bits)); + +PNG_EXPORT(226, void, png_set_text_compression_method, (png_structrp png_ptr, + int method)); +#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ /* 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 + * different manner by calling png_set_???_fn(). See libpng-manual.txt for * more information. */ -#if !defined(PNG_NO_STDIO) +#ifdef PNG_STDIO_SUPPORTED /* 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)); +PNG_EXPORT(74, void, png_init_io, (png_structrp png_ptr, png_FILE_p fp)); #endif /* Replace the (error and abort), and warning functions with user @@ -1910,128 +1759,181 @@ extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)) * 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)); +PNG_EXPORT(75, void, png_set_error_fn, (png_structrp 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)); +PNG_EXPORT(76, png_voidp, png_get_error_ptr, (png_const_structrp png_ptr)); /* Replace the default data output functions with a user supplied one(s). * If buffered output is not used, then output_flush_fn can be set to NULL. * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time * output_flush_fn will be ignored (and thus can be NULL). + * It is probably a mistake to use NULL for output_flush_fn if + * write_data_fn is not also NULL unless you have built libpng with + * PNG_WRITE_FLUSH_SUPPORTED undefined, because in this case libpng's + * default flush function, which uses the standard *FILE structure, will + * be used. */ -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)); +PNG_EXPORT(77, void, png_set_write_fn, (png_structrp 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)); +PNG_EXPORT(78, void, png_set_read_fn, (png_structrp 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)); +PNG_EXPORT(79, png_voidp, png_get_io_ptr, (png_const_structrp png_ptr)); -extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, - png_read_status_ptr read_row_fn)); +PNG_EXPORT(80, void, png_set_read_status_fn, (png_structrp png_ptr, + png_read_status_ptr read_row_fn)); -extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, - png_write_status_ptr write_row_fn)); +PNG_EXPORT(81, void, png_set_write_status_fn, (png_structrp 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)); +PNG_EXPORT(82, void, png_set_mem_fn, (png_structrp 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)); +PNG_EXPORT(83, png_voidp, png_get_mem_ptr, (png_const_structrp png_ptr)); #endif -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) -extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp - png_ptr, png_user_transform_ptr read_user_transform_fn)); +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(84, void, png_set_read_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr read_user_transform_fn)); #endif -#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) -extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp - png_ptr, png_user_transform_ptr write_user_transform_fn)); +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED +PNG_EXPORT(85, void, png_set_write_user_transform_fn, (png_structrp png_ptr, + png_user_transform_ptr write_user_transform_fn)); #endif -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) -extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp - png_ptr, png_voidp user_transform_ptr, int user_transform_depth, - int user_transform_channels)); +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +PNG_EXPORT(86, void, png_set_user_transform_info, (png_structrp 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)); +PNG_EXPORT(87, png_voidp, png_get_user_transform_ptr, + (png_const_structrp png_ptr)); +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +/* Return information about the row currently being processed. Note that these + * APIs do not fail but will return unexpected results if called outside a user + * transform callback. Also note that when transforming an interlaced image the + * row number is the row number within the sub-image of the interlace pass, so + * the value will increase to the height of the sub-image (not the full image) + * then reset to 0 for the next pass. + * + * Use PNG_ROW_FROM_PASS_ROW(row, pass) and PNG_COL_FROM_PASS_COL(col, pass) to + * find the output pixel (x,y) given an interlaced sub-image pixel + * (row,col,pass). (See below for these macros.) + */ +PNG_EXPORT(217, png_uint_32, png_get_current_row_number, (png_const_structrp)); +PNG_EXPORT(218, png_byte, png_get_current_pass_number, (png_const_structrp)); +#endif + +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED +/* This callback is called only for *unknown* chunks. If + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED is set then it is possible to set known + * chunks to be treated as unknown, however in this case the callback must do + * any processing required by the chunk (e.g. by calling the appropriate + * png_set_ APIs.) + * + * There is no write support - on write, by default, all the chunks in the + * 'unknown' list are written in the specified position. + * + * The integer return from the callback function is interpreted thus: + * + * negative: An error occured, png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be saved. A critical + * chunk will cause an error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + * + * See "INTERACTION WTIH USER CHUNK CALLBACKS" below for important notes about + * how this behavior will change in libpng 1.7 + */ +PNG_EXPORT(88, void, png_set_read_user_chunk_fn, (png_structrp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); #endif #ifdef PNG_USER_CHUNKS_SUPPORTED -extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, - png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); -extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp - png_ptr)); +PNG_EXPORT(89, png_voidp, png_get_user_chunk_ptr, (png_const_structrp png_ptr)); #endif #ifdef PNG_PROGRESSIVE_READ_SUPPORTED /* Sets the function callbacks for the push reader, and a pointer to a * user-defined structure available to the callback functions. */ -extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, - png_voidp progressive_ptr, - png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, - png_progressive_end_ptr end_fn)); +PNG_EXPORT(90, void, png_set_progressive_read_fn, (png_structrp 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)); +/* Returns the user pointer associated with the push read functions */ +PNG_EXPORT(91, png_voidp, png_get_progressive_ptr, + (png_const_structrp 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 to be called when data becomes available */ +PNG_EXPORT(92, void, png_process_data, (png_structrp png_ptr, + png_inforp 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????? +/* A function which may be called *only* within png_process_data to stop the + * processing of any more data. The function returns the number of bytes + * remaining, excluding any that libpng has cached internally. A subsequent + * call to png_process_data must supply these bytes again. If the argument + * 'save' is set to true the routine will first save all the pending data and + * will always return 0. */ -extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, - png_bytep old_row, png_bytep new_row)); +PNG_EXPORT(219, png_size_t, png_process_data_pause, (png_structrp, int save)); + +/* A function which may be called *only* outside (after) a call to + * png_process_data. It returns the number of bytes of data to skip in the + * input. Normally it will return 0, but if it returns a non-zero value the + * application must skip than number of bytes of input data and pass the + * following data to the next call to png_process_data. + */ +PNG_EXPORT(220, png_uint_32, png_process_data_skip, (png_structrp)); + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Function that combines rows. 'new_row' is a flag that should come from + * the callback and be non-NULL if anything needs to be done; the library + * stores its own version of the new data internally and ignores the passed + * in value. + */ +PNG_EXPORT(93, void, png_progressive_combine_row, (png_const_structrp png_ptr, + png_bytep old_row, png_const_bytep new_row)); +#endif /* PNG_READ_INTERLACING_SUPPORTED */ #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ -extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, - png_uint_32 size)); +PNG_EXPORTA(94, png_voidp, png_malloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); +/* Added at libpng version 1.4.0 */ +PNG_EXPORTA(95, png_voidp, png_calloc, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); -#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 +PNG_EXPORTA(96, png_voidp, png_malloc_warn, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED); -/* 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 +/* Frees a pointer allocated by png_malloc() */ +PNG_EXPORT(97, void, png_free, (png_const_structrp png_ptr, png_voidp ptr)); /* 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 +PNG_EXPORT(98, void, png_free_data, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 free_me, int num)); + /* 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 */ + * by libpng or by the application; this works on the png_info structure passed + * in, it does not change the state for other png_info structures. + * + * It is unlikely that this function works correctly as of 1.6.0 and using it + * may result either in memory leaks or double free of allocated data. + */ +PNG_EXPORTA(99, void, png_data_freer, (png_const_structrp png_ptr, + png_inforp info_ptr, int freer, png_uint_32 mask), PNG_DEPRECATED); + +/* 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 @@ -2042,8 +1944,10 @@ extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, #define PNG_FREE_ROWS 0x0040 #define PNG_FREE_PCAL 0x0080 #define PNG_FREE_SCAL 0x0100 -#define PNG_FREE_UNKN 0x0200 -#define PNG_FREE_LIST 0x0400 +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_FREE_UNKN 0x0200 +#endif +/* PNG_FREE_LIST 0x0400 removed in 1.6.0 because it is ignored */ #define PNG_FREE_PLTE 0x1000 #define PNG_FREE_TRNS 0x2000 #define PNG_FREE_TEXT 0x4000 @@ -2051,47 +1955,59 @@ extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, #define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ #ifdef PNG_USER_MEM_SUPPORTED -extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, - png_uint_32 size)); -extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, - png_voidp ptr)); +PNG_EXPORTA(100, png_voidp, png_malloc_default, (png_const_structrp png_ptr, + png_alloc_size_t size), PNG_ALLOCATED PNG_DEPRECATED); +PNG_EXPORTA(101, void, png_free_default, (png_const_structrp png_ptr, + png_voidp ptr), PNG_DEPRECATED); #endif -extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, - png_voidp s1, png_voidp s2, png_uint_32 size)); - -extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, - png_voidp s1, int value, png_uint_32 size)); - -#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ -extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, - int check)); -#endif /* USE_FAR_KEYWORD */ - -#ifndef PNG_NO_ERROR_TEXT +#ifdef PNG_ERROR_TEXT_SUPPORTED /* 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)); +PNG_EXPORTA(102, void, png_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); /* 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)); +PNG_EXPORTA(103, void, png_chunk_error, (png_const_structrp png_ptr, + png_const_charp error_message), PNG_NORETURN); + #else /* Fatal error in PNG image of libpng - can't continue */ -extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)); +PNG_EXPORTA(104, void, png_err, (png_const_structrp png_ptr), PNG_NORETURN); #endif -#ifndef PNG_NO_WARNINGS +#ifdef PNG_WARNINGS_SUPPORTED /* 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)); +PNG_EXPORT(105, void, png_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); + +/* Non-fatal error in libpng, chunk name is prepended to message. */ +PNG_EXPORT(106, void, png_chunk_warning, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Benign error in libpng. Can continue, but may have a problem. + * User can choose whether to handle as a fatal error or as a warning. */ +PNG_EXPORT(107, void, png_benign_error, (png_const_structrp 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 */ +/* Same, chunk name is prepended to message (only during read) */ +PNG_EXPORT(108, void, png_chunk_benign_error, (png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif + +PNG_EXPORT(109, void, png_set_benign_errors, + (png_structrp png_ptr, int allowed)); +#else +# ifdef PNG_ALLOW_BENIGN_ERRORS +# define png_benign_error png_warning +# define png_chunk_benign_error png_chunk_warning +# else +# define png_benign_error png_error +# define png_chunk_benign_error png_chunk_error +# endif +#endif /* The png_set_ functions are for storing values in the png_info_struct. * Similarly, the png_get_ calls are used to read values from the @@ -2106,421 +2022,495 @@ extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, * 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)); +PNG_EXPORT(110, png_uint_32, png_get_valid, (png_const_structrp png_ptr, + png_const_inforp 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)); +PNG_EXPORT(111, png_size_t, png_get_rowbytes, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); -#if defined(PNG_INFO_IMAGE_SUPPORTED) +#ifdef 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)); + * returned from png_read_png(). + */ +PNG_EXPORT(112, png_bytepp, png_get_rows, (png_const_structrp png_ptr, + png_const_inforp 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)); + * by png_write_png(). + */ +PNG_EXPORT(113, void, png_set_rows, (png_const_structrp png_ptr, + png_inforp 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)); +PNG_EXPORT(114, png_byte, png_get_channels, (png_const_structrp png_ptr, + png_const_inforp 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)); +PNG_EXPORT(115, png_uint_32, png_get_image_width, (png_const_structrp png_ptr, + png_const_inforp 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)); +PNG_EXPORT(116, png_uint_32, png_get_image_height, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); /* Returns image bit_depth. */ -extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp -png_ptr, png_infop info_ptr)); +PNG_EXPORT(117, png_byte, png_get_bit_depth, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); /* Returns image color_type. */ -extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); +PNG_EXPORT(118, png_byte, png_get_color_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); /* Returns image filter_type. */ -extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); +PNG_EXPORT(119, png_byte, png_get_filter_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); /* Returns image interlace_type. */ -extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); +PNG_EXPORT(120, png_byte, png_get_interlace_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); /* Returns image compression_type. */ -extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp -png_ptr, png_infop info_ptr)); +PNG_EXPORT(121, png_byte, png_get_compression_type, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); /* Returns image resolution in pixels per meter, from pHYs chunk data. */ -extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp -png_ptr, png_infop info_ptr)); +PNG_EXPORT(122, png_uint_32, png_get_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(123, png_uint_32, png_get_x_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(124, png_uint_32, png_get_y_pixels_per_meter, + (png_const_structrp png_ptr, png_const_inforp 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 +PNG_FP_EXPORT(125, float, png_get_pixel_aspect_ratio, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +PNG_FIXED_EXPORT(210, png_fixed_point, png_get_pixel_aspect_ratio_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) /* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ -extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp -png_ptr, png_infop info_ptr)); -extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp -png_ptr, png_infop info_ptr)); +PNG_EXPORT(126, png_int_32, png_get_x_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(127, png_int_32, png_get_y_offset_pixels, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(128, png_int_32, png_get_x_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); +PNG_EXPORT(129, png_int_32, png_get_y_offset_microns, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); #endif /* PNG_EASY_ACCESS_SUPPORTED */ +#ifdef PNG_READ_SUPPORTED /* Returns pointer to signature string read from PNG header */ -extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -#if defined(PNG_bKGD_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_16p *background)); +PNG_EXPORT(130, png_const_bytep, png_get_signature, (png_const_structrp png_ptr, + png_const_inforp info_ptr)); #endif -#if defined(PNG_bKGD_SUPPORTED) -extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_16p background)); +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(131, png_uint_32, png_get_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_16p *background)); #endif -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, - png_infop info_ptr, double *white_x, double *white_y, double *red_x, - double *red_y, double *green_x, double *green_y, double *blue_x, - double *blue_y)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point - *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, - png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point - *int_blue_x, png_fixed_point *int_blue_y)); -#endif +#ifdef PNG_bKGD_SUPPORTED +PNG_EXPORT(132, void, png_set_bKGD, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_16p background)); #endif -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, - png_infop info_ptr, double white_x, double white_y, double red_x, - double red_y, double green_x, double green_y, double blue_x, double blue_y)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, - png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point - int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, - png_fixed_point int_blue_y)); -#endif +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(133, png_uint_32, png_get_cHRM, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)) +PNG_FP_EXPORT(230, png_uint_32, png_get_cHRM_XYZ, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *red_X, double *red_Y, double *red_Z, + double *green_X, double *green_Y, double *green_Z, double *blue_X, + double *blue_Y, double *blue_Z)) +PNG_FIXED_EXPORT(134, png_uint_32, png_get_cHRM_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_white_x, png_fixed_point *int_white_y, + png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, + png_fixed_point *int_blue_x, png_fixed_point *int_blue_y)) +PNG_FIXED_EXPORT(231, png_uint_32, png_get_cHRM_XYZ_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z)) #endif -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, - png_infop info_ptr, double *file_gamma)); -#endif -extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point *int_file_gamma)); +#ifdef PNG_cHRM_SUPPORTED +PNG_FP_EXPORT(135, void, png_set_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, double green_x, + double green_y, double blue_x, double blue_y)) +PNG_FP_EXPORT(232, void, png_set_cHRM_XYZ, (png_const_structrp png_ptr, + png_inforp info_ptr, double red_X, double red_Y, double red_Z, + double green_X, double green_Y, double green_Z, double blue_X, + double blue_Y, double blue_Z)) +PNG_FIXED_EXPORT(136, void, png_set_cHRM_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_white_x, + png_fixed_point int_white_y, png_fixed_point int_red_x, + png_fixed_point int_red_y, png_fixed_point int_green_x, + png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)) +PNG_FIXED_EXPORT(233, void, png_set_cHRM_XYZ_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z)) #endif -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, - png_infop info_ptr, double file_gamma)); -#endif -extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_fixed_point int_file_gamma)); +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(137, png_uint_32, png_get_gAMA, (png_const_structrp png_ptr, + png_const_inforp info_ptr, double *file_gamma)) +PNG_FIXED_EXPORT(138, png_uint_32, png_get_gAMA_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_file_gamma)) #endif -#if defined(PNG_hIST_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_16p *hist)); +#ifdef PNG_gAMA_SUPPORTED +PNG_FP_EXPORT(139, void, png_set_gAMA, (png_const_structrp png_ptr, + png_inforp info_ptr, double file_gamma)) +PNG_FIXED_EXPORT(140, void, png_set_gAMA_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, png_fixed_point int_file_gamma)) #endif -#if defined(PNG_hIST_SUPPORTED) -extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_16p hist)); +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(141, png_uint_32, png_get_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_16p *hist)); #endif -extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, - int *bit_depth, int *color_type, int *interlace_method, - int *compression_method, int *filter_method)); +#ifdef PNG_hIST_SUPPORTED +PNG_EXPORT(142, void, png_set_hIST, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_uint_16p hist)); +#endif -extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, - int color_type, int interlace_method, int compression_method, - int filter_method)); +PNG_EXPORT(143, png_uint_32, png_get_IHDR, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); -#if defined(PNG_oFFs_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, +PNG_EXPORT(144, void, png_set_IHDR, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(145, png_uint_32, png_get_oFFs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)); #endif -#if defined(PNG_oFFs_SUPPORTED) -extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, - int unit_type)); +#ifdef PNG_oFFs_SUPPORTED +PNG_EXPORT(146, void, png_set_oFFs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); #endif -#if defined(PNG_pCAL_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, - int *type, int *nparams, png_charp *units, png_charpp *params)); +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(147, png_uint_32, png_get_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charp *purpose, png_int_32 *X0, + png_int_32 *X1, int *type, int *nparams, png_charp *units, + png_charpp *params)); #endif -#if defined(PNG_pCAL_SUPPORTED) -extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, - int type, int nparams, png_charp units, png_charpp params)); +#ifdef PNG_pCAL_SUPPORTED +PNG_EXPORT(148, void, png_set_pCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_const_charp units, png_charpp params)); #endif -#if defined(PNG_pHYs_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(149, png_uint_32, png_get_pHYs, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); #endif -#if defined(PNG_pHYs_SUPPORTED) -extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(150, void, png_set_pHYs, (png_const_structrp png_ptr, + png_inforp info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); #endif -extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_colorp *palette, int *num_palette)); +PNG_EXPORT(151, png_uint_32, png_get_PLTE, (png_const_structrp png_ptr, + png_inforp info_ptr, png_colorp *palette, int *num_palette)); -extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_colorp palette, int num_palette)); +PNG_EXPORT(152, void, png_set_PLTE, (png_structrp png_ptr, + png_inforp info_ptr, png_const_colorp palette, int num_palette)); -#if defined(PNG_sBIT_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_8p *sig_bit)); +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(153, png_uint_32, png_get_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_color_8p *sig_bit)); #endif -#if defined(PNG_sBIT_SUPPORTED) -extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_color_8p sig_bit)); +#ifdef PNG_sBIT_SUPPORTED +PNG_EXPORT(154, void, png_set_sBIT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_color_8p sig_bit)); #endif -#if defined(PNG_sRGB_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, - png_infop info_ptr, int *intent)); +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(155, png_uint_32, png_get_sRGB, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *file_srgb_intent)); #endif -#if defined(PNG_sRGB_SUPPORTED) -extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, - png_infop info_ptr, int intent)); -extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, - png_infop info_ptr, int intent)); +#ifdef PNG_sRGB_SUPPORTED +PNG_EXPORT(156, void, png_set_sRGB, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); +PNG_EXPORT(157, void, png_set_sRGB_gAMA_and_cHRM, (png_const_structrp png_ptr, + png_inforp info_ptr, int srgb_intent)); #endif -#if defined(PNG_iCCP_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_charpp name, int *compression_type, - png_charpp profile, png_uint_32 *proflen)); - /* Note to maintainer: profile should be png_bytepp */ +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(158, png_uint_32, png_get_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen)); #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 */ +#ifdef PNG_iCCP_SUPPORTED +PNG_EXPORT(159, void, png_set_iCCP, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen)); #endif -#if defined(PNG_sPLT_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_sPLT_tpp entries)); +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(160, int, png_get_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_sPLT_tpp entries)); #endif -#if defined(PNG_sPLT_SUPPORTED) -extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#ifdef PNG_sPLT_SUPPORTED +PNG_EXPORT(161, void, png_set_sPLT, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries)); #endif -#if defined(PNG_TEXT_SUPPORTED) +#ifdef 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)); +PNG_EXPORT(162, int, png_get_text, (png_const_structrp png_ptr, + png_inforp 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. +/* 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)); +#ifdef PNG_TEXT_SUPPORTED +PNG_EXPORT(163, void, png_set_text, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text)); #endif -#if defined(PNG_tIME_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_timep *mod_time)); +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(164, png_uint_32, png_get_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_timep *mod_time)); #endif -#if defined(PNG_tIME_SUPPORTED) -extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_timep mod_time)); +#ifdef PNG_tIME_SUPPORTED +PNG_EXPORT(165, void, png_set_tIME, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_timep mod_time)); #endif -#if defined(PNG_tRNS_SUPPORTED) -extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_bytep *trans, int *num_trans, - png_color_16p *trans_values)); +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(166, png_uint_32, png_get_tRNS, (png_const_structrp png_ptr, + png_inforp info_ptr, png_bytep *trans_alpha, int *num_trans, + png_color_16p *trans_color)); #endif -#if defined(PNG_tRNS_SUPPORTED) -extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_bytep trans, int num_trans, - png_color_16p trans_values)); +#ifdef PNG_tRNS_SUPPORTED +PNG_EXPORT(167, void, png_set_tRNS, (png_structrp png_ptr, + png_inforp info_ptr, png_const_bytep trans_alpha, int num_trans, + png_const_color_16p trans_color)); #endif -#if defined(PNG_tRNS_SUPPORTED) +#ifdef PNG_sCAL_SUPPORTED +PNG_FP_EXPORT(168, png_uint_32, png_get_sCAL, (png_const_structrp png_ptr, + png_const_inforp info_ptr, int *unit, double *width, double *height)) +#if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* NOTE: this API is currently implemented using floating point arithmetic, + * consequently it can only be used on systems with floating point support. + * In any case the range of values supported by png_fixed_point is small and it + * is highly recommended that png_get_sCAL_s be used instead. + */ +PNG_FIXED_EXPORT(214, png_uint_32, png_get_sCAL_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_fixed_point *width, png_fixed_point *height)) #endif +PNG_EXPORT(169, png_uint_32, png_get_sCAL_s, + (png_const_structrp png_ptr, png_const_inforp info_ptr, int *unit, + png_charpp swidth, png_charpp sheight)); -#if defined(PNG_sCAL_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, int *unit, double *width, double *height)); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, - png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); -#endif -#endif +PNG_FP_EXPORT(170, void, png_set_sCAL, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, double width, double height)) +PNG_FIXED_EXPORT(213, void, png_set_sCAL_fixed, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, png_fixed_point width, + png_fixed_point height)) +PNG_EXPORT(171, void, png_set_sCAL_s, (png_const_structrp png_ptr, + png_inforp info_ptr, int unit, + png_const_charp swidth, png_const_charp sheight)); #endif /* PNG_sCAL_SUPPORTED */ -#if defined(PNG_sCAL_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, - png_infop info_ptr, int unit, double width, double height)); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, - png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); -#endif -#endif -#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +/* Provide the default handling for all unknown chunks or, optionally, for + * specific unknown chunks. + * + * NOTE: prior to 1.6.0 the handling specified for particular chunks on read was + * ignored and the default was used, the per-chunk setting only had an effect on + * write. If you wish to have chunk-specific handling on read in code that must + * work on earlier versions you must use a user chunk callback to specify the + * desired handling (keep or discard.) + * + * The 'keep' parameter is a PNG_HANDLE_CHUNK_ value as listed below. The + * parameter is interpreted as follows: + * + * READ: + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Known chunks: do normal libpng processing, do not keep the chunk (but + * see the comments below about PNG_HANDLE_AS_UNKNOWN_SUPPORTED) + * Unknown chunks: for a specific chunk use the global default, when used + * as the default discard the chunk data. + * PNG_HANDLE_CHUNK_NEVER: + * Discard the chunk data. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Keep the chunk data if the chunk is not critical else raise a chunk + * error. + * PNG_HANDLE_CHUNK_ALWAYS: + * Keep the chunk data. + * + * If the chunk data is saved it can be retrieved using png_get_unknown_chunks, + * below. Notice that specifying "AS_DEFAULT" as a global default is equivalent + * to specifying "NEVER", however when "AS_DEFAULT" is used for specific chunks + * it simply resets the behavior to the libpng default. + * + * INTERACTION WTIH USER CHUNK CALLBACKS: + * The per-chunk handling is always used when there is a png_user_chunk_ptr + * callback and the callback returns 0; the chunk is then always stored *unless* + * it is critical and the per-chunk setting is other than ALWAYS. Notice that + * the global default is *not* used in this case. (In effect the per-chunk + * value is incremented to at least IF_SAFE.) + * + * IMPORTANT NOTE: this behavior will change in libpng 1.7 - the global and + * per-chunk defaults will be honored. If you want to preserve the current + * behavior when your callback returns 0 you must set PNG_HANDLE_CHUNK_IF_SAFE + * as the default - if you don't do this libpng 1.6 will issue a warning. + * + * If you want unhandled unknown chunks to be discarded in libpng 1.6 and + * earlier simply return '1' (handled). + * + * PNG_HANDLE_AS_UNKNOWN_SUPPORTED: + * If this is *not* set known chunks will always be handled by libpng and + * will never be stored in the unknown chunk list. Known chunks listed to + * png_set_keep_unknown_chunks will have no effect. If it is set then known + * chunks listed with a keep other than AS_DEFAULT will *never* be processed + * by libpng, in addition critical chunks must either be processed by the + * callback or saved. + * + * The IHDR and IEND chunks must not be listed. Because this turns off the + * default handling for chunks that would otherwise be recognized the + * behavior of libpng transformations may well become incorrect! + * + * WRITE: + * When writing chunks the options only apply to the chunks specified by + * png_set_unknown_chunks (below), libpng will *always* write known chunks + * required by png_set_ calls and will always write the core critical chunks + * (as required for PLTE). + * + * Each chunk in the png_set_unknown_chunks list is looked up in the + * png_set_keep_unknown_chunks list to find the keep setting, this is then + * interpreted as follows: + * + * PNG_HANDLE_CHUNK_AS_DEFAULT: + * Write safe-to-copy chunks and write other chunks if the global + * default is set to _ALWAYS, otherwise don't write this chunk. + * PNG_HANDLE_CHUNK_NEVER: + * Do not write the chunk. + * PNG_HANDLE_CHUNK_IF_SAFE: + * Write the chunk if it is safe-to-copy, otherwise do not write it. + * PNG_HANDLE_CHUNK_ALWAYS: + * Write the chunk. + * + * Note that the default behavior is effectively the opposite of the read case - + * in read unknown chunks are not stored by default, in write they are written + * by default. Also the behavior of PNG_HANDLE_CHUNK_IF_SAFE is very different + * - on write the safe-to-copy bit is checked, on read the critical bit is + * checked and on read if the chunk is critical an error will be raised. + * + * num_chunks: + * =========== + * If num_chunks is positive, then the "keep" parameter specifies the manner + * for handling only those chunks appearing in the chunk_list array, + * otherwise the chunk list array is ignored. + * + * If num_chunks is 0 the "keep" parameter specifies the default behavior for + * unknown chunks, as described above. + * + * If num_chunks is negative, then the "keep" parameter specifies the manner + * for handling all unknown chunks plus all chunks recognized by libpng + * except for the IHDR, PLTE, tRNS, IDAT, and IEND chunks (which continue to + * be processed by libpng. + */ +PNG_EXPORT(172, void, png_set_keep_unknown_chunks, (png_structrp png_ptr, + int keep, png_const_bytep chunk_list, int num_chunks)); -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) -/* provide a list of chunks and how they are to be handled, if the built-in - handling or default unknown chunk handling is not desired. Any chunks not - listed will be handled in the default manner. The IHDR and IEND chunks - must not be listed. - keep = 0: follow default behaviour - = 1: do not keep - = 2: keep only if safe-to-copy - = 3: keep even if unsafe-to-copy -*/ -extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp - png_ptr, int keep, png_bytep chunk_list, int num_chunks)); -extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, - png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); -extern PNG_EXPORT(void, png_set_unknown_chunk_location) - PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); -extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp - png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +/* The "keep" PNG_HANDLE_CHUNK_ parameter for the specified chunk is returned; + * the result is therefore true (non-zero) if special handling is required, + * false for the default handling. + */ +PNG_EXPORT(173, int, png_handle_as_unknown, (png_const_structrp png_ptr, + png_const_bytep chunk_name)); #endif -#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED -PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep - chunk_name)); + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +PNG_EXPORT(174, void, png_set_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, + int num_unknowns)); + /* NOTE: prior to 1.6.0 this routine set the 'location' field of the added + * unknowns to the location currently stored in the png_struct. This is + * invariably the wrong value on write. To fix this call the following API + * for each chunk in the list with the correct location. If you know your + * code won't be compiled on earlier versions you can rely on + * png_set_unknown_chunks(write-ptr, png_get_unknown_chunks(read-ptr)) doing + * the correct thing. + */ + +PNG_EXPORT(175, void, png_set_unknown_chunk_location, + (png_const_structrp png_ptr, png_inforp info_ptr, int chunk, int location)); + +PNG_EXPORT(176, int, png_get_unknown_chunks, (png_const_structrp png_ptr, + png_inforp info_ptr, png_unknown_chunkpp entries)); #endif /* Png_free_data() will turn off the "valid" flag for anything it frees. - If you need to turn it off for a chunk that your application has freed, - you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ -extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, - png_infop info_ptr, int mask)); - -#if defined(PNG_INFO_IMAGE_SUPPORTED) -/* The "params" pointer is currently not used and is for future expansion. */ -extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, - png_infop info_ptr, - int transforms, - png_voidp params)); -extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, - png_infop info_ptr, - int transforms, - png_voidp params)); -#endif - -/* Define PNG_DEBUG at compile time for debugging information. Higher - * numbers for PNG_DEBUG mean more debugging information. This has - * only been added since version 0.95 so it is not implemented throughout - * libpng yet, but more support will be added as needed. + * 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); */ -#ifdef PNG_DEBUG -#if (PNG_DEBUG > 0) -#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) -#include -#if (PNG_DEBUG > 1) -#define png_debug(l,m) _RPT0(_CRT_WARN,m) -#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) -#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) -#endif -#else /* PNG_DEBUG_FILE || !_MSC_VER */ -#ifndef PNG_DEBUG_FILE -#define PNG_DEBUG_FILE stderr -#endif /* PNG_DEBUG_FILE */ -#if (PNG_DEBUG > 1) -#define png_debug(l,m) \ -{ \ - int num_tabs=l; \ - fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ - (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ -} -#define png_debug1(l,m,p1) \ -{ \ - int num_tabs=l; \ - fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ - (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ -} -#define png_debug2(l,m,p1,p2) \ -{ \ - int num_tabs=l; \ - fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ - (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ -} -#endif /* (PNG_DEBUG > 1) */ -#endif /* _MSC_VER */ -#endif /* (PNG_DEBUG > 0) */ -#endif /* PNG_DEBUG */ -#ifndef png_debug -#define png_debug(l, m) -#endif -#ifndef png_debug1 -#define png_debug1(l, m, p1) -#endif -#ifndef png_debug2 -#define png_debug2(l, m, p1, p2) +PNG_EXPORT(177, void, png_set_invalid, (png_const_structrp png_ptr, + png_inforp info_ptr, int mask)); + +#ifdef PNG_INFO_IMAGE_SUPPORTED +/* The "params" pointer is currently not used and is for future expansion. */ +PNG_EXPORT(178, void, png_read_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); +PNG_EXPORT(179, void, png_write_png, (png_structrp png_ptr, png_inforp info_ptr, + int transforms, png_voidp params)); #endif -extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); -extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); +PNG_EXPORT(180, png_const_charp, png_get_copyright, + (png_const_structrp png_ptr)); +PNG_EXPORT(181, png_const_charp, png_get_header_ver, + (png_const_structrp png_ptr)); +PNG_EXPORT(182, png_const_charp, png_get_header_version, + (png_const_structrp png_ptr)); +PNG_EXPORT(183, png_const_charp, png_get_libpng_ver, + (png_const_structrp png_ptr)); #ifdef PNG_MNG_FEATURES_SUPPORTED -extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp - png_ptr, png_uint_32 mng_features_permitted)); +PNG_EXPORT(184, png_uint_32, png_permit_mng_features, (png_structrp png_ptr, + png_uint_32 mng_features_permitted)); #endif /* For use in png_set_keep_unknown, added to version 1.2.6 */ @@ -2528,94 +2518,151 @@ extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp #define PNG_HANDLE_CHUNK_NEVER 1 #define PNG_HANDLE_CHUNK_IF_SAFE 2 #define PNG_HANDLE_CHUNK_ALWAYS 3 - -/* Added to version 1.2.0 */ -#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) -#if defined(PNG_MMX_CODE_SUPPORTED) -#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ -#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ -#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 -#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 -#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 -#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 -#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 -#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 -#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ - -#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ - | PNG_ASM_FLAG_MMX_READ_INTERLACE \ - | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ - | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ - | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ - | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) -#define PNG_MMX_WRITE_FLAGS ( 0 ) - -#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ - | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ - | PNG_MMX_READ_FLAGS \ - | PNG_MMX_WRITE_FLAGS ) - -#define PNG_SELECT_READ 1 -#define PNG_SELECT_WRITE 2 -#endif /* PNG_MMX_CODE_SUPPORTED */ - -#if !defined(PNG_1_0_X) -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) - PNGARG((int flag_select, int *compilerID)); - -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) - PNGARG((int flag_select)); - -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_asm_flags) - PNGARG((png_structp png_ptr)); - -/* pngget.c */ -extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) - PNGARG((png_structp png_ptr)); - -/* pngget.c */ -extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) - PNGARG((png_structp png_ptr)); - -/* pngset.c */ -extern PNG_EXPORT(void,png_set_asm_flags) - PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); - -/* pngset.c */ -extern PNG_EXPORT(void,png_set_mmx_thresholds) - PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, - png_uint_32 mmx_rowbytes_threshold)); - -#endif /* PNG_1_0_X */ - -#if !defined(PNG_1_0_X) -/* png.c, pnggccrd.c, or pngvcrd.c */ -extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); -#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ +#define PNG_HANDLE_CHUNK_LAST 4 /* Strip the prepended error numbers ("#nnn ") from error and warning - * messages before passing them to the error or warning handler. */ + * 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)); +PNG_EXPORT(185, void, png_set_strip_error_numbers, (png_structrp png_ptr, + png_uint_32 strip_mode)); #endif -#endif /* PNG_1_0_X */ - -/* Added at libpng-1.2.6 */ +/* Added in libpng-1.2.6 */ #ifdef PNG_SET_USER_LIMITS_SUPPORTED -extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp - png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); -extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp - png_ptr)); -extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp - png_ptr)); +PNG_EXPORT(186, void, png_set_user_limits, (png_structrp png_ptr, + png_uint_32 user_width_max, png_uint_32 user_height_max)); +PNG_EXPORT(187, png_uint_32, png_get_user_width_max, + (png_const_structrp png_ptr)); +PNG_EXPORT(188, png_uint_32, png_get_user_height_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.0 */ +PNG_EXPORT(189, void, png_set_chunk_cache_max, (png_structrp png_ptr, + png_uint_32 user_chunk_cache_max)); +PNG_EXPORT(190, png_uint_32, png_get_chunk_cache_max, + (png_const_structrp png_ptr)); +/* Added in libpng-1.4.1 */ +PNG_EXPORT(191, void, png_set_chunk_malloc_max, (png_structrp png_ptr, + png_alloc_size_t user_chunk_cache_max)); +PNG_EXPORT(192, png_alloc_size_t, png_get_chunk_malloc_max, + (png_const_structrp png_ptr)); #endif -/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ +#if defined(PNG_INCH_CONVERSIONS_SUPPORTED) +PNG_EXPORT(193, png_uint_32, png_get_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(194, png_uint_32, png_get_x_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_EXPORT(195, png_uint_32, png_get_y_pixels_per_inch, + (png_const_structrp png_ptr, png_const_inforp info_ptr)); + +PNG_FP_EXPORT(196, float, png_get_x_offset_inches, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(211, png_fixed_point, png_get_x_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +PNG_FP_EXPORT(197, float, png_get_y_offset_inches, (png_const_structrp png_ptr, + png_const_inforp info_ptr)) +#ifdef PNG_FIXED_POINT_SUPPORTED /* otherwise not implemented. */ +PNG_FIXED_EXPORT(212, png_fixed_point, png_get_y_offset_inches_fixed, + (png_const_structrp png_ptr, png_const_inforp info_ptr)) +#endif + +# ifdef PNG_pHYs_SUPPORTED +PNG_EXPORT(198, png_uint_32, png_get_pHYs_dpi, (png_const_structrp png_ptr, + png_const_inforp info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, + int *unit_type)); +# endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */ + +/* Added in libpng-1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +PNG_EXPORT(199, png_uint_32, png_get_io_state, (png_const_structrp png_ptr)); + +/* Removed from libpng 1.6; use png_get_io_chunk_type. */ +PNG_REMOVED(200, png_const_bytep, png_get_io_chunk_name, (png_structrp png_ptr), + PNG_DEPRECATED) + +PNG_EXPORT(216, png_uint_32, png_get_io_chunk_type, + (png_const_structrp png_ptr)); + +/* The flags returned by png_get_io_state() are the following: */ +# define PNG_IO_NONE 0x0000 /* no I/O at this moment */ +# define PNG_IO_READING 0x0001 /* currently reading */ +# define PNG_IO_WRITING 0x0002 /* currently writing */ +# define PNG_IO_SIGNATURE 0x0010 /* currently at the file signature */ +# define PNG_IO_CHUNK_HDR 0x0020 /* currently at the chunk header */ +# define PNG_IO_CHUNK_DATA 0x0040 /* currently at the chunk data */ +# define PNG_IO_CHUNK_CRC 0x0080 /* currently at the chunk crc */ +# define PNG_IO_MASK_OP 0x000f /* current operation: reading/writing */ +# define PNG_IO_MASK_LOC 0x00f0 /* current location: sig/hdr/data/crc */ +#endif /* ?PNG_IO_STATE_SUPPORTED */ + +/* Interlace support. The following macros are always defined so that if + * libpng interlace handling is turned off the macros may be used to handle + * interlaced images within the application. + */ +#define PNG_INTERLACE_ADAM7_PASSES 7 + +/* Two macros to return the first row and first column of the original, + * full, image which appears in a given pass. 'pass' is in the range 0 + * to 6 and the result is in the range 0 to 7. + */ +#define PNG_PASS_START_ROW(pass) (((1&~(pass))<<(3-((pass)>>1)))&7) +#define PNG_PASS_START_COL(pass) (((1& (pass))<<(3-(((pass)+1)>>1)))&7) + +/* A macro to return the offset between pixels in the output row for a pair of + * pixels in the input - effectively the inverse of the 'COL_SHIFT' macro that + * follows. Note that ROW_OFFSET is the offset from one row to the next whereas + * COL_OFFSET is from one column to the next, within a row. + */ +#define PNG_PASS_ROW_OFFSET(pass) ((pass)>2?(8>>(((pass)-1)>>1)):8) +#define PNG_PASS_COL_OFFSET(pass) (1<<((7-(pass))>>1)) + +/* Two macros to help evaluate the number of rows or columns in each + * pass. This is expressed as a shift - effectively log2 of the number or + * rows or columns in each 8x8 tile of the original image. + */ +#define PNG_PASS_ROW_SHIFT(pass) ((pass)>2?(8-(pass))>>1:3) +#define PNG_PASS_COL_SHIFT(pass) ((pass)>1?(7-(pass))>>1:3) + +/* Hence two macros to determine the number of rows or columns in a given + * pass of an image given its height or width. In fact these macros may + * return non-zero even though the sub-image is empty, because the other + * dimension may be empty for a small image. + */ +#define PNG_PASS_ROWS(height, pass) (((height)+(((1<>PNG_PASS_ROW_SHIFT(pass)) +#define PNG_PASS_COLS(width, pass) (((width)+(((1<>PNG_PASS_COL_SHIFT(pass)) + +/* For the reader row callbacks (both progressive and sequential) it is + * necessary to find the row in the output image given a row in an interlaced + * image, so two more macros: + */ +#define PNG_ROW_FROM_PASS_ROW(y_in, pass) \ + (((y_in)<>(((7-(off))-(pass))<<2)) & 0xF) | \ + ((0x01145AF0>>(((7-(off))-(pass))<<2)) & 0xF0)) + +#define PNG_ROW_IN_INTERLACE_PASS(y, pass) \ + ((PNG_PASS_MASK(pass,0) >> ((y)&7)) & 1) +#define PNG_COL_IN_INTERLACE_PASS(x, pass) \ + ((PNG_PASS_MASK(pass,1) >> ((x)&7)) & 1) #ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED /* With these routines we avoid an integer divide, which will be slower on @@ -2630,910 +2677,616 @@ extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp * [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 */ + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ -# define png_composite(composite, fg, alpha, bg) \ - { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ - + (png_uint_16)(bg)*(png_uint_16)(255 - \ - (png_uint_16)(alpha)) + (png_uint_16)128); \ +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) \ + * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 \ + - (png_uint_16)(alpha)) + 128); \ (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } -# define png_composite_16(composite, fg, alpha, bg) \ - { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ - + (png_uint_32)(bg)*(png_uint_32)(65535L - \ - (png_uint_32)(alpha)) + (png_uint_32)32768L); \ +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) \ + * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(65535 \ + - (png_uint_32)(alpha)) + 32768); \ (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } -#else /* standard method using integer division */ +#else /* Standard method using integer division */ -# define png_composite(composite, fg, alpha, bg) \ - (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ - (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ - (png_uint_16)127) / 255) +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + 127) / 255) # define png_composite_16(composite, fg, alpha, bg) \ (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ - (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ - (png_uint_32)32767) / (png_uint_32)65535L) - + (png_uint_32)(bg)*(png_uint_32)(65535 - (png_uint_32)(alpha)) + \ + 32767) / 65535) #endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ -/* Inline macros to do direct reads of bytes from the input buffer. These - * require that you are using an architecture that uses PNG byte ordering - * (MSB first) and supports unaligned data storage. I think that PowerPC - * in big-endian mode and 680x0 are the only ones that will support this. - * The x86 line of processors definitely do not. The png_get_int_32() - * routine also assumes we are using two's complement format for negative - * values, which is almost certainly true. - */ -#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) -# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) -# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) -# define png_get_int_32(buf) ( *((png_int_32p) (buf))) -#else -extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); -extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); -extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); -#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ -extern PNG_EXPORT(png_uint_32,png_get_uint_31) - PNGARG((png_structp png_ptr, png_bytep buf)); +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(201, png_uint_32, png_get_uint_32, (png_const_bytep buf)); +PNG_EXPORT(202, png_uint_16, png_get_uint_16, (png_const_bytep buf)); +PNG_EXPORT(203, png_int_32, png_get_int_32, (png_const_bytep buf)); +#endif + +PNG_EXPORT(204, png_uint_32, png_get_uint_31, (png_const_structrp png_ptr, + png_const_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 32-bit number into a buffer in PNG byte order (big-endian). */ +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(205, void, png_save_uint_32, (png_bytep buf, png_uint_32 i)); +#endif +#ifdef PNG_SAVE_INT_32_SUPPORTED +PNG_EXPORT(206, void, png_save_int_32, (png_bytep buf, png_int_32 i)); +#endif /* Place a 16-bit number into a buffer in PNG byte order. * The parameter is declared unsigned int, not png_uint_16, * just to avoid potential problems on pre-ANSI C compilers. */ -extern PNG_EXPORT(void,png_save_uint_16) - PNGARG((png_bytep buf, unsigned int i)); +#ifdef PNG_WRITE_INT_FUNCTIONS_SUPPORTED +PNG_EXPORT(207, void, png_save_uint_16, (png_bytep buf, unsigned int i)); /* No png_save_int_16 -- may be added if there's a real need for it. */ +#endif -/* ************************************************************************* */ - -/* 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. +#ifdef PNG_USE_READ_MACROS +/* Inline macros to do direct reads of bytes from the input buffer. + * The png_get_int_32() routine assumes we are using two's complement + * format for negative values, which is almost certainly true. */ +# define PNG_get_uint_32(buf) \ + (((png_uint_32)(*(buf)) << 24) + \ + ((png_uint_32)(*((buf) + 1)) << 16) + \ + ((png_uint_32)(*((buf) + 2)) << 8) + \ + ((png_uint_32)(*((buf) + 3)))) + /* From libpng-1.4.0 until 1.4.4, the png_get_uint_16 macro (but not the + * function) incorrectly returned a value of type png_uint_32. + */ +# define PNG_get_uint_16(buf) \ + ((png_uint_16) \ + (((unsigned int)(*(buf)) << 8) + \ + ((unsigned int)(*((buf) + 1))))) -/* Various modes of operation, that are visible to applications because - * they are used for unknown chunk location. - */ -#define PNG_HAVE_IHDR 0x01 -#define PNG_HAVE_PLTE 0x02 -#define PNG_HAVE_IDAT 0x04 -#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ -#define PNG_HAVE_IEND 0x10 +# define PNG_get_int_32(buf) \ + ((png_int_32)((*(buf) & 0x80) \ + ? -((png_int_32)((png_get_uint_32(buf) ^ 0xffffffffL) + 1)) \ + : (png_int_32)png_get_uint_32(buf))) -#if defined(PNG_INTERNAL) - -/* More modes of operation. Note that after an init, mode is set to - * zero automatically when the structure is created. - */ -#define PNG_HAVE_gAMA 0x20 -#define PNG_HAVE_cHRM 0x40 -#define PNG_HAVE_sRGB 0x80 -#define PNG_HAVE_CHUNK_HEADER 0x100 -#define PNG_WROTE_tIME 0x200 -#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 -#define PNG_BACKGROUND_IS_GRAY 0x800 -#define PNG_HAVE_PNG_SIGNATURE 0x1000 -#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ - -/* flags for the transformations the PNG library does on the image data */ -#define PNG_BGR 0x0001 -#define PNG_INTERLACE 0x0002 -#define PNG_PACK 0x0004 -#define PNG_SHIFT 0x0008 -#define PNG_SWAP_BYTES 0x0010 -#define PNG_INVERT_MONO 0x0020 -#define PNG_DITHER 0x0040 -#define PNG_BACKGROUND 0x0080 -#define PNG_BACKGROUND_EXPAND 0x0100 - /* 0x0200 unused */ -#define PNG_16_TO_8 0x0400 -#define PNG_RGBA 0x0800 -#define PNG_EXPAND 0x1000 -#define PNG_GAMMA 0x2000 -#define PNG_GRAY_TO_RGB 0x4000 -#define PNG_FILLER 0x8000L -#define PNG_PACKSWAP 0x10000L -#define PNG_SWAP_ALPHA 0x20000L -#define PNG_STRIP_ALPHA 0x40000L -#define PNG_INVERT_ALPHA 0x80000L -#define PNG_USER_TRANSFORM 0x100000L -#define PNG_RGB_TO_GRAY_ERR 0x200000L -#define PNG_RGB_TO_GRAY_WARN 0x400000L -#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ - /* 0x800000L Unused */ -#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ -#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ - /* 0x4000000L unused */ - /* 0x8000000L unused */ - /* 0x10000000L unused */ - /* 0x20000000L unused */ - /* 0x40000000L unused */ - -/* flags for png_create_struct */ -#define PNG_STRUCT_PNG 0x0001 -#define PNG_STRUCT_INFO 0x0002 - -/* Scaling factor for filter heuristic weighting calculations */ -#define PNG_WEIGHT_SHIFT 8 -#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) -#define PNG_COST_SHIFT 3 -#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) - -/* flags for the png_ptr->flags rather than declaring a byte for each one */ -#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 -#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 -#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 -#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 -#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 -#define PNG_FLAG_ZLIB_FINISHED 0x0020 -#define PNG_FLAG_ROW_INIT 0x0040 -#define PNG_FLAG_FILLER_AFTER 0x0080 -#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 -#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 -#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 -#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 -#define PNG_FLAG_FREE_PLTE 0x1000 -#define PNG_FLAG_FREE_TRNS 0x2000 -#define PNG_FLAG_FREE_HIST 0x4000 -#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L -#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L -#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L -#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L -#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L -#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L -#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ -#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ - /* 0x800000L unused */ - /* 0x1000000L unused */ - /* 0x2000000L unused */ - /* 0x4000000L unused */ - /* 0x8000000L unused */ - /* 0x10000000L unused */ - /* 0x20000000L unused */ - /* 0x40000000L unused */ - -#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ - PNG_FLAG_CRC_ANCILLARY_NOWARN) - -#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ - PNG_FLAG_CRC_CRITICAL_IGNORE) - -#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ - PNG_FLAG_CRC_CRITICAL_MASK) - -/* save typing and make code easier to understand */ - -#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ - abs((int)((c1).green) - (int)((c2).green)) + \ - abs((int)((c1).blue) - (int)((c2).blue))) - -/* Added to libpng-1.2.6 JB */ -#define PNG_ROWBYTES(pixel_bits, width) \ - ((pixel_bits) >= 8 ? \ - ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ - (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) - -/* PNG_OUT_OF_RANGE returns true if value is outside the range - ideal-delta..ideal+delta. Each argument is evaluated twice. - "ideal" and "delta" should be constants, normally simple - integers, "value" a variable. Added to libpng-1.2.6 JB */ -#define PNG_OUT_OF_RANGE(value, ideal, delta) \ - ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) - -/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ -#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) -/* place to hold the signature string for a PNG file. */ -#ifdef PNG_USE_GLOBAL_ARRAYS - PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; + /* If PNG_PREFIX is defined the same thing as below happens in pnglibconf.h, + * but defining a macro name prefixed with PNG_PREFIX. + */ +# ifndef PNG_PREFIX +# define png_get_uint_32(buf) PNG_get_uint_32(buf) +# define png_get_uint_16(buf) PNG_get_uint_16(buf) +# define png_get_int_32(buf) PNG_get_int_32(buf) +# endif #else -#endif -#endif /* PNG_NO_EXTERN */ - -/* Constant strings for known chunk types. If you need to add a chunk, - * define the name here, and add an invocation of the macro in png.c and - * wherever it's needed. - */ -#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} -#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} -#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} -#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} -#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} -#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} -#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} -#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} -#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} -#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} -#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} -#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} -#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} -#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} -#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} -#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} -#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} -#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} -#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} -#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} -#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} - -#ifdef PNG_USE_GLOBAL_ARRAYS -PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; -PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; -#endif /* PNG_USE_GLOBAL_ARRAYS */ - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Initialize png_ptr struct for reading, and allocate any other memory. - * (old interface - DEPRECATED - use png_create_read_struct instead). - */ -extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); -#undef png_read_init -#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ - PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); -#endif - -extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size)); -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t - png_info_size)); -#endif - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Initialize png_ptr struct for writing, and allocate any other memory. - * (old interface - DEPRECATED - use png_create_write_struct instead). - */ -extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); -#undef png_write_init -#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ - PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); -#endif - -extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size)); -extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, - png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t - png_info_size)); - -/* Allocate memory for an internal libpng struct */ -PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); - -/* Free memory from internal libpng struct */ -PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); - -PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr - malloc_fn, png_voidp mem_ptr)); -PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, - png_free_ptr free_fn, png_voidp mem_ptr)); - -/* Free any memory that info_ptr points to and reset struct. */ -PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, - png_infop info_ptr)); - -#ifndef PNG_1_0_X -/* Function to allocate memory for zlib. */ -PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); - -/* Function to free memory for zlib */ -PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); - -#ifdef PNG_SIZE_T -/* Function to convert a sizeof an item to png_sizeof item */ - PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); -#endif - -/* Next four functions are used internally as callbacks. PNGAPI is required - * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ - -PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, - png_bytep data, png_size_t length)); - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t length)); -#endif - -PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, - png_bytep data, png_size_t length)); - -#if defined(PNG_WRITE_FLUSH_SUPPORTED) -#if !defined(PNG_NO_STDIO) -PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); -#endif -#endif -#else /* PNG_1_0_X */ -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t length)); -#endif -#endif /* PNG_1_0_X */ - -/* Reset the CRC variable */ -PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); - -/* Write the "data" buffer to whatever output you are using. */ -PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, - png_size_t length)); - -/* Read data from whatever input you are using into the "data" buffer */ -PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, - png_size_t length)); - -/* Read bytes into buf, and update png_ptr->crc */ -PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, - png_size_t length)); - -/* Decompress data in a chunk that uses compression */ -#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ - defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) -PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, - int comp_type, png_charp chunkdata, png_size_t chunklength, - png_size_t prefix_length, png_size_t *data_length)); -#endif - -/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ -PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); - -/* Read the CRC from the file and compare it to the libpng calculated CRC */ -PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); - -/* Calculate the CRC over a section of data. Note that we are only - * passing a maximum of 64K on systems that have this as a memory limit, - * since this is the maximum buffer size we can specify. - */ -PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, - png_size_t length)); - -#if defined(PNG_WRITE_FLUSH_SUPPORTED) -PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); -#endif - -/* simple function to write the signature */ -PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); - -/* write various chunks */ - -/* Write the IHDR chunk, and update the png_struct with the necessary - * information. - */ -PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, - png_uint_32 height, - int bit_depth, int color_type, int compression_method, int filter_method, - int interlace_method)); - -PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, - png_uint_32 num_pal)); - -PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, - png_size_t length)); - -PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); - -#if defined(PNG_WRITE_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point - file_gamma)); -#endif -#endif - -#if defined(PNG_WRITE_sBIT_SUPPORTED) -PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, - int color_type)); -#endif - -#if defined(PNG_WRITE_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, - double white_x, double white_y, - double red_x, double red_y, double green_x, double green_y, - double blue_x, double blue_y)); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, - png_fixed_point int_white_x, png_fixed_point int_white_y, - png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point - int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, - png_fixed_point int_blue_y)); -#endif -#endif - -#if defined(PNG_WRITE_sRGB_SUPPORTED) -PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, - int intent)); -#endif - -#if defined(PNG_WRITE_iCCP_SUPPORTED) -PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, - png_charp name, int compression_type, - png_charp profile, int proflen)); - /* Note to maintainer: profile should be png_bytep */ -#endif - -#if defined(PNG_WRITE_sPLT_SUPPORTED) -PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, - png_sPLT_tp palette)); -#endif - -#if defined(PNG_WRITE_tRNS_SUPPORTED) -PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, - png_color_16p values, int number, int color_type)); -#endif - -#if defined(PNG_WRITE_bKGD_SUPPORTED) -PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, - png_color_16p values, int color_type)); -#endif - -#if defined(PNG_WRITE_hIST_SUPPORTED) -PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, - int num_hist)); -#endif - -#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ - defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) -PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, - png_charp key, png_charpp new_key)); -#endif - -#if defined(PNG_WRITE_tEXt_SUPPORTED) -PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, - png_charp text, png_size_t text_len)); -#endif - -#if defined(PNG_WRITE_zTXt_SUPPORTED) -PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, - png_charp text, png_size_t text_len, int compression)); -#endif - -#if defined(PNG_WRITE_iTXt_SUPPORTED) -PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, - int compression, png_charp key, png_charp lang, png_charp lang_key, - png_charp text)); -#endif - -#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ -PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, - png_infop info_ptr, png_textp text_ptr, int num_text)); -#endif - -#if defined(PNG_WRITE_oFFs_SUPPORTED) -PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, - png_int_32 x_offset, png_int_32 y_offset, int unit_type)); -#endif - -#if defined(PNG_WRITE_pCAL_SUPPORTED) -PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, - png_int_32 X0, png_int_32 X1, int type, int nparams, - png_charp units, png_charpp params)); -#endif - -#if defined(PNG_WRITE_pHYs_SUPPORTED) -PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, - png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, - int unit_type)); -#endif - -#if defined(PNG_WRITE_tIME_SUPPORTED) -PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, - png_timep mod_time)); -#endif - -#if defined(PNG_WRITE_sCAL_SUPPORTED) -#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) -PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, - int unit, double width, double height)); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, - int unit, png_charp width, png_charp height)); -#endif -#endif -#endif - -/* Called when finished processing a row of data */ -PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); - -/* Internal use only. Called before first row of data */ -PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); - -#if defined(PNG_READ_GAMMA_SUPPORTED) -PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); -#endif - -/* combine a row of data, dealing with alpha, etc. if requested */ -PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, - int mask)); - -#if defined(PNG_READ_INTERLACING_SUPPORTED) -/* expand an interlaced row */ -/* OLD pre-1.0.9 interface: -PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, - png_bytep row, int pass, png_uint_32 transformations)); - */ -PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); -#endif - -/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ - -#if defined(PNG_WRITE_INTERLACING_SUPPORTED) -/* grab pixels out of a row for an interlaced pass */ -PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, - png_bytep row, int pass)); -#endif - -/* unfilter a row */ -PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, - png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); - -/* Choose the best filter to use and filter the row data */ -PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, - png_row_infop row_info)); - -/* Write out the filtered row. */ -PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, - png_bytep filtered_row)); -/* finish a row while reading, dealing with interlacing passes, etc. */ -PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); - -/* initialize the row buffers, etc. */ -PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); -/* optional call to update the users info structure */ -PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, - png_infop info_ptr)); - -/* these are the functions that do the transformations */ -#if defined(PNG_READ_FILLER_SUPPORTED) -PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, - png_bytep row, png_uint_32 filler, png_uint_32 flags)); -#endif - -#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ - defined(PNG_READ_STRIP_ALPHA_SUPPORTED) -PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, - png_bytep row, png_uint_32 flags)); -#endif - -#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) -PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) -PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop - row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) -PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_READ_PACK_SUPPORTED) -PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_SHIFT_SUPPORTED) -PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, - png_color_8p sig_bits)); -#endif - -#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) -PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_16_TO_8_SUPPORTED) -PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); -#endif - -#if defined(PNG_READ_DITHER_SUPPORTED) -PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, - png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); - -# if defined(PNG_CORRECT_PALETTE_SUPPORTED) -PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, - png_colorp palette, int num_palette)); +# ifdef PNG_PREFIX + /* No macros; revert to the (redefined) function */ +# define PNG_get_uint_32 (png_get_uint_32) +# define PNG_get_uint_16 (png_get_uint_16) +# define PNG_get_int_32 (png_get_int_32) # endif #endif -#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); -#endif +/******************************************************************************* + * SIMPLIFIED API + ******************************************************************************* + * + * Please read the documentation in libpng-manual.txt (TODO: write said + * documentation) if you don't understand what follows. + * + * The simplified API hides the details of both libpng and the PNG file format + * itself. It allows PNG files to be read into a very limited number of + * in-memory bitmap formats or to be written from the same formats. If these + * formats do not accomodate your needs then you can, and should, use the more + * sophisticated APIs above - these support a wide variety of in-memory formats + * and a wide variety of sophisticated transformations to those formats as well + * as a wide variety of APIs to manipulate ancillary information. + * + * To read a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure (see below) on the stack and set the + * version field to PNG_IMAGE_VERSION. + * 2) Call the appropriate png_image_begin_read... function. + * 3) Set the png_image 'format' member to the required sample format. + * 4) Allocate a buffer for the image and, if required, the color-map. + * 5) Call png_image_finish_read to read the image and, if required, the + * color-map into your buffers. + * + * There are no restrictions on the format of the PNG input itself; all valid + * color types, bit depths, and interlace methods are acceptable, and the + * input image is transformed as necessary to the requested in-memory format + * during the png_image_finish_read() step. The only caveat is that if you + * request a color-mapped image from a PNG that is full-color or makes + * complex use of an alpha channel the transformation is extremely lossy and the + * result may look terrible. + * + * To write a PNG file using the simplified API: + * + * 1) Declare a 'png_image' structure on the stack and memset() it to all zero. + * 2) Initialize the members of the structure that describe the image, setting + * the 'format' member to the format of the image samples. + * 3) Call the appropriate png_image_write... function with a pointer to the + * image and, if necessary, the color-map to write the PNG data. + * + * png_image is a structure that describes the in-memory format of an image + * when it is being read or defines the in-memory format of an image that you + * need to write: + */ +#define PNG_IMAGE_VERSION 1 -#if defined(PNG_WRITE_PACK_SUPPORTED) -PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, - png_bytep row, png_uint_32 bit_depth)); -#endif +typedef struct png_control *png_controlp; +typedef struct +{ + png_controlp opaque; /* Initialize to NULL, free with png_image_free */ + png_uint_32 version; /* Set to PNG_IMAGE_VERSION */ + png_uint_32 width; /* Image width in pixels (columns) */ + png_uint_32 height; /* Image height in pixels (rows) */ + png_uint_32 format; /* Image format as defined below */ + png_uint_32 flags; /* A bit mask containing informational flags */ + png_uint_32 colormap_entries; + /* Number of entries in the color-map */ -#if defined(PNG_WRITE_SHIFT_SUPPORTED) -PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, - png_color_8p bit_depth)); -#endif + /* In the event of an error or warning the following field will be set to a + * non-zero value and the 'message' field will contain a '\0' terminated + * string with the libpng error or warning message. If both warnings and + * an error were encountered, only the error is recorded. If there + * are multiple warnings, only the first one is recorded. + * + * The upper 30 bits of this value are reserved, the low two bits contain + * a value as follows: + */ +# define PNG_IMAGE_WARNING 1 +# define PNG_IMAGE_ERROR 2 + /* + * The result is a two bit code such that a value more than 1 indicates + * a failure in the API just called: + * + * 0 - no warning or error + * 1 - warning + * 2 - error + * 3 - error preceded by warning + */ +# define PNG_IMAGE_FAILED(png_cntrl) ((((png_cntrl).warning_or_error)&0x03)>1) -#if defined(PNG_READ_BACKGROUND_SUPPORTED) -#if defined(PNG_READ_GAMMA_SUPPORTED) -PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, - png_color_16p trans_values, png_color_16p background, - png_color_16p background_1, - png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, - png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, - png_uint_16pp gamma_16_to_1, int gamma_shift)); -#else -PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, - png_color_16p trans_values, png_color_16p background)); -#endif -#endif + png_uint_32 warning_or_error; -#if defined(PNG_READ_GAMMA_SUPPORTED) -PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, - png_bytep gamma_table, png_uint_16pp gamma_16_table, - int gamma_shift)); -#endif + char message[64]; +} png_image, *png_imagep; -#if defined(PNG_READ_EXPAND_SUPPORTED) -PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, - png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); -PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, - png_bytep row, png_color_16p trans_value)); -#endif - -/* The following decodes the appropriate chunks, and does error correction, - * then calls the appropriate callback for the chunk if it is valid. +/* The samples of the image have one to four channels whose components have + * original values in the range 0 to 1.0: + * + * 1: A single gray or luminance channel (G). + * 2: A gray/luminance channel and an alpha channel (GA). + * 3: Three red, green, blue color channels (RGB). + * 4: Three color channels and an alpha channel (RGBA). + * + * The components are encoded in one of two ways: + * + * a) As a small integer, value 0..255, contained in a single byte. For the + * alpha channel the original value is simply value/255. For the color or + * luminance channels the value is encoded according to the sRGB specification + * and matches the 8-bit format expected by typical display devices. + * + * The color/gray channels are not scaled (pre-multiplied) by the alpha + * channel and are suitable for passing to color management software. + * + * b) As a value in the range 0..65535, contained in a 2-byte integer. All + * channels can be converted to the original value by dividing by 65535; all + * channels are linear. Color channels use the RGB encoding (RGB end-points) of + * the sRGB specification. This encoding is identified by the + * PNG_FORMAT_FLAG_LINEAR flag below. + * + * When the simplified API needs to convert between sRGB and linear colorspaces, + * the actual sRGB transfer curve defined in the sRGB specification (see the + * article at http://en.wikipedia.org/wiki/SRGB) is used, not the gamma=1/2.2 + * approximation used elsewhere in libpng. + * + * When an alpha channel is present it is expected to denote pixel coverage + * of the color or luminance channels and is returned as an associated alpha + * channel: the color/gray channels are scaled (pre-multiplied) by the alpha + * value. + * + * The samples are either contained directly in the image data, between 1 and 8 + * bytes per pixel according to the encoding, or are held in a color-map indexed + * by bytes in the image data. In the case of a color-map the color-map entries + * are individual samples, encoded as above, and the image data has one byte per + * pixel to select the relevant sample from the color-map. */ -/* decode the IHDR chunk */ -PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); +/* PNG_FORMAT_* + * + * #defines to be used in png_image::format. Each #define identifies a + * particular layout of sample data and, if present, alpha values. There are + * separate defines for each of the two component encodings. + * + * A format is built up using single bit flag values. All combinations are + * valid. Formats can be built up from the flag values or you can use one of + * the predefined values below. When testing formats always use the FORMAT_FLAG + * macros to test for individual features - future versions of the library may + * add new flags. + * + * When reading or writing color-mapped images the format should be set to the + * format of the entries in the color-map then png_image_{read,write}_colormap + * called to read or write the color-map and set the format correctly for the + * image data. Do not set the PNG_FORMAT_FLAG_COLORMAP bit directly! + * + * NOTE: libpng can be built with particular features disabled, if you see + * compiler errors because the definition of one of the following flags has been + * compiled out it is because libpng does not have the required support. It is + * possible, however, for the libpng configuration to enable the format on just + * read or just write; in that case you may see an error at run time. You can + * guard against this by checking for the definition of the appropriate + * "_SUPPORTED" macro, one of: + * + * PNG_SIMPLIFIED_{READ,WRITE}_{BGR,AFIRST}_SUPPORTED + */ +#define PNG_FORMAT_FLAG_ALPHA 0x01U /* format with an alpha channel */ +#define PNG_FORMAT_FLAG_COLOR 0x02U /* color format: otherwise grayscale */ +#define PNG_FORMAT_FLAG_LINEAR 0x04U /* 2 byte channels else 1 byte */ +#define PNG_FORMAT_FLAG_COLORMAP 0x08U /* image data is color-mapped */ -#if defined(PNG_READ_bKGD_SUPPORTED) -PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); +#ifdef PNG_FORMAT_BGR_SUPPORTED +# define PNG_FORMAT_FLAG_BGR 0x10U /* BGR colors, else order is RGB */ #endif -#if defined(PNG_READ_cHRM_SUPPORTED) -PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); +#ifdef PNG_FORMAT_AFIRST_SUPPORTED +# define PNG_FORMAT_FLAG_AFIRST 0x20U /* alpha channel comes first */ #endif -#if defined(PNG_READ_gAMA_SUPPORTED) -PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); +/* Commonly used formats have predefined macros. + * + * First the single byte (sRGB) formats: + */ +#define PNG_FORMAT_GRAY 0 +#define PNG_FORMAT_GA PNG_FORMAT_FLAG_ALPHA +#define PNG_FORMAT_AG (PNG_FORMAT_GA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_RGB PNG_FORMAT_FLAG_COLOR +#define PNG_FORMAT_BGR (PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_BGR) +#define PNG_FORMAT_RGBA (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ARGB (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_AFIRST) +#define PNG_FORMAT_BGRA (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_ABGR (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_AFIRST) + +/* Then the linear 2-byte formats. When naming these "Y" is used to + * indicate a luminance (gray) channel. + */ +#define PNG_FORMAT_LINEAR_Y PNG_FORMAT_FLAG_LINEAR +#define PNG_FORMAT_LINEAR_Y_ALPHA (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_ALPHA) +#define PNG_FORMAT_LINEAR_RGB (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR) +#define PNG_FORMAT_LINEAR_RGB_ALPHA \ + (PNG_FORMAT_FLAG_LINEAR|PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA) + +/* With color-mapped formats the image data is one byte for each pixel, the byte + * is an index into the color-map which is formatted as above. To obtain a + * color-mapped format it is sufficient just to add the PNG_FOMAT_FLAG_COLORMAP + * to one of the above definitions, or you can use one of the definitions below. + */ +#define PNG_FORMAT_RGB_COLORMAP (PNG_FORMAT_RGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGR_COLORMAP (PNG_FORMAT_BGR|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_RGBA_COLORMAP (PNG_FORMAT_RGBA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ARGB_COLORMAP (PNG_FORMAT_ARGB|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_BGRA_COLORMAP (PNG_FORMAT_BGRA|PNG_FORMAT_FLAG_COLORMAP) +#define PNG_FORMAT_ABGR_COLORMAP (PNG_FORMAT_ABGR|PNG_FORMAT_FLAG_COLORMAP) + +/* PNG_IMAGE macros + * + * These are convenience macros to derive information from a png_image + * structure. The PNG_IMAGE_SAMPLE_ macros return values appropriate to the + * actual image sample values - either the entries in the color-map or the + * pixels in the image. The PNG_IMAGE_PIXEL_ macros return corresponding values + * for the pixels and will always return 1 for color-mapped formats. The + * remaining macros return information about the rows in the image and the + * complete image. + * + * NOTE: All the macros that take a png_image::format parameter are compile time + * constants if the format parameter is, itself, a constant. Therefore these + * macros can be used in array declarations and case labels where required. + * Similarly the macros are also pre-processor constants (sizeof is not used) so + * they can be used in #if tests. + * + * First the information about the samples. + */ +#define PNG_IMAGE_SAMPLE_CHANNELS(fmt)\ + (((fmt)&(PNG_FORMAT_FLAG_COLOR|PNG_FORMAT_FLAG_ALPHA))+1) + /* Return the total number of channels in a given format: 1..4 */ + +#define PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)\ + ((((fmt) & PNG_FORMAT_FLAG_LINEAR) >> 2)+1) + /* Return the size in bytes of a single component of a pixel or color-map + * entry (as appropriate) in the image: 1 or 2. + */ + +#define PNG_IMAGE_SAMPLE_SIZE(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * PNG_IMAGE_SAMPLE_COMPONENT_SIZE(fmt)) + /* This is the size of the sample data for one sample. If the image is + * color-mapped it is the size of one color-map entry (and image pixels are + * one byte in size), otherwise it is the size of one image pixel. + */ + +#define PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(fmt)\ + (PNG_IMAGE_SAMPLE_CHANNELS(fmt) * 256) + /* The maximum size of the color-map required by the format expressed in a + * count of components. This can be used to compile-time allocate a + * color-map: + * + * png_uint_16 colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(linear_fmt)]; + * + * png_byte colormap[PNG_IMAGE_MAXIMUM_COLORMAP_COMPONENTS(sRGB_fmt)]; + * + * Alternatively use the PNG_IMAGE_COLORMAP_SIZE macro below to use the + * information from one of the png_image_begin_read_ APIs and dynamically + * allocate the required memory. + */ + +/* Corresponding information about the pixels */ +#define PNG_IMAGE_PIXEL_(test,fmt)\ + (((fmt)&PNG_FORMAT_FLAG_COLORMAP)?1:test(fmt)) + +#define PNG_IMAGE_PIXEL_CHANNELS(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_CHANNELS,fmt) + /* The number of separate channels (components) in a pixel; 1 for a + * color-mapped image. + */ + +#define PNG_IMAGE_PIXEL_COMPONENT_SIZE(fmt)\ + PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_COMPONENT_SIZE,fmt) + /* The size, in bytes, of each component in a pixel; 1 for a color-mapped + * image. + */ + +#define PNG_IMAGE_PIXEL_SIZE(fmt) PNG_IMAGE_PIXEL_(PNG_IMAGE_SAMPLE_SIZE,fmt) + /* The size, in bytes, of a complete pixel; 1 for a color-mapped image. */ + +/* Information about the whole row, or whole image */ +#define PNG_IMAGE_ROW_STRIDE(image)\ + (PNG_IMAGE_PIXEL_CHANNELS((image).format) * (image).width) + /* Return the total number of components in a single row of the image; this + * is the minimum 'row stride', the minimum count of components between each + * row. For a color-mapped image this is the minimum number of bytes in a + * row. + */ + +#define PNG_IMAGE_BUFFER_SIZE(image, row_stride)\ + (PNG_IMAGE_PIXEL_COMPONENT_SIZE((image).format)*(image).height*(row_stride)) + /* Return the size, in bytes, of an image buffer given a png_image and a row + * stride - the number of components to leave space for in each row. + */ + +#define PNG_IMAGE_SIZE(image)\ + PNG_IMAGE_BUFFER_SIZE(image, PNG_IMAGE_ROW_STRIDE(image)) + /* Return the size, in bytes, of the image in memory given just a png_image; + * the row stride is the minimum stride required for the image. + */ + +#define PNG_IMAGE_COLORMAP_SIZE(image)\ + (PNG_IMAGE_SAMPLE_SIZE((image).format) * (image).colormap_entries) + /* Return the size, in bytes, of the color-map of this image. If the image + * format is not a color-map format this will return a size sufficient for + * 256 entries in the given format; check PNG_FORMAT_FLAG_COLORMAP if + * you don't want to allocate a color-map in this case. + */ + +/* PNG_IMAGE_FLAG_* + * + * Flags containing additional information about the image are held in the + * 'flags' field of png_image. + */ +#define PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB 0x01 + /* This indicates the the RGB values of the in-memory bitmap do not + * correspond to the red, green and blue end-points defined by sRGB. + */ + +#define PNG_IMAGE_FLAG_FAST 0x02 + /* On write emphasise speed over compression; the resultant PNG file will be + * larger but will be produced significantly faster, particular for large + * images. Do not use this option for images which will be distributed, only + * used it when producing intermediate files that will be read back in + * repeatedly. For a typical 24-bit image the option will double the read + * speed at the cost of increasing the image size by 25%, however for many + * more compressible images the PNG file can be 10 times larger with only a + * slight speed gain. + */ + +#define PNG_IMAGE_FLAG_16BIT_sRGB 0x04 + /* On read if the image is a 16-bit per component image and there is no gAMA + * or sRGB chunk assume that the components are sRGB encoded. Notice that + * images output by the simplified API always have gamma information; setting + * this flag only affects the interpretation of 16-bit images from an + * external source. It is recommended that the application expose this flag + * to the user; the user can normally easily recognize the difference between + * linear and sRGB encoding. This flag has no effect on write - the data + * passed to the write APIs must have the correct encoding (as defined + * above.) + * + * If the flag is not set (the default) input 16-bit per component data is + * assumed to be linear. + * + * NOTE: the flag can only be set after the png_image_begin_read_ call, + * because that call initializes the 'flags' field. + */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* READ APIs + * --------- + * + * The png_image passed to the read APIs must have been initialized by setting + * the png_controlp field 'opaque' to NULL (or, safer, memset the whole thing.) + */ +#ifdef PNG_STDIO_SUPPORTED +PNG_EXPORT(234, int, png_image_begin_read_from_file, (png_imagep image, + const char *file_name)); + /* The named file is opened for read and the image header is filled in + * from the PNG header in the file. + */ + +PNG_EXPORT(235, int, png_image_begin_read_from_stdio, (png_imagep image, + FILE* file)); + /* The PNG header is read from the stdio FILE object. */ +#endif /* PNG_STDIO_SUPPORTED */ + +PNG_EXPORT(236, int, png_image_begin_read_from_memory, (png_imagep image, + png_const_voidp memory, png_size_t size)); + /* The PNG header is read from the given memory buffer. */ + +PNG_EXPORT(237, int, png_image_finish_read, (png_imagep image, + png_const_colorp background, void *buffer, png_int_32 row_stride, + void *colormap)); + /* Finish reading the image into the supplied buffer and clean up the + * png_image structure. + * + * row_stride is the step, in byte or 2-byte units as appropriate, + * between adjacent rows. A positive stride indicates that the top-most row + * is first in the buffer - the normal top-down arrangement. A negative + * stride indicates that the bottom-most row is first in the buffer. + * + * background need only be supplied if an alpha channel must be removed from + * a png_byte format and the removal is to be done by compositing on a solid + * color; otherwise it may be NULL and any composition will be done directly + * onto the buffer. The value is an sRGB color to use for the background, + * for grayscale output the green channel is used. + * + * background must be supplied when an alpha channel must be removed from a + * single byte color-mapped output format, in other words if: + * + * 1) The original format from png_image_begin_read_from_* had + * PNG_FORMAT_FLAG_ALPHA set. + * 2) The format set by the application does not. + * 3) The format set by the application has PNG_FORMAT_FLAG_COLORMAP set and + * PNG_FORMAT_FLAG_LINEAR *not* set. + * + * For linear output removing the alpha channel is always done by compositing + * on black and background is ignored. + * + * colormap must be supplied when PNG_FORMAT_FLAG_COLORMAP is set. It must + * be at least the size (in bytes) returned by PNG_IMAGE_COLORMAP_SIZE. + * image->colormap_entries will be updated to the actual number of entries + * written to the colormap; this may be less than the original value. + */ + +PNG_EXPORT(238, void, png_image_free, (png_imagep image)); + /* Free any data allocated by libpng in image->opaque, setting the pointer to + * NULL. May be called at any time after the structure is initialized. + */ +#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +/* WRITE APIS + * ---------- + * For write you must initialize a png_image structure to describe the image to + * be written. To do this use memset to set the whole structure to 0 then + * initialize fields describing your image. + * + * version: must be set to PNG_IMAGE_VERSION + * opaque: must be initialized to NULL + * width: image width in pixels + * height: image height in rows + * format: the format of the data (image and color-map) you wish to write + * flags: set to 0 unless one of the defined flags applies; set + * PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB for color format images where the RGB + * values do not correspond to the colors in sRGB. + * colormap_entries: set to the number of entries in the color-map (0 to 256) + */ +PNG_EXPORT(239, int, png_image_write_to_file, (png_imagep image, + const char *file, int convert_to_8bit, const void *buffer, + png_int_32 row_stride, const void *colormap)); + /* Write the image to the named file. */ + +PNG_EXPORT(240, int, png_image_write_to_stdio, (png_imagep image, FILE *file, + int convert_to_8_bit, const void *buffer, png_int_32 row_stride, + const void *colormap)); + /* Write the image to the given (FILE*). */ + +/* With both write APIs if image is in one of the linear formats with 16-bit + * data then setting convert_to_8_bit will cause the output to be an 8-bit PNG + * gamma encoded according to the sRGB specification, otherwise a 16-bit linear + * encoded PNG file is written. + * + * With color-mapped data formats the colormap parameter point to a color-map + * with at least image->colormap_entries encoded in the specified format. If + * the format is linear the written PNG color-map will be converted to sRGB + * regardless of the convert_to_8_bit flag. + * + * With all APIs row_stride is handled as in the read APIs - it is the spacing + * from one row to the next in component sized units (1 or 2 bytes) and if + * negative indicates a bottom-up row layout in the buffer. + * + * Note that the write API does not support interlacing or sub-8-bit pixels. + */ +#endif /* PNG_SIMPLIFIED_WRITE_SUPPORTED */ +/******************************************************************************* + * END OF SIMPLIFIED API + ******************************************************************************/ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +PNG_EXPORT(242, void, png_set_check_for_invalid_index, + (png_structrp png_ptr, int allowed)); +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +PNG_EXPORT(243, int, png_get_palette_max, (png_const_structp png_ptr, + png_const_infop info_ptr)); +# endif +#endif /* CHECK_FOR_INVALID_INDEX */ + +/******************************************************************************* + * IMPLEMENTATION OPTIONS + ******************************************************************************* + * + * Support for arbitrary implementation-specific optimizations. The API allows + * particular options to be turned on or off. 'Option' is the number of the + * option and 'onoff' is 0 (off) or non-0 (on). The value returned is given + * by the PNG_OPTION_ defines below. + * + * HARDWARE: normally hardware capabilites, such as the Intel SSE instructions, + * are detected at run time, however sometimes it may be impossible + * to do this in user mode, in which case it is necessary to discover + * the capabilities in an OS specific way. Such capabilities are + * listed here when libpng has support for them and must be turned + * ON by the application if present. + * + * SOFTWARE: sometimes software optimizations actually result in performance + * decrease on some architectures or systems, or with some sets of + * PNG images. 'Software' options allow such optimizations to be + * selected at run time. + */ +#ifdef PNG_SET_OPTION_SUPPORTED +#ifdef PNG_ARM_NEON_API_SUPPORTED +# define PNG_ARM_NEON 0 /* HARDWARE: ARM Neon SIMD instructions supported */ +#endif +#define PNG_OPTION_NEXT 2 /* Next option - numbers must be even */ + +/* Return values: NOTE: there are four values and 'off' is *not* zero */ +#define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ +#define PNG_OPTION_INVALID 1 /* Option number out of range */ +#define PNG_OPTION_OFF 2 +#define PNG_OPTION_ON 3 + +PNG_EXPORT(244, int, png_set_option, (png_structrp png_ptr, int option, + int onoff)); #endif -#if defined(PNG_READ_hIST_SUPPORTED) -PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif +/******************************************************************************* + * END OF HARDWARE OPTIONS + ******************************************************************************/ -#if defined(PNG_READ_iCCP_SUPPORTED) -extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif /* PNG_READ_iCCP_SUPPORTED */ +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project + * defs, scripts/pnglibconf.h, and scripts/pnglibconf.h.prebuilt + */ -#if defined(PNG_READ_iTXt_SUPPORTED) -PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_oFFs_SUPPORTED) -PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_pCAL_SUPPORTED) -PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_pHYs_SUPPORTED) -PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_sBIT_SUPPORTED) -PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_sCAL_SUPPORTED) -PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_sPLT_SUPPORTED) -extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif /* PNG_READ_sPLT_SUPPORTED */ - -#if defined(PNG_READ_sRGB_SUPPORTED) -PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_tEXt_SUPPORTED) -PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_tIME_SUPPORTED) -PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_tRNS_SUPPORTED) -PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -#if defined(PNG_READ_zTXt_SUPPORTED) -PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, - png_uint_32 length)); -#endif - -PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); - -PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, - png_bytep chunk_name)); - -/* handle the transformations for reading and writing */ -PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); - -PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); - -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, - png_uint_32 length)); -PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t buffer_length)); -PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, - png_bytep buffer, png_size_t buffer_length)); -PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); -PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); -PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, - png_infop info_ptr)); -PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); -#if defined(PNG_READ_tEXt_SUPPORTED) -PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif -#if defined(PNG_READ_zTXt_SUPPORTED) -PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif -#if defined(PNG_READ_iTXt_SUPPORTED) -PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, - png_infop info_ptr, png_uint_32 length)); -PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, - png_infop info_ptr)); -#endif - -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ - -#ifdef PNG_MNG_FEATURES_SUPPORTED -PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, - png_bytep row)); -PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, - png_bytep row)); -#endif - -#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) -#if defined(PNG_MMX_CODE_SUPPORTED) -/* png.c */ /* PRIVATE */ -PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); -#endif -#endif - -#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) -PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, -png_infop info_ptr)); - -#if defined(PNG_pHYs_SUPPORTED) -PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, -png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); -#endif /* PNG_pHYs_SUPPORTED */ -#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ - -/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ - -#endif /* PNG_INTERNAL */ - -#ifdef __cplusplus -//} +/* The last ordinal number (this is the *last* one already used; the next + * one to use is one more than this.) Maintainer, remember to add an entry to + * scripts/symbols.def as well. + */ +#ifdef PNG_EXPORT_LAST_ORDINAL + PNG_EXPORT_LAST_ORDINAL(244); #endif #endif /* PNG_VERSION_INFO_ONLY */ -/* do not put anything past this line */ +/* Do not put anything past this line */ #endif /* PNG_H */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngconf.h b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngconf.h index c379da8..b3ceeae 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngconf.h +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngconf.h @@ -1,11 +1,16 @@ /* pngconf.h - machine configurable file for libpng * - * libpng version 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 + * libpng version 1.6.1 - March 28, 2013 + * + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * */ /* Any machine specific code is near the front of this file, so if you @@ -17,1489 +22,595 @@ #ifndef PNGCONF_H #define PNGCONF_H -#define PNG_1_2_X - -//============================================================================== -// These are some Juce config settings that should remove any unnecessary code bloat.. - -#define PNG_NO_STDIO 1 -#define PNG_DEBUG 0 -#define PNG_NO_WARNINGS 1 -#define PNG_NO_ERROR_TEXT 1 -#define PNG_NO_ERROR_NUMBERS 1 -#define PNG_NO_USER_MEM 1 -#define PNG_NO_READ_iCCP 1 -#define PNG_NO_READ_UNKNOWN_CHUNKS 1 -#define PNG_NO_READ_USER_CHUNKS 1 -#define PNG_NO_READ_iTXt 1 -#define PNG_NO_READ_sCAL 1 -#define PNG_NO_READ_sPLT 1 - -#define png_error(a, b) png_err(a) -#define png_warning(a, b) -#define png_chunk_error(a, b) png_err(a) -#define png_chunk_warning(a, b) - -//============================================================================== - -/* - * PNG_USER_CONFIG has to be defined on the compiler command line. This - * includes the resource compiler for Windows DLL configurations. - */ -#ifdef PNG_USER_CONFIG -# ifndef PNG_USER_PRIVATEBUILD -# define PNG_USER_PRIVATEBUILD +/* To do: Do all of this in scripts/pnglibconf.dfa */ +#ifdef PNG_SAFE_LIMITS_SUPPORTED +# ifdef PNG_USER_WIDTH_MAX +# undef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +# endif +# ifdef PNG_USER_HEIGHT_MAX +# undef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +# endif +# ifdef PNG_USER_CHUNK_MALLOC_MAX +# undef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 4000000L +# endif +# ifdef PNG_USER_CHUNK_CACHE_MAX +# undef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 128 # endif -#include "pngusr.h" #endif -/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ -#ifdef PNG_CONFIGURE_LIBPNG -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#endif +#ifndef PNG_BUILDING_SYMBOL_TABLE /* else includes may cause problems */ -/* - * Added at libpng-1.2.8 +/* From libpng 1.6.0 libpng requires an ANSI X3.159-1989 ("ISOC90") compliant C + * compiler for correct compilation. The following header files are required by + * the standard. If your compiler doesn't provide these header files, or they + * do not match the standard, you will need to provide/improve them. + */ +#include +#include + +/* Library header files. These header files are all defined by ISOC90; libpng + * expects conformant implementations, however, an ISOC90 conformant system need + * not provide these header files if the functionality cannot be implemented. + * In this case it will be necessary to disable the relevant parts of libpng in + * the build of pnglibconf.h. * - * If you create a private DLL you need to define in "pngusr.h" the followings: - * #define PNG_USER_PRIVATEBUILD - * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." - * #define PNG_USER_DLLFNAME_POSTFIX - * e.g. // private DLL "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 + * Prior to 1.6.0 string.h was included here; the API changes in 1.6.0 to not + * include this unnecessary header file. */ -#ifdef __STDC__ -#ifdef SPECIALBUILD -# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ - are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") -#endif - -#ifdef PRIVATEBUILD -# pragma message("PRIVATEBUILD is deprecated.\ - Use PNG_USER_PRIVATEBUILD instead.") -# define PNG_USER_PRIVATEBUILD PRIVATEBUILD -#endif -#endif /* __STDC__ */ - -#ifndef PNG_VERSION_INFO_ONLY - -/* End of material added to libpng-1.2.8 */ - -/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble - Restored at libpng-1.2.21 */ -# define PNG_WARN_UNINITIALIZED_ROW 1 -/* End of material added at libpng-1.2.19/1.2.21 */ - -/* This is the size of the compression buffer, and thus the size of - * an IDAT chunk. Make this whatever size you feel is best for your - * machine. One of these will be allocated per png_struct. When this - * is full, it writes the data to the disk, and does some other - * calculations. Making this an extremely small size will slow - * the library down, but you may want to experiment to determine - * where it becomes significant, if you are concerned with memory - * usage. Note that zlib allocates at least 32Kb also. For readers, - * this describes the size of the buffer available to read the data in. - * Unless this gets smaller than the size of a row (compressed), - * it should not make much difference how big this is. - */ - -#ifndef PNG_ZBUF_SIZE -# define PNG_ZBUF_SIZE 8192 -#endif - -/* Enable if you want a write-only libpng */ - -#ifndef PNG_NO_READ_SUPPORTED -# define PNG_READ_SUPPORTED -#endif - -/* Enable if you want a read-only libpng */ - -#ifndef PNG_NO_WRITE_SUPPORTED -# define PNG_WRITE_SUPPORTED -#endif - -/* Enabled by default in 1.2.0. You can disable this if you don't need to - support PNGs that are embedded in MNG datastreams */ -#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) -# ifndef PNG_MNG_FEATURES_SUPPORTED -# define PNG_MNG_FEATURES_SUPPORTED -# endif -#endif - -#ifndef PNG_NO_FLOATING_POINT_SUPPORTED -# ifndef PNG_FLOATING_POINT_SUPPORTED -# define PNG_FLOATING_POINT_SUPPORTED -# endif -#endif - -/* If you are running on a machine where you cannot allocate more - * than 64K of memory at once, uncomment this. While libpng will not - * normally need that much memory in a chunk (unless you load up a very - * large file), zlib needs to know how big of a chunk it can use, and - * libpng thus makes sure to check any memory allocation to verify it - * will fit into memory. -#define PNG_MAX_MALLOC_64K - */ -#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) -# define PNG_MAX_MALLOC_64K -#endif - -/* Special munging to support doing things the 'cygwin' way: - * 'Normal' png-on-win32 defines/defaults: - * PNG_BUILD_DLL -- building dll - * PNG_USE_DLL -- building an application, linking to dll - * (no define) -- building static library, or building an - * application and linking to the static lib - * 'Cygwin' defines/defaults: - * PNG_BUILD_DLL -- (ignored) building the dll - * (no define) -- (ignored) building an application, linking to the dll - * PNG_STATIC -- (ignored) building the static lib, or building an - * application that links to the static lib. - * ALL_STATIC -- (ignored) building various static libs, or building an - * application that links to the static libs. - * Thus, - * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and - * this bit of #ifdefs will define the 'correct' config variables based on - * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but - * unnecessary. - * - * Also, the precedence order is: - * ALL_STATIC (since we can't #undef something outside our namespace) - * PNG_BUILD_DLL - * PNG_STATIC - * (nothing) == PNG_USE_DLL - * - * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent - * of auto-import in binutils, we no longer need to worry about - * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, - * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes - * to __declspec() stuff. However, we DO need to worry about - * PNG_BUILD_DLL and PNG_STATIC because those change some defaults - * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. - */ -#if defined(__CYGWIN__) -# if defined(ALL_STATIC) -# if defined(PNG_BUILD_DLL) -# undef PNG_BUILD_DLL -# endif -# if defined(PNG_USE_DLL) -# undef PNG_USE_DLL -# endif -# if defined(PNG_DLL) -# undef PNG_DLL -# endif -# if !defined(PNG_STATIC) -# define PNG_STATIC -# endif -# else -# if defined (PNG_BUILD_DLL) -# if defined(PNG_STATIC) -# undef PNG_STATIC -# endif -# if defined(PNG_USE_DLL) -# undef PNG_USE_DLL -# endif -# if !defined(PNG_DLL) -# define PNG_DLL -# endif -# else -# if defined(PNG_STATIC) -# if defined(PNG_USE_DLL) -# undef PNG_USE_DLL -# endif -# if defined(PNG_DLL) -# undef PNG_DLL -# endif -# else -# if !defined(PNG_USE_DLL) -# define PNG_USE_DLL -# endif -# if !defined(PNG_DLL) -# define PNG_DLL -# endif -# endif -# endif -# endif -#endif - -/* This protects us against compilers that run on a windowing system - * and thus don't have or would rather us not use the stdio types: - * stdin, stdout, and stderr. The only one currently used is stderr - * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will - * prevent these from being compiled and used. #defining PNG_NO_STDIO - * will also prevent these, plus will prevent the entire set of stdio - * macros and functions (FILE *, printf, etc.) from being compiled and used, - * unless (PNG_DEBUG > 0) has been #defined. - * - * #define PNG_NO_CONSOLE_IO - * #define PNG_NO_STDIO - */ - -#if defined(_WIN32_WCE) -# include - /* Console I/O functions are not supported on WindowsCE */ -# define PNG_NO_CONSOLE_IO -# ifdef PNG_DEBUG -# undef PNG_DEBUG -# endif -#endif - -#ifdef PNG_BUILD_DLL -# ifndef PNG_CONSOLE_IO_SUPPORTED -# ifndef PNG_NO_CONSOLE_IO -# define PNG_NO_CONSOLE_IO -# endif -# endif -#endif - -# ifdef PNG_NO_STDIO -# ifndef PNG_NO_CONSOLE_IO -# define PNG_NO_CONSOLE_IO -# endif -# ifdef PNG_DEBUG -# if (PNG_DEBUG > 0) -# include -# endif -# endif -# else -# if !defined(_WIN32_WCE) -/* "stdio.h" functions are not supported on WindowsCE */ -# include -# endif -# endif - -/* This macro protects us against machines that don't have function - * prototypes (ie K&R style headers). If your compiler does not handle - * function prototypes, define this macro and use the included ansi2knr. - * I've always been able to use _NO_PROTO as the indicator, but you may - * need to drag the empty declaration out in front of here, or change the - * ifdef to suit your own needs. - */ -#ifndef PNGARG - -#ifdef OF /* zlib prototype munger */ -# define PNGARG(arglist) OF(arglist) -#else - -#ifdef _NO_PROTO -# define PNGARG(arglist) () -# ifndef PNG_TYPECAST_NULL -# define PNG_TYPECAST_NULL -# endif -#else -# define PNGARG(arglist) arglist -#endif /* _NO_PROTO */ - - -#endif /* OF */ - -#endif /* PNGARG */ - -/* Try to determine if we are compiling on a Mac. Note that testing for - * just __MWERKS__ is not good enough, because the Codewarrior is now used - * on non-Mac platforms. - */ -#ifndef MACOS -# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ - defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) -# define MACOS -# endif -#endif - -/* enough people need this for various reasons to include it here */ -#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) -# include -#endif - -#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) -# define PNG_SETJMP_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED + /* Required for the definition of FILE: */ +# include #endif #ifdef PNG_SETJMP_SUPPORTED -/* This is an attempt to force a single setjmp behaviour on Linux. If - * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. - */ - -# ifdef __linux__ -# ifdef _BSD_SOURCE -# define PNG_SAVE_BSD_SOURCE -# undef _BSD_SOURCE -# endif -# ifdef _SETJMP_H - /* If you encounter a compiler error here, see the explanation - * near the end of INSTALL. - */ - __png.h__ already includes setjmp.h; - __dont__ include it again.; -# endif -# endif /* __linux__ */ - - /* include setjmp.h for error handling */ + /* Required for the definition of jmp_buf and the declaration of longjmp: */ # include - -# ifdef __linux__ -# ifdef PNG_SAVE_BSD_SOURCE -# define _BSD_SOURCE -# undef PNG_SAVE_BSD_SOURCE -# endif -# endif /* __linux__ */ -#endif /* PNG_SETJMP_SUPPORTED */ - -#ifdef BSD -#if ! JUCE_MAC -# include -#endif -#else -# include #endif -/* Other defines for things like memory and the like can go here. */ -#ifdef PNG_INTERNAL +#ifdef PNG_CONVERT_tIME_SUPPORTED + /* Required for struct tm: */ +# include +#endif -#include +#endif /* PNG_BUILDING_SYMBOL_TABLE */ -/* 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 +/* Prior to 1.6.0 it was possible to turn off 'const' in declarations using + * PNG_NO_CONST; this is no longer supported except for data declarations which + * apparently still cause problems in 2011 on some compilers. */ -#define PNG_EXTERN +#define PNG_CONST const /* backward compatibility only */ -/* Other defines specific to compilers can go here. Try to keep - * them inside an appropriate ifdef/endif pair for portability. +/* This controls optimization of the reading of 16 and 32 bit values + * from PNG files. It can be set on a per-app-file basis - it + * just changes whether a macro is used when the function is called. + * The library builder sets the default; if read functions are not + * built into the library the macro implementation is forced on. + */ +#ifndef PNG_READ_INT_FUNCTIONS_SUPPORTED +# define PNG_USE_READ_MACROS +#endif +#if !defined(PNG_NO_USE_READ_MACROS) && !defined(PNG_USE_READ_MACROS) +# if PNG_DEFAULT_READ_MACROS +# define PNG_USE_READ_MACROS +# endif +#endif + +/* COMPILER SPECIFIC OPTIONS. + * + * These options are provided so that a variety of difficult compilers + * can be used. Some are fixed at build time (e.g. PNG_API_RULE + * below) but still have compiler specific implementations, others + * may be changed on a per-file basis when compiling against libpng. */ -#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 +/* The PNGARG macro was used in versions of libpng prior to 1.6.0 to protect + * against legacy (pre ISOC90) compilers that did not understand function + * prototypes. It is not required for modern C compilers. + */ +#ifndef PNGARG +# define PNGARG(arglist) arglist +#endif + +/* Function calling conventions. + * ============================= + * Normally it is not necessary to specify to the compiler how to call + * a function - it just does it - however on x86 systems derived from + * Microsoft and Borland C compilers ('IBM PC', 'DOS', 'Windows' systems + * and some others) there are multiple ways to call a function and the + * default can be changed on the compiler command line. For this reason + * libpng specifies the calling convention of every exported function and + * every function called via a user supplied function pointer. This is + * done in this file by defining the following macros: + * + * PNGAPI Calling convention for exported functions. + * PNGCBAPI Calling convention for user provided (callback) functions. + * PNGCAPI Calling convention used by the ANSI-C library (required + * for longjmp callbacks and sometimes used internally to + * specify the calling convention for zlib). + * + * These macros should never be overridden. If it is necessary to + * change calling convention in a private build this can be done + * by setting PNG_API_RULE (which defaults to 0) to one of the values + * below to select the correct 'API' variants. + * + * PNG_API_RULE=0 Use PNGCAPI - the 'C' calling convention - throughout. + * This is correct in every known environment. + * PNG_API_RULE=1 Use the operating system convention for PNGAPI and + * the 'C' calling convention (from PNGCAPI) for + * callbacks (PNGCBAPI). This is no longer required + * in any known environment - if it has to be used + * please post an explanation of the problem to the + * libpng mailing list. + * + * These cases only differ if the operating system does not use the C + * calling convention, at present this just means the above cases + * (x86 DOS/Windows sytems) and, even then, this does not apply to + * Cygwin running on those systems. + * + * Note that the value must be defined in pnglibconf.h so that what + * the application uses to call the library matches the conventions + * set when building the library. + */ + +/* Symbol export + * ============= + * When building a shared library it is almost always necessary to tell + * the compiler which symbols to export. The png.h macro 'PNG_EXPORT' + * is used to mark the symbols. On some systems these symbols can be + * extracted at link time and need no special processing by the compiler, + * on other systems the symbols are flagged by the compiler and just + * the declaration requires a special tag applied (unfortunately) in a + * compiler dependent way. Some systems can do either. + * + * A small number of older systems also require a symbol from a DLL to + * be flagged to the program that calls it. This is a problem because + * we do not know in the header file included by application code that + * the symbol will come from a shared library, as opposed to a statically + * linked one. For this reason the application must tell us by setting + * the magic flag PNG_USE_DLL to turn on the special processing before + * it includes png.h. + * + * Four additional macros are used to make this happen: + * + * PNG_IMPEXP The magic (if any) to cause a symbol to be exported from + * the build or imported if PNG_USE_DLL is set - compiler + * and system specific. + * + * PNG_EXPORT_TYPE(type) A macro that pre or appends PNG_IMPEXP to + * 'type', compiler specific. + * + * PNG_DLL_EXPORT Set to the magic to use during a libpng build to + * make a symbol exported from the DLL. Not used in the + * public header files; see pngpriv.h for how it is used + * in the libpng build. + * + * PNG_DLL_IMPORT Set to the magic to force the libpng symbols to come + * from a DLL - used to define PNG_IMPEXP when + * PNG_USE_DLL is set. + */ + +/* System specific discovery. + * ========================== + * This code is used at build time to find PNG_IMPEXP, the API settings + * and PNG_EXPORT_TYPE(), it may also set a macro to indicate the DLL + * import processing is possible. On Windows systems it also sets + * compiler-specific macros to the values required to change the calling + * conventions of the various functions. + */ +#if defined(_Windows) || defined(_WINDOWS) || defined(WIN32) ||\ + defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + /* Windows system (DOS doesn't support DLLs). Includes builds under Cygwin or + * MinGW on any architecture currently supported by Windows. Also includes + * Watcom builds but these need special treatment because they are not + * compatible with GCC or Visual C because of different calling conventions. + */ +# if PNG_API_RULE == 2 + /* If this line results in an error, either because __watcall is not + * understood or because of a redefine just below you cannot use *this* + * build of the library with the compiler you are using. *This* build was + * build using Watcom and applications must also be built using Watcom! + */ +# define PNGCAPI __watcall +# endif + +# if defined(__GNUC__) || (defined(_MSC_VER) && (_MSC_VER >= 800)) +# define PNGCAPI __cdecl +# if PNG_API_RULE == 1 + /* If this line results in an error __stdcall is not understood and + * PNG_API_RULE should not have been set to '1'. + */ +# define PNGAPI __stdcall # endif # else -# include -# endif -# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) - /* Amiga SAS/C: We must include builtin FPU functions when compiling using - * MATH=68881 - */ -# include -# endif -#endif - -/* Codewarrior on NT has linking problems without this. */ -#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) -# define PNG_ALWAYS_EXTERN -#endif - -/* This provides the non-ANSI (far) memory allocation routines. */ -#if defined(__TURBOC__) && defined(__MSDOS__) -# include -# include -#endif - -/* I have no idea why is this necessary... */ -#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ - defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) -# include -#endif - -/* This controls how fine the dithering gets. As this allocates - * a largish chunk of memory (32K), those who are not as concerned - * with dithering quality can decrease some or all of these. - */ -#ifndef PNG_DITHER_RED_BITS -# define PNG_DITHER_RED_BITS 5 -#endif -#ifndef PNG_DITHER_GREEN_BITS -# define PNG_DITHER_GREEN_BITS 5 -#endif -#ifndef PNG_DITHER_BLUE_BITS -# define PNG_DITHER_BLUE_BITS 5 -#endif - -/* This controls how fine the gamma correction becomes when you - * are only interested in 8 bits anyway. Increasing this value - * results in more memory being used, and more pow() functions - * being called to fill in the gamma tables. Don't set this value - * less then 8, and even that may not work (I haven't tested it). - */ - -#ifndef PNG_MAX_GAMMA_8 -# define PNG_MAX_GAMMA_8 11 -#endif - -/* This controls how much a difference in gamma we can tolerate before - * we actually start doing gamma conversion. - */ -#ifndef PNG_GAMMA_THRESHOLD -# define PNG_GAMMA_THRESHOLD 0.05 -#endif - -#endif /* PNG_INTERNAL */ - -/* The following uses const char * instead of char * for error - * and warning message functions, so some compilers won't complain. - * If you do not want to use const, define PNG_NO_CONST here. - */ - -#ifndef PNG_NO_CONST -# define PNG_CONST const -#else -# define PNG_CONST -#endif - -/* The following defines give you the ability to remove code from the - * library that you will not be using. I wish I could figure out how to - * automate this, but I can't do that without making it seriously hard - * on the users. So if you are not using an ability, change the #define - * to and #undef, and that part of the library will not be compiled. If - * your linker can't find a function, you may want to make sure the - * ability is defined here. Some of these depend upon some others being - * defined. I haven't figured out all the interactions here, so you may - * have to experiment awhile to get everything to compile. If you are - * creating or using a shared library, you probably shouldn't touch this, - * as it will affect the size of the structures, and this will cause bad - * things to happen if the library and/or application ever change. - */ - -/* Any features you will not be using can be undef'ed here */ - -/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user - * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS - * on the compile line, then pick and choose which ones to define without - * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED - * if you only want to have a png-compliant reader/writer but don't need - * any of the extra transformations. This saves about 80 kbytes in a - * typical installation of the library. (PNG_NO_* form added in version - * 1.0.1c, for consistency) - */ - -/* The size of the png_text structure changed in libpng-1.0.6 when - * iTXt support was added. iTXt support was turned off by default through - * libpng-1.2.x, to support old apps that malloc the png_text structure - * instead of calling png_set_text() and letting libpng malloc it. It - * was turned on by default in libpng-1.3.0. - */ - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -# ifndef PNG_NO_iTXt_SUPPORTED -# define PNG_NO_iTXt_SUPPORTED -# endif -# ifndef PNG_NO_READ_iTXt -# define PNG_NO_READ_iTXt -# endif -# ifndef PNG_NO_WRITE_iTXt -# define PNG_NO_WRITE_iTXt -# endif -#endif - -#if !defined(PNG_NO_iTXt_SUPPORTED) -# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) -# define PNG_READ_iTXt -# endif -# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) -# define PNG_WRITE_iTXt -# endif -#endif - -/* The following support, added after version 1.0.0, can be turned off here en - * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility - * with old applications that require the length of png_struct and png_info - * to remain unchanged. - */ - -#ifdef PNG_LEGACY_SUPPORTED -# define PNG_NO_FREE_ME -# define PNG_NO_READ_UNKNOWN_CHUNKS -# define PNG_NO_WRITE_UNKNOWN_CHUNKS -# define PNG_NO_READ_USER_CHUNKS -# define PNG_NO_READ_iCCP -# define PNG_NO_WRITE_iCCP -# define PNG_NO_READ_iTXt -# define PNG_NO_WRITE_iTXt -# define PNG_NO_READ_sCAL -# define PNG_NO_WRITE_sCAL -# define PNG_NO_READ_sPLT -# define PNG_NO_WRITE_sPLT -# define PNG_NO_INFO_IMAGE -# define PNG_NO_READ_RGB_TO_GRAY -# define PNG_NO_READ_USER_TRANSFORM -# define PNG_NO_WRITE_USER_TRANSFORM -# define PNG_NO_USER_MEM -# define PNG_NO_READ_EMPTY_PLTE -# define PNG_NO_MNG_FEATURES -# define PNG_NO_FIXED_POINT_SUPPORTED -#endif - -/* Ignore attempt to turn off both floating and fixed point support */ -#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ - !defined(PNG_NO_FIXED_POINT_SUPPORTED) -# define PNG_FIXED_POINT_SUPPORTED -#endif - -#ifndef PNG_NO_FREE_ME -# define PNG_FREE_ME_SUPPORTED -#endif - -#if defined(PNG_READ_SUPPORTED) - -#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ - !defined(PNG_NO_READ_TRANSFORMS) -# define PNG_READ_TRANSFORMS_SUPPORTED -#endif - -#ifdef PNG_READ_TRANSFORMS_SUPPORTED -# ifndef PNG_NO_READ_EXPAND -# define PNG_READ_EXPAND_SUPPORTED -# endif -# ifndef PNG_NO_READ_SHIFT -# define PNG_READ_SHIFT_SUPPORTED -# endif -# ifndef PNG_NO_READ_PACK -# define PNG_READ_PACK_SUPPORTED -# endif -# ifndef PNG_NO_READ_BGR -# define PNG_READ_BGR_SUPPORTED -# endif -# ifndef PNG_NO_READ_SWAP -# define PNG_READ_SWAP_SUPPORTED -# endif -# ifndef PNG_NO_READ_PACKSWAP -# define PNG_READ_PACKSWAP_SUPPORTED -# endif -# ifndef PNG_NO_READ_INVERT -# define PNG_READ_INVERT_SUPPORTED -# endif -# ifndef PNG_NO_READ_DITHER -# define PNG_READ_DITHER_SUPPORTED -# endif -# ifndef PNG_NO_READ_BACKGROUND -# define PNG_READ_BACKGROUND_SUPPORTED -# endif -# ifndef PNG_NO_READ_16_TO_8 -# define PNG_READ_16_TO_8_SUPPORTED -# endif -# ifndef PNG_NO_READ_FILLER -# define PNG_READ_FILLER_SUPPORTED -# endif -# ifndef PNG_NO_READ_GAMMA -# define PNG_READ_GAMMA_SUPPORTED -# endif -# ifndef PNG_NO_READ_GRAY_TO_RGB -# define PNG_READ_GRAY_TO_RGB_SUPPORTED -# endif -# ifndef PNG_NO_READ_SWAP_ALPHA -# define PNG_READ_SWAP_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_READ_INVERT_ALPHA -# define PNG_READ_INVERT_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_READ_STRIP_ALPHA -# define PNG_READ_STRIP_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_READ_USER_TRANSFORM -# define PNG_READ_USER_TRANSFORM_SUPPORTED -# endif -# ifndef PNG_NO_READ_RGB_TO_GRAY -# define PNG_READ_RGB_TO_GRAY_SUPPORTED -# endif -#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ - -#if !defined(PNG_NO_PROGRESSIVE_READ) && \ - !defined(PNG_PROGRESSIVE_READ_SUPPORTED) /* if you don't do progressive */ -# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ -#endif /* about interlacing capability! You'll */ - /* still have interlacing unless you change the following line: */ - -#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ - -#ifndef PNG_NO_READ_COMPOSITE_NODIV -# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ -# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ -# endif -#endif - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Deprecated, will be removed from version 2.0.0. - Use PNG_MNG_FEATURES_SUPPORTED instead. */ -#ifndef PNG_NO_READ_EMPTY_PLTE -# define PNG_READ_EMPTY_PLTE_SUPPORTED -#endif -#endif - -#endif /* PNG_READ_SUPPORTED */ - -#if defined(PNG_WRITE_SUPPORTED) - -# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ - !defined(PNG_NO_WRITE_TRANSFORMS) -# define PNG_WRITE_TRANSFORMS_SUPPORTED -#endif - -#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED -# ifndef PNG_NO_WRITE_SHIFT -# define PNG_WRITE_SHIFT_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_PACK -# define PNG_WRITE_PACK_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_BGR -# define PNG_WRITE_BGR_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_SWAP -# define PNG_WRITE_SWAP_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_PACKSWAP -# define PNG_WRITE_PACKSWAP_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_INVERT -# define PNG_WRITE_INVERT_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_FILLER -# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ -# endif -# ifndef PNG_NO_WRITE_SWAP_ALPHA -# define PNG_WRITE_SWAP_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_INVERT_ALPHA -# define PNG_WRITE_INVERT_ALPHA_SUPPORTED -# endif -# ifndef PNG_NO_WRITE_USER_TRANSFORM -# define PNG_WRITE_USER_TRANSFORM_SUPPORTED -# endif -#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ - -#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ - !defined(PNG_WRITE_INTERLACING_SUPPORTED) -#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant - encoders, but can cause trouble - if left undefined */ -#endif - -#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ - !defined(PNG_WRITE_WEIGHTED_FILTER) && \ - defined(PNG_FLOATING_POINT_SUPPORTED) -# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED -#endif - -#ifndef PNG_NO_WRITE_FLUSH -# define PNG_WRITE_FLUSH_SUPPORTED -#endif - -#if defined(PNG_1_0_X) || defined (PNG_1_2_X) -/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ -#ifndef PNG_NO_WRITE_EMPTY_PLTE -# define PNG_WRITE_EMPTY_PLTE_SUPPORTED -#endif -#endif - -#endif /* PNG_WRITE_SUPPORTED */ - -#ifndef PNG_1_0_X -# ifndef PNG_NO_ERROR_NUMBERS -# define PNG_ERROR_NUMBERS_SUPPORTED -# endif -#endif /* PNG_1_0_X */ - -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) -# ifndef PNG_NO_USER_TRANSFORM_PTR -# define PNG_USER_TRANSFORM_PTR_SUPPORTED -# endif -#endif - -#ifndef PNG_NO_STDIO -# define PNG_TIME_RFC1123_SUPPORTED -#endif - -/* This adds extra functions in pngget.c for accessing data from the - * info pointer (added in version 0.99) - * png_get_image_width() - * png_get_image_height() - * png_get_bit_depth() - * png_get_color_type() - * png_get_compression_type() - * png_get_filter_type() - * png_get_interlace_type() - * png_get_pixel_aspect_ratio() - * png_get_pixels_per_meter() - * png_get_x_offset_pixels() - * png_get_y_offset_pixels() - * png_get_x_offset_microns() - * png_get_y_offset_microns() - */ -#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) -# define PNG_EASY_ACCESS_SUPPORTED -#endif - -/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 - * and removed from version 1.2.20. The following will be removed - * from libpng-1.4.0 -*/ - -#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) -# ifndef PNG_OPTIMIZED_CODE_SUPPORTED -# define PNG_OPTIMIZED_CODE_SUPPORTED -# endif -#endif - -#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) -# ifndef PNG_ASSEMBLER_CODE_SUPPORTED -# define PNG_ASSEMBLER_CODE_SUPPORTED -# endif - -# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) - /* work around 64-bit gcc compiler bugs in gcc-3.x */ -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_NO_MMX_CODE + /* An older compiler, or one not detected (erroneously) above, + * if necessary override on the command line to get the correct + * variants for the compiler. + */ +# ifndef PNGCAPI +# define PNGCAPI _cdecl # endif -# endif - -# if defined(__APPLE__) -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_NO_MMX_CODE +# if PNG_API_RULE == 1 && !defined(PNGAPI) +# define PNGAPI _stdcall # endif +# endif /* compiler/api */ + /* NOTE: PNGCBAPI always defaults to PNGCAPI. */ + +# if defined(PNGAPI) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined if PNGAPI is changed" # endif -# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_NO_MMX_CODE +# if (defined(_MSC_VER) && _MSC_VER < 800) ||\ + (defined(__BORLANDC__) && __BORLANDC__ < 0x500) + /* older Borland and MSC + * compilers used '__export' and required this to be after + * the type. + */ +# ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) type PNG_IMPEXP # endif -# endif +# define PNG_DLL_EXPORT __export +# else /* newer compiler */ +# define PNG_DLL_EXPORT __declspec(dllexport) +# ifndef PNG_DLL_IMPORT +# define PNG_DLL_IMPORT __declspec(dllimport) +# endif +# endif /* compiler */ -# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) -# define PNG_MMX_CODE_SUPPORTED -# endif +#else /* !Windows */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# define PNGAPI _System +# else /* !Windows/x86 && !OS/2 */ + /* Use the defaults, or define PNG*API on the command line (but + * this will have to be done for every compile!) + */ +# endif /* other system, !OS/2 */ +#endif /* !Windows/x86 */ +/* Now do all the defaulting . */ +#ifndef PNGCAPI +# define PNGCAPI #endif -/* end of obsolete code to be removed from libpng-1.4.0 */ - -#if !defined(PNG_1_0_X) -#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) -# define PNG_USER_MEM_SUPPORTED +#ifndef PNGCBAPI +# define PNGCBAPI PNGCAPI #endif -#endif /* PNG_1_0_X */ - -/* Added at libpng-1.2.6 */ -#if !defined(PNG_1_0_X) -#ifndef PNG_SET_USER_LIMITS_SUPPORTED -#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) -# define PNG_SET_USER_LIMITS_SUPPORTED +#ifndef PNGAPI +# define PNGAPI PNGCAPI #endif -#endif -#endif /* PNG_1_0_X */ -/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter - * how large, set these limits to 0x7fffffffL +/* PNG_IMPEXP may be set on the compilation system command line or (if not set) + * then in an internal header file when building the library, otherwise (when + * using the library) it is set here. */ -#ifndef PNG_USER_WIDTH_MAX -# define PNG_USER_WIDTH_MAX 1000000L -#endif -#ifndef PNG_USER_HEIGHT_MAX -# define PNG_USER_HEIGHT_MAX 1000000L -#endif +#ifndef PNG_IMPEXP +# if defined(PNG_USE_DLL) && defined(PNG_DLL_IMPORT) + /* This forces use of a DLL, disallowing static linking */ +# define PNG_IMPEXP PNG_DLL_IMPORT +# 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 +# ifndef PNG_IMPEXP +# define PNG_IMPEXP # 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 +/* In 1.5.2 the definition of PNG_FUNCTION has been changed to always treat + * 'attributes' as a storage class - the attributes go at the start of the + * function definition, and attributes are always appended regardless of the + * compiler. This considerably simplifies these macros but may cause problems + * if any compilers both need function attributes and fail to handle them as + * a storage class (this is unlikely.) + */ +#ifndef PNG_FUNCTION +# define PNG_FUNCTION(type, name, args, attributes) attributes type name args #endif -*/ -/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ -/* -#define PNG_NO_POINTER_INDEXING -*/ +#ifndef PNG_EXPORT_TYPE +# define PNG_EXPORT_TYPE(type) PNG_IMPEXP type +#endif -/* These functions are turned off by default, as they will be phased out. */ -/* -#define PNG_USELESS_TESTS_SUPPORTED -#define PNG_CORRECT_PALETTE_SUPPORTED -*/ + /* The ordinal value is only relevant when preprocessing png.h for symbol + * table entries, so we discard it here. See the .dfn files in the + * scripts directory. + */ +#ifndef PNG_EXPORTA -/* 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. +# define PNG_EXPORTA(ordinal, type, name, args, attributes)\ + PNG_FUNCTION(PNG_EXPORT_TYPE(type),(PNGAPI name),PNGARG(args), \ + extern attributes) +#endif + +/* ANSI-C (C90) does not permit a macro to be invoked with an empty argument, + * so make something non-empty to satisfy the requirement: + */ +#define PNG_EMPTY /*empty list*/ + +#define PNG_EXPORT(ordinal, type, name, args)\ + PNG_EXPORTA(ordinal, type, name, args, PNG_EMPTY) + +/* Use PNG_REMOVED to comment out a removed interface. */ +#ifndef PNG_REMOVED +# define PNG_REMOVED(ordinal, type, name, args, attributes) +#endif + +#ifndef PNG_CALLBACK +# define PNG_CALLBACK(type, name, args) type (PNGCBAPI name) PNGARG(args) +#endif + +/* Support for compiler specific function attributes. These are used + * so that where compiler support is available incorrect use of API + * functions in png.h will generate compiler warnings. + * + * Added at libpng-1.2.41. */ -#if defined(PNG_READ_SUPPORTED) && \ - !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ - !defined(PNG_NO_READ_ANCILLARY_CHUNKS) -# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED -#endif - -#if defined(PNG_WRITE_SUPPORTED) && \ - !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ - !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) -# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED -#endif - -#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED - -#ifdef PNG_NO_READ_TEXT -# define PNG_NO_READ_iTXt -# define PNG_NO_READ_tEXt -# define PNG_NO_READ_zTXt -#endif -#ifndef PNG_NO_READ_bKGD -# define PNG_READ_bKGD_SUPPORTED -# define PNG_bKGD_SUPPORTED -#endif -#ifndef PNG_NO_READ_cHRM -# define PNG_READ_cHRM_SUPPORTED -# define PNG_cHRM_SUPPORTED -#endif -#ifndef PNG_NO_READ_gAMA -# define PNG_READ_gAMA_SUPPORTED -# define PNG_gAMA_SUPPORTED -#endif -#ifndef PNG_NO_READ_hIST -# define PNG_READ_hIST_SUPPORTED -# define PNG_hIST_SUPPORTED -#endif -#ifndef PNG_NO_READ_iCCP -# define PNG_READ_iCCP_SUPPORTED -# define PNG_iCCP_SUPPORTED -#endif -#ifndef PNG_NO_READ_iTXt -# ifndef PNG_READ_iTXt_SUPPORTED -# define PNG_READ_iTXt_SUPPORTED -# endif -# ifndef PNG_iTXt_SUPPORTED -# define PNG_iTXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_READ_oFFs -# define PNG_READ_oFFs_SUPPORTED -# define PNG_oFFs_SUPPORTED -#endif -#ifndef PNG_NO_READ_pCAL -# define PNG_READ_pCAL_SUPPORTED -# define PNG_pCAL_SUPPORTED -#endif -#ifndef PNG_NO_READ_sCAL -# define PNG_READ_sCAL_SUPPORTED -# define PNG_sCAL_SUPPORTED -#endif -#ifndef PNG_NO_READ_pHYs -# define PNG_READ_pHYs_SUPPORTED -# define PNG_pHYs_SUPPORTED -#endif -#ifndef PNG_NO_READ_sBIT -# define PNG_READ_sBIT_SUPPORTED -# define PNG_sBIT_SUPPORTED -#endif -#ifndef PNG_NO_READ_sPLT -# define PNG_READ_sPLT_SUPPORTED -# define PNG_sPLT_SUPPORTED -#endif -#ifndef PNG_NO_READ_sRGB -# define PNG_READ_sRGB_SUPPORTED -# define PNG_sRGB_SUPPORTED -#endif -#ifndef PNG_NO_READ_tEXt -# define PNG_READ_tEXt_SUPPORTED -# define PNG_tEXt_SUPPORTED -#endif -#ifndef PNG_NO_READ_tIME -# define PNG_READ_tIME_SUPPORTED -# define PNG_tIME_SUPPORTED -#endif -#ifndef PNG_NO_READ_tRNS -# define PNG_READ_tRNS_SUPPORTED -# define PNG_tRNS_SUPPORTED -#endif -#ifndef PNG_NO_READ_zTXt -# define PNG_READ_zTXt_SUPPORTED -# define PNG_zTXt_SUPPORTED -#endif -#ifndef PNG_NO_READ_UNKNOWN_CHUNKS -# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED -# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED -# define PNG_UNKNOWN_CHUNKS_SUPPORTED -# endif -# ifndef PNG_NO_HANDLE_AS_UNKNOWN -# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# endif -#endif -#if !defined(PNG_NO_READ_USER_CHUNKS) && \ - defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) -# define PNG_READ_USER_CHUNKS_SUPPORTED -# define PNG_USER_CHUNKS_SUPPORTED -# ifdef PNG_NO_READ_UNKNOWN_CHUNKS -# undef PNG_NO_READ_UNKNOWN_CHUNKS -# endif -# ifdef PNG_NO_HANDLE_AS_UNKNOWN -# undef PNG_NO_HANDLE_AS_UNKNOWN -# endif -#endif -#ifndef PNG_NO_READ_OPT_PLTE -# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ -#endif /* optional PLTE chunk in RGB and RGBA images */ -#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ - defined(PNG_READ_zTXt_SUPPORTED) -# define PNG_READ_TEXT_SUPPORTED -# define PNG_TEXT_SUPPORTED -#endif - -#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ - -#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED - -#ifdef PNG_NO_WRITE_TEXT -# define PNG_NO_WRITE_iTXt -# define PNG_NO_WRITE_tEXt -# define PNG_NO_WRITE_zTXt -#endif -#ifndef PNG_NO_WRITE_bKGD -# define PNG_WRITE_bKGD_SUPPORTED -# ifndef PNG_bKGD_SUPPORTED -# define PNG_bKGD_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_cHRM -# define PNG_WRITE_cHRM_SUPPORTED -# ifndef PNG_cHRM_SUPPORTED -# define PNG_cHRM_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_gAMA -# define PNG_WRITE_gAMA_SUPPORTED -# ifndef PNG_gAMA_SUPPORTED -# define PNG_gAMA_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_hIST -# define PNG_WRITE_hIST_SUPPORTED -# ifndef PNG_hIST_SUPPORTED -# define PNG_hIST_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_iCCP -# define PNG_WRITE_iCCP_SUPPORTED -# ifndef PNG_iCCP_SUPPORTED -# define PNG_iCCP_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_iTXt -# ifndef PNG_WRITE_iTXt_SUPPORTED -# define PNG_WRITE_iTXt_SUPPORTED -# endif -# ifndef PNG_iTXt_SUPPORTED -# define PNG_iTXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_oFFs -# define PNG_WRITE_oFFs_SUPPORTED -# ifndef PNG_oFFs_SUPPORTED -# define PNG_oFFs_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_pCAL -# define PNG_WRITE_pCAL_SUPPORTED -# ifndef PNG_pCAL_SUPPORTED -# define PNG_pCAL_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sCAL -# define PNG_WRITE_sCAL_SUPPORTED -# ifndef PNG_sCAL_SUPPORTED -# define PNG_sCAL_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_pHYs -# define PNG_WRITE_pHYs_SUPPORTED -# ifndef PNG_pHYs_SUPPORTED -# define PNG_pHYs_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sBIT -# define PNG_WRITE_sBIT_SUPPORTED -# ifndef PNG_sBIT_SUPPORTED -# define PNG_sBIT_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sPLT -# define PNG_WRITE_sPLT_SUPPORTED -# ifndef PNG_sPLT_SUPPORTED -# define PNG_sPLT_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_sRGB -# define PNG_WRITE_sRGB_SUPPORTED -# ifndef PNG_sRGB_SUPPORTED -# define PNG_sRGB_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_tEXt -# define PNG_WRITE_tEXt_SUPPORTED -# ifndef PNG_tEXt_SUPPORTED -# define PNG_tEXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_tIME -# define PNG_WRITE_tIME_SUPPORTED -# ifndef PNG_tIME_SUPPORTED -# define PNG_tIME_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_tRNS -# define PNG_WRITE_tRNS_SUPPORTED -# ifndef PNG_tRNS_SUPPORTED -# define PNG_tRNS_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_zTXt -# define PNG_WRITE_zTXt_SUPPORTED -# ifndef PNG_zTXt_SUPPORTED -# define PNG_zTXt_SUPPORTED -# endif -#endif -#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS -# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED -# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED -# define PNG_UNKNOWN_CHUNKS_SUPPORTED -# endif -# ifndef PNG_NO_HANDLE_AS_UNKNOWN -# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED -# endif -# endif -#endif -#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ - defined(PNG_WRITE_zTXt_SUPPORTED) -# define PNG_WRITE_TEXT_SUPPORTED -# ifndef PNG_TEXT_SUPPORTED -# define PNG_TEXT_SUPPORTED +#ifndef PNG_NO_PEDANTIC_WARNINGS +# ifndef PNG_PEDANTIC_WARNINGS_SUPPORTED +# define PNG_PEDANTIC_WARNINGS_SUPPORTED # endif #endif -#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ - -/* Turn this off to disable png_read_png() and - * png_write_png() and leave the row_pointers member - * out of the info structure. - */ -#ifndef PNG_NO_INFO_IMAGE -# define PNG_INFO_IMAGE_SUPPORTED -#endif - -/* need the time information for reading tIME chunks */ -#if defined(PNG_tIME_SUPPORTED) -# if !defined(_WIN32_WCE) - /* "time.h" functions are not supported on WindowsCE */ -# include -# endif -#endif - -/* Some typedefs to get us started. These should be safe on most of the - * common platforms. The typedefs should be at least as large as the - * numbers suggest (a png_uint_32 must be at least 32 bits long), but they - * don't have to be exactly that size. Some compilers dislike passing - * unsigned shorts as function parameters, so you may be better off using - * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may - * want to have unsigned int for png_uint_32 instead of unsigned long. - */ - -typedef unsigned long png_uint_32; -typedef long png_int_32; -typedef unsigned short png_uint_16; -typedef short png_int_16; -typedef unsigned char png_byte; - -/* This is usually size_t. It is typedef'ed just in case you need it to - change (I'm not sure if you will or not, so I thought I'd be safe) */ -#ifdef PNG_SIZE_T - typedef PNG_SIZE_T png_size_t; -# define png_sizeof(x) png_convert_size(sizeof (x)) -#else - typedef size_t png_size_t; -# define png_sizeof(x) sizeof (x) -#endif - -/* The following is needed for medium model support. It cannot be in the - * PNG_INTERNAL section. Needs modification for other compilers besides - * MSC. Model independent support declares all arrays and pointers to be - * large using the far keyword. The zlib version used must also support - * model independent data. As of version zlib 1.0.4, the necessary changes - * have been made in zlib. The USE_FAR_KEYWORD define triggers other - * changes that are needed. (Tim Wegner) - */ - -/* Separate compiler dependencies (problem here is that zlib.h always - defines FAR. (SJT) */ -#ifdef __BORLANDC__ -# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) -# define LDATA 1 -# else -# define LDATA 0 -# endif - /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ -# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) -# define PNG_MAX_MALLOC_64K -# if (LDATA != 1) -# ifndef FAR -# define FAR __far +#ifdef PNG_PEDANTIC_WARNINGS_SUPPORTED + /* Support for compiler specific function attributes. These are used + * so that where compiler support is available, incorrect use of API + * functions in png.h will generate compiler warnings. Added at libpng + * version 1.2.41. Disabling these removes the warnings but may also produce + * less efficient code. + */ +# if defined(__GNUC__) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT __attribute__((__warn_unused_result__)) +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __attribute__((__noreturn__)) +# endif +# if __GNUC__ >= 3 +# ifndef PNG_ALLOCATED +# define PNG_ALLOCATED __attribute__((__malloc__)) # 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__ */ +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __attribute__((__deprecated__)) +# endif +# ifndef PNG_PRIVATE +# if 0 /* Doesn't work so we use deprecated instead*/ +# define PNG_PRIVATE \ + __attribute__((warning("This function is not exported by libpng."))) +# else +# define PNG_PRIVATE \ + __attribute__((__deprecated__)) +# endif +# endif +# if ((__GNUC__ != 3) || !defined(__GNUC_MINOR__) || (__GNUC_MINOR__ >= 1)) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* __GNUC__ == 3.0 */ +# endif /* __GNUC__ >= 3 */ +# elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* not supported */ +# endif +# ifndef PNG_NORETURN +# define PNG_NORETURN __declspec(noreturn) +# endif +# ifndef PNG_ALLOCATED +# if (_MSC_VER >= 1400) +# define PNG_ALLOCATED __declspec(restrict) +# endif +# endif +# ifndef PNG_DEPRECATED +# define PNG_DEPRECATED __declspec(deprecated) +# endif +# ifndef PNG_PRIVATE +# define PNG_PRIVATE __declspec(deprecated) +# endif +# ifndef PNG_RESTRICT +# if (_MSC_VER >= 1400) +# define PNG_RESTRICT __restrict +# endif +# endif -/* 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) - */ +# elif defined(__WATCOMC__) +# ifndef PNG_RESTRICT +# define PNG_RESTRICT __restrict +# endif +# endif /* _MSC_VER */ +#endif /* PNG_PEDANTIC_WARNINGS */ -/* MSC Medium model */ -#if defined(FAR) -# if defined(M_I86MM) -# define USE_FAR_KEYWORD -# define FARDATA FAR -# include +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED /* Use of this function is deprecated */ +#endif +#ifndef PNG_USE_RESULT +# define PNG_USE_RESULT /* The result of this function must be checked */ +#endif +#ifndef PNG_NORETURN +# define PNG_NORETURN /* This function does not return */ +#endif +#ifndef PNG_ALLOCATED +# define PNG_ALLOCATED /* The result of the function is new memory */ +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE /* This is a private libpng function */ +#endif +#ifndef PNG_RESTRICT +# define PNG_RESTRICT /* The C99 "restrict" feature */ +#endif +#ifndef PNG_FP_EXPORT /* A floating point API. */ +# ifdef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No floating point APIs */ +# define PNG_FP_EXPORT(ordinal, type, name, args) +# endif +#endif +#ifndef PNG_FIXED_EXPORT /* A fixed point API. */ +# ifdef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_EXPORT(ordinal, type, name, args); +# else /* No fixed point APIs */ +# define PNG_FIXED_EXPORT(ordinal, type, name, args) # endif #endif -/* SJT: default case */ -#ifndef FAR -# define FAR +#ifndef PNG_BUILDING_SYMBOL_TABLE +/* Some typedefs to get us started. These should be safe on most of the common + * platforms. + * + * png_uint_32 and png_int_32 may, currently, be larger than required to hold a + * 32-bit value however this is not normally advisable. + * + * png_uint_16 and png_int_16 should always be two bytes in size - this is + * verified at library build time. + * + * png_byte must always be one byte in size. + * + * The checks below use constants from limits.h, as defined by the ISOC90 + * standard. + */ +#if CHAR_BIT == 8 && UCHAR_MAX == 255 + typedef unsigned char png_byte; +#else +# error "libpng requires 8 bit bytes" #endif -/* At this point FAR is always defined */ -#ifndef FARDATA -# define FARDATA +#if INT_MIN == -32768 && INT_MAX == 32767 + typedef int png_int_16; +#elif SHRT_MIN == -32768 && SHRT_MAX == 32767 + typedef short png_int_16; +#else +# error "libpng requires a signed 16 bit type" #endif -/* Typedef for floating-point numbers that are converted - to fixed-point with a multiple of 100,000, e.g., int_gamma */ +#if UINT_MAX == 65535 + typedef unsigned int png_uint_16; +#elif USHRT_MAX == 65535 + typedef unsigned short png_uint_16; +#else +# error "libpng requires an unsigned 16 bit type" +#endif + +#if INT_MIN < -2147483646 && INT_MAX > 2147483646 + typedef int png_int_32; +#elif LONG_MIN < -2147483646 && LONG_MAX > 2147483646 + typedef long int png_int_32; +#else +# error "libpng requires a signed 32 bit (or more) type" +#endif + +#if UINT_MAX > 4294967294 + typedef unsigned int png_uint_32; +#elif ULONG_MAX > 4294967294 + typedef unsigned long int png_uint_32; +#else +# error "libpng requires an unsigned 32 bit (or more) type" +#endif + +/* Prior to 1.6.0 it was possible to disable the use of size_t, 1.6.0, however, + * requires an ISOC90 compiler and relies on consistent behavior of sizeof. + */ +typedef size_t png_size_t; +typedef ptrdiff_t png_ptrdiff_t; + +/* libpng needs to know the maximum value of 'size_t' and this controls the + * definition of png_alloc_size_t, below. This maximum value of size_t limits + * but does not control the maximum allocations the library makes - there is + * direct application control of this through png_set_user_limits(). + */ +#ifndef PNG_SMALL_SIZE_T + /* Compiler specific tests for systems where size_t is known to be less than + * 32 bits (some of these systems may no longer work because of the lack of + * 'far' support; see above.) + */ +# if (defined(__TURBOC__) && !defined(__FLAT__)) ||\ + (defined(_MSC_VER) && defined(MAXSEG_64K)) +# define PNG_SMALL_SIZE_T +# endif +#endif + +/* png_alloc_size_t is guaranteed to be no smaller than png_size_t, and no + * smaller than png_uint_32. Casts from png_size_t or png_uint_32 to + * png_alloc_size_t are not necessary; in fact, it is recommended not to use + * them at all so that the compiler can complain when something turns out to be + * problematic. + * + * Casts in the other direction (from png_alloc_size_t to png_size_t or + * png_uint_32) should be explicitly applied; however, we do not expect to + * encounter practical situations that require such conversions. + * + * PNG_SMALL_SIZE_T must be defined if the maximum value of size_t is less than + * 4294967295 - i.e. less than the maximum value of png_uint_32. + */ +#ifdef PNG_SMALL_SIZE_T + typedef png_uint_32 png_alloc_size_t; +#else + typedef png_size_t png_alloc_size_t; +#endif + +/* Prior to 1.6.0 libpng offered limited support for Microsoft C compiler + * implementations of Intel CPU specific support of user-mode segmented address + * spaces, where 16-bit pointers address more than 65536 bytes of memory using + * separate 'segment' registers. The implementation requires two different + * types of pointer (only one of which includes the segment value.) + * + * If required this support is available in version 1.2 of libpng and may be + * available in versions through 1.5, although the correctness of the code has + * not been verified recently. + */ + +/* Typedef for floating-point numbers that are converted to fixed-point with a + * multiple of 100,000, e.g., gamma + */ typedef png_int_32 png_fixed_point; /* Add typedefs for pointers */ -typedef void FAR * png_voidp; -typedef png_byte FAR * png_bytep; -typedef png_uint_32 FAR * png_uint_32p; -typedef png_int_32 FAR * png_int_32p; -typedef png_uint_16 FAR * png_uint_16p; -typedef png_int_16 FAR * png_int_16p; -typedef PNG_CONST char FAR * png_const_charp; -typedef char FAR * png_charp; -typedef png_fixed_point FAR * png_fixed_point_p; +typedef void * png_voidp; +typedef const void * png_const_voidp; +typedef png_byte * png_bytep; +typedef const png_byte * png_const_bytep; +typedef png_uint_32 * png_uint_32p; +typedef const png_uint_32 * png_const_uint_32p; +typedef png_int_32 * png_int_32p; +typedef const png_int_32 * png_const_int_32p; +typedef png_uint_16 * png_uint_16p; +typedef const png_uint_16 * png_const_uint_16p; +typedef png_int_16 * png_int_16p; +typedef const png_int_16 * png_const_int_16p; +typedef char * png_charp; +typedef const char * png_const_charp; +typedef png_fixed_point * png_fixed_point_p; +typedef const png_fixed_point * png_const_fixed_point_p; +typedef png_size_t * png_size_tp; +typedef const png_size_t * png_const_size_tp; -#ifndef PNG_NO_STDIO -#if defined(_WIN32_WCE) -typedef HANDLE png_FILE_p; -#else -typedef FILE * png_FILE_p; -#endif +#ifdef PNG_STDIO_SUPPORTED +typedef FILE * png_FILE_p; #endif #ifdef PNG_FLOATING_POINT_SUPPORTED -typedef double FAR * png_doublep; +typedef double * png_doublep; +typedef const double * png_const_doublep; #endif /* Pointers to pointers; i.e. arrays */ -typedef png_byte FAR * FAR * png_bytepp; -typedef png_uint_32 FAR * FAR * png_uint_32pp; -typedef png_int_32 FAR * FAR * png_int_32pp; -typedef png_uint_16 FAR * FAR * png_uint_16pp; -typedef png_int_16 FAR * FAR * png_int_16pp; -typedef PNG_CONST char FAR * FAR * png_const_charpp; -typedef char FAR * FAR * png_charpp; -typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +typedef png_byte * * png_bytepp; +typedef png_uint_32 * * png_uint_32pp; +typedef png_int_32 * * png_int_32pp; +typedef png_uint_16 * * png_uint_16pp; +typedef png_int_16 * * png_int_16pp; +typedef const char * * png_const_charpp; +typedef char * * png_charpp; +typedef png_fixed_point * * png_fixed_point_pp; #ifdef PNG_FLOATING_POINT_SUPPORTED -typedef double FAR * FAR * png_doublepp; +typedef double * * png_doublepp; #endif /* Pointers to pointers to pointers; i.e., pointer to array */ -typedef char FAR * FAR * FAR * png_charppp; +typedef char * * * png_charppp; -#if 0 -/* SPC - Is this stuff deprecated? */ -/* It'll be removed as of libpng-1.3.0 - GR-P */ -/* libpng typedefs for types in zlib. If zlib changes - * or another compression library is used, then change these. - * Eliminates need to change all the source files. - */ -typedef charf * png_zcharp; -typedef charf * FAR * png_zcharpp; -typedef z_stream FAR * png_zstreamp; -#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ - -/* - * Define PNG_BUILD_DLL if the module being built is a Windows - * LIBPNG DLL. - * - * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. - * It is equivalent to Microsoft predefined macro _DLL that is - * automatically defined when you compile using the share - * version of the CRT (C Run-Time library) - * - * The cygwin mods make this behavior a little different: - * Define PNG_BUILD_DLL if you are building a dll for use with cygwin - * Define PNG_STATIC if you are building a static library for use with cygwin, - * -or- if you are building an application that you want to link to the - * static library. - * PNG_USE_DLL is defined by default (no user action needed) unless one of - * the other flags is defined. - */ - -#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) -# define PNG_DLL -#endif -/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. - * When building a static lib, default to no GLOBAL ARRAYS, but allow - * command-line override - */ -#if defined(__CYGWIN__) -# if !defined(PNG_STATIC) -# if defined(PNG_USE_GLOBAL_ARRAYS) -# undef PNG_USE_GLOBAL_ARRAYS -# endif -# if !defined(PNG_USE_LOCAL_ARRAYS) -# define PNG_USE_LOCAL_ARRAYS -# endif -# else -# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) -# if defined(PNG_USE_GLOBAL_ARRAYS) -# undef PNG_USE_GLOBAL_ARRAYS -# endif -# endif -# endif -# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) -# define PNG_USE_LOCAL_ARRAYS -# endif -#endif - -/* Do not use global arrays (helps with building DLL's) - * They are no longer used in libpng itself, since version 1.0.5c, - * but might be required for some pre-1.0.5c applications. - */ -#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) -# if defined(PNG_NO_GLOBAL_ARRAYS) || \ - (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) -# define PNG_USE_LOCAL_ARRAYS -# else -# define PNG_USE_GLOBAL_ARRAYS -# endif -#endif - -#if defined(__CYGWIN__) -# undef PNGAPI -# define PNGAPI __cdecl -# undef PNG_IMPEXP -# define PNG_IMPEXP -#endif - -/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", - * you may get warnings regarding the linkage of png_zalloc and png_zfree. - * Don't ignore those warnings; you must also reset the default calling - * convention in your compiler to match your PNGAPI, and you must build - * zlib and your applications the same way you build libpng. - */ - -#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) -# ifndef PNG_NO_MODULEDEF -# define PNG_NO_MODULEDEF -# endif -#endif - -#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) -# define PNG_IMPEXP -#endif - -#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ - (( defined(_Windows) || defined(_WINDOWS) || \ - defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) - -# ifndef PNGAPI -# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) -# define PNGAPI __cdecl -# else -# define PNGAPI _cdecl -# endif -# endif - -# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ - 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) -# define PNG_IMPEXP -# endif - -# if !defined(PNG_IMPEXP) - -# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol -# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol - - /* Borland/Microsoft */ -# if defined(_MSC_VER) || defined(__BORLANDC__) -# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) -# define PNG_EXPORT PNG_EXPORT_TYPE1 -# else -# define PNG_EXPORT PNG_EXPORT_TYPE2 -# if defined(PNG_BUILD_DLL) -# define PNG_IMPEXP __export -# else -# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in - VC++ */ -# endif /* Exists in Borland C++ for - C++ classes (== huge) */ -# endif -# endif - -# if !defined(PNG_IMPEXP) -# if defined(PNG_BUILD_DLL) -# define PNG_IMPEXP __declspec(dllexport) -# else -# define PNG_IMPEXP __declspec(dllimport) -# endif -# endif -# endif /* PNG_IMPEXP */ -#else /* !(DLL || non-cygwin WINDOWS) */ -# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) -# ifndef PNGAPI -# define PNGAPI _System -# endif -# else -# if 0 /* ... other platforms, with other meanings */ -# endif -# endif -#endif - -#ifndef PNGAPI -# define PNGAPI -#endif -#ifndef PNG_IMPEXP -# define PNG_IMPEXP -#endif - -#ifdef PNG_BUILDSYMS -# ifndef PNG_EXPORT -# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END -# endif -# ifdef PNG_USE_GLOBAL_ARRAYS -# ifndef PNG_EXPORT_VAR -# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT -# endif -# endif -#endif - -#ifndef PNG_EXPORT -# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol -#endif - -#ifdef PNG_USE_GLOBAL_ARRAYS -# ifndef PNG_EXPORT_VAR -# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type -# endif -#endif - -/* User may want to use these so they are not in PNG_INTERNAL. Any library - * functions that are passed far data must be model independent. - */ - -#ifndef PNG_ABORT -# define PNG_ABORT() abort() -#endif - -#ifdef PNG_SETJMP_SUPPORTED -# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) -#else -# define png_jmpbuf(png_ptr) \ - (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) -#endif - -#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ -/* use this to make far-to-near assignments */ -# define CHECK 1 -# define NOCHECK 0 -# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) -# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) -# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ -# define png_strcpy _fstrcpy -# define png_strncpy _fstrncpy /* Added to v 1.2.6 */ -# define png_strlen _fstrlen -# define png_memcmp _fmemcmp /* SJT: added */ -# define png_memcpy _fmemcpy -# define png_memset _fmemset -#else /* use the usual functions */ -# define CVT_PTR(ptr) (ptr) -# define CVT_PTR_NOCHECK(ptr) (ptr) -# ifndef PNG_NO_SNPRINTF -# ifdef _MSC_VER -# define png_snprintf _snprintf /* Added to v 1.2.19 */ -# define png_snprintf2 _snprintf -# define png_snprintf6 _snprintf -# else -# define png_snprintf snprintf /* Added to v 1.2.19 */ -# define png_snprintf2 snprintf -# define png_snprintf6 snprintf -# endif -# else - /* You don't have or don't want to use snprintf(). Caution: Using - * sprintf instead of snprintf exposes your application to accidental - * or malevolent buffer overflows. If you don't have snprintf() - * as a general rule you should provide one (you can get one from - * Portable OpenSSH). */ -# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) -# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) -# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ - sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) -# endif -# define png_strcpy strcpy -# define png_strncpy strncpy /* Added to v 1.2.6 */ -# define png_strlen strlen -# define png_memcmp memcmp /* SJT: added */ -# define png_memcpy memcpy -# define png_memset memset -#endif -/* End of memory model independent support */ - -/* Just a little check that someone hasn't tried to define something - * contradictory. - */ -#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) -# undef PNG_ZBUF_SIZE -# define PNG_ZBUF_SIZE 65536L -#endif - -/* Added at libpng-1.2.8 */ -#endif /* PNG_VERSION_INFO_ONLY */ +#endif /* PNG_BUILDING_SYMBOL_TABLE */ #endif /* PNGCONF_H */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngerror.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngerror.c index 70336bd..f40cc83 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngerror.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngerror.c @@ -1,128 +1,407 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * This file provides a location for all error handling. Users who * need special error handling are expected to write replacement functions * and use png_set_error_fn() to use those functions. See the instructions * at each function. */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +static PNG_FUNCTION(void, png_default_error,PNGARG((png_const_structrp png_ptr, + png_const_charp error_message)),PNG_NORETURN); + +#ifdef PNG_WARNINGS_SUPPORTED static void /* PRIVATE */ -png_default_error PNGARG((png_structp png_ptr, - png_const_charp error_message)); -#ifndef PNG_NO_WARNINGS -static void /* PRIVATE */ -png_default_warning PNGARG((png_structp png_ptr, - png_const_charp warning_message)); -#endif /* PNG_NO_WARNINGS */ +png_default_warning PNGARG((png_const_structrp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_WARNINGS_SUPPORTED */ /* This function is called whenever there is a fatal error. This function * should not be changed. If there is a need to handle errors differently, * you should supply a replacement error function and use png_set_error_fn() * to replace the error function at run-time. */ -#ifndef PNG_NO_ERROR_TEXT -void PNGAPI -png_error(png_structp png_ptr, png_const_charp error_message) +#ifdef PNG_ERROR_TEXT_SUPPORTED +PNG_FUNCTION(void,PNGAPI +png_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) { #ifdef PNG_ERROR_NUMBERS_SUPPORTED char msg[16]; if (png_ptr != NULL) { - if (png_ptr->flags& - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) - { - if (*error_message == '#') - { - int offset; - for (offset=1; offset<15; offset++) - if (*(error_message+offset) == ' ') + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == PNG_LITERAL_SHARP) + { + /* Strip "#nnnn " from beginning of error message. */ + int offset; + for (offset = 1; offset<15; offset++) + if (error_message[offset] == ' ') break; - if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) - { - int i; - for (i=0; iflags&PNG_FLAG_STRIP_ERROR_TEXT) - { - msg[0]='0'; - msg[1]='\0'; - error_message=msg; - } + + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i = 0; i < offset - 1; i++) + msg[i] = error_message[i + 1]; + msg[i - 1] = '\0'; + error_message = msg; + } + + else + error_message += offset; + } + + else + { + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0] = '0'; + msg[1] = '\0'; + error_message = msg; + } } } } #endif if (png_ptr != NULL && png_ptr->error_fn != NULL) - (*(png_ptr->error_fn))(png_ptr, error_message); + (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), + error_message); /* If the custom handler doesn't exist, or if it returns, use the default handler, which will not return. */ png_default_error(png_ptr, error_message); } #else -void PNGAPI -png_err(png_structp png_ptr) +PNG_FUNCTION(void,PNGAPI +png_err,(png_const_structrp png_ptr),PNG_NORETURN) { + /* Prior to 1.5.2 the error_fn received a NULL pointer, expressed + * erroneously as '\0', instead of the empty string "". This was + * apparently an error, introduced in libpng-1.2.20, and png_default_error + * will crash in this case. + */ if (png_ptr != NULL && png_ptr->error_fn != NULL) - (*(png_ptr->error_fn))(png_ptr, '\0'); + (*(png_ptr->error_fn))(png_constcast(png_structrp,png_ptr), ""); /* 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'); + png_default_error(png_ptr, ""); } -#endif /* PNG_NO_ERROR_TEXT */ +#endif /* PNG_ERROR_TEXT_SUPPORTED */ -#ifndef PNG_NO_WARNINGS +/* Utility to safely appends strings to a buffer. This never errors out so + * error checking is not required in the caller. + */ +size_t +png_safecat(png_charp buffer, size_t bufsize, size_t pos, + png_const_charp string) +{ + if (buffer != NULL && pos < bufsize) + { + if (string != NULL) + while (*string != '\0' && pos < bufsize-1) + buffer[pos++] = *string++; + + buffer[pos] = '\0'; + } + + return pos; +} + +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED) +/* Utility to dump an unsigned value into a buffer, given a start pointer and + * and end pointer (which should point just *beyond* the end of the buffer!) + * Returns the pointer to the start of the formatted string. + */ +png_charp +png_format_number(png_const_charp start, png_charp end, int format, + png_alloc_size_t number) +{ + int count = 0; /* number of digits output */ + int mincount = 1; /* minimum number required */ + int output = 0; /* digit output (for the fixed point format) */ + + *--end = '\0'; + + /* This is written so that the loop always runs at least once, even with + * number zero. + */ + while (end > start && (number != 0 || count < mincount)) + { + + static const char digits[] = "0123456789ABCDEF"; + + switch (format) + { + case PNG_NUMBER_FORMAT_fixed: + /* Needs five digits (the fraction) */ + mincount = 5; + if (output || number % 10 != 0) + { + *--end = digits[number % 10]; + output = 1; + } + number /= 10; + break; + + case PNG_NUMBER_FORMAT_02u: + /* Expects at least 2 digits. */ + mincount = 2; + /* FALL THROUGH */ + + case PNG_NUMBER_FORMAT_u: + *--end = digits[number % 10]; + number /= 10; + break; + + case PNG_NUMBER_FORMAT_02x: + /* This format expects at least two digits */ + mincount = 2; + /* FALL THROUGH */ + + case PNG_NUMBER_FORMAT_x: + *--end = digits[number & 0xf]; + number >>= 4; + break; + + default: /* an error */ + number = 0; + break; + } + + /* Keep track of the number of digits added */ + ++count; + + /* Float a fixed number here: */ + if (format == PNG_NUMBER_FORMAT_fixed) if (count == 5) if (end > start) + { + /* End of the fraction, but maybe nothing was output? In that case + * drop the decimal point. If the number is a true zero handle that + * here. + */ + if (output) + *--end = '.'; + else if (number == 0) /* and !output */ + *--end = '0'; + } + } + + return end; +} +#endif + +#ifdef PNG_WARNINGS_SUPPORTED /* This function is called whenever there is a non-fatal error. This function * should not be changed. If there is a need to handle warnings differently, * you should supply a replacement warning function and use * png_set_error_fn() to replace the warning function at run-time. */ void PNGAPI -png_warning(png_structp png_ptr, png_const_charp warning_message) +png_warning(png_const_structrp png_ptr, png_const_charp warning_message) { int offset = 0; if (png_ptr != NULL) { #ifdef PNG_ERROR_NUMBERS_SUPPORTED if (png_ptr->flags& - (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) #endif - { - if (*warning_message == '#') - { - for (offset=1; offset<15; offset++) - if (*(warning_message+offset) == ' ') + { + if (*warning_message == PNG_LITERAL_SHARP) + { + for (offset = 1; offset < 15; offset++) + if (warning_message[offset] == ' ') break; - } - } - if (png_ptr != NULL && png_ptr->warning_fn != NULL) - (*(png_ptr->warning_fn))(png_ptr, warning_message+offset); + } + } } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_constcast(png_structrp,png_ptr), + warning_message + offset); else - png_default_warning(png_ptr, warning_message+offset); + png_default_warning(png_ptr, warning_message + offset); } -#endif /* PNG_NO_WARNINGS */ +/* These functions support 'formatted' warning messages with up to + * PNG_WARNING_PARAMETER_COUNT parameters. In the format string the parameter + * is introduced by @, where 'number' starts at 1. This follows the + * standard established by X/Open for internationalizable error messages. + */ +void +png_warning_parameter(png_warning_parameters p, int number, + png_const_charp string) +{ + if (number > 0 && number <= PNG_WARNING_PARAMETER_COUNT) + (void)png_safecat(p[number-1], (sizeof p[number-1]), 0, string); +} + +void +png_warning_parameter_unsigned(png_warning_parameters p, int number, int format, + png_alloc_size_t value) +{ + char buffer[PNG_NUMBER_BUFFER_SIZE]; + png_warning_parameter(p, number, PNG_FORMAT_NUMBER(buffer, format, value)); +} + +void +png_warning_parameter_signed(png_warning_parameters p, int number, int format, + png_int_32 value) +{ + png_alloc_size_t u; + png_charp str; + char buffer[PNG_NUMBER_BUFFER_SIZE]; + + /* Avoid overflow by doing the negate in a png_alloc_size_t: */ + u = (png_alloc_size_t)value; + if (value < 0) + u = ~u + 1; + + str = PNG_FORMAT_NUMBER(buffer, format, u); + + if (value < 0 && str > buffer) + *--str = '-'; + + png_warning_parameter(p, number, str); +} + +void +png_formatted_warning(png_const_structrp png_ptr, png_warning_parameters p, + png_const_charp message) +{ + /* The internal buffer is just 192 bytes - enough for all our messages, + * overflow doesn't happen because this code checks! If someone figures + * out how to send us a message longer than 192 bytes, all that will + * happen is that the message will be truncated appropriately. + */ + size_t i = 0; /* Index in the msg[] buffer: */ + char msg[192]; + + /* Each iteration through the following loop writes at most one character + * to msg[i++] then returns here to validate that there is still space for + * the trailing '\0'. It may (in the case of a parameter) read more than + * one character from message[]; it must check for '\0' and continue to the + * test if it finds the end of string. + */ + while (i<(sizeof msg)-1 && *message != '\0') + { + /* '@' at end of string is now just printed (previously it was skipped); + * it is an error in the calling code to terminate the string with @. + */ + if (p != NULL && *message == '@' && message[1] != '\0') + { + int parameter_char = *++message; /* Consume the '@' */ + static const char valid_parameters[] = "123456789"; + int parameter = 0; + + /* Search for the parameter digit, the index in the string is the + * parameter to use. + */ + while (valid_parameters[parameter] != parameter_char && + valid_parameters[parameter] != '\0') + ++parameter; + + /* If the parameter digit is out of range it will just get printed. */ + if (parameter < PNG_WARNING_PARAMETER_COUNT) + { + /* Append this parameter */ + png_const_charp parm = p[parameter]; + png_const_charp pend = p[parameter] + (sizeof p[parameter]); + + /* No need to copy the trailing '\0' here, but there is no guarantee + * that parm[] has been initialized, so there is no guarantee of a + * trailing '\0': + */ + while (i<(sizeof msg)-1 && *parm != '\0' && parm < pend) + msg[i++] = *parm++; + + /* Consume the parameter digit too: */ + ++message; + continue; + } + + /* else not a parameter and there is a character after the @ sign; just + * copy that. This is known not to be '\0' because of the test above. + */ + } + + /* At this point *message can't be '\0', even in the bad parameter case + * above where there is a lone '@' at the end of the message string. + */ + msg[i++] = *message++; + } + + /* i is always less than (sizeof msg), so: */ + msg[i] = '\0'; + + /* And this is the formatted message. It may be larger than + * PNG_MAX_ERROR_TEXT, but that is only used for 'chunk' errors and these + * are not (currently) formatted. + */ + png_warning(png_ptr, msg); +} +#endif /* PNG_WARNINGS_SUPPORTED */ + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_benign_error(png_const_structrp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + { +# ifdef PNG_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + png_ptr->chunk_name != 0) + png_chunk_warning(png_ptr, error_message); + else +# endif + png_warning(png_ptr, error_message); + } + + else + { +# ifdef PNG_READ_SUPPORTED + if ((png_ptr->mode & PNG_IS_READ_STRUCT) != 0 && + png_ptr->chunk_name != 0) + png_chunk_error(png_ptr, error_message); + else +# endif + png_error(png_ptr, error_message); + } +} + +void /* PRIVATE */ +png_app_warning(png_const_structrp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_APP_WARNINGS_WARN) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); +} + +void /* PRIVATE */ +png_app_error(png_const_structrp png_ptr, png_const_charp error_message) +{ + if (png_ptr->flags & PNG_FLAG_APP_ERRORS_WARN) + png_warning(png_ptr, error_message); + else + png_error(png_ptr, error_message); +} +#endif /* BENIGN_ERRORS */ /* 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, @@ -131,186 +410,410 @@ png_warning(png_structp png_ptr, png_const_charp warning_message) * if the character is invalid. */ #define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) -/*static PNG_CONST char png_digit[16] = { +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) +#define PNG_MAX_ERROR_TEXT 196 /* Currently limited be profile_error in png.c */ +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_ERROR_TEXT_SUPPORTED) static void /* PRIVATE */ -png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp - error_message) +png_format_buffer(png_const_structrp png_ptr, png_charp buffer, png_const_charp + error_message) { - int iout = 0, iin = 0; + png_uint_32 chunk_name = png_ptr->chunk_name; + int iout = 0, ishift = 24; - while (iin < 4) + while (ishift >= 0) { - int c = png_ptr->chunk_name[iin++]; + int c = (int)(chunk_name >> ishift) & 0xff; + + ishift -= 8; if (isnonalpha(c)) { - buffer[iout++] = '['; + buffer[iout++] = PNG_LITERAL_LEFT_SQUARE_BRACKET; buffer[iout++] = png_digit[(c & 0xf0) >> 4]; buffer[iout++] = png_digit[c & 0x0f]; - buffer[iout++] = ']'; + buffer[iout++] = PNG_LITERAL_RIGHT_SQUARE_BRACKET; } + else { - buffer[iout++] = (png_byte)c; + buffer[iout++] = (char)c; } } if (error_message == NULL) - buffer[iout] = 0; + buffer[iout] = '\0'; + else { + int iin = 0; + buffer[iout++] = ':'; buffer[iout++] = ' '; - png_strncpy(buffer+iout, error_message, 63); - buffer[iout+63] = 0; + + while (iin < PNG_MAX_ERROR_TEXT-1 && error_message[iin] != '\0') + buffer[iout++] = error_message[iin++]; + + /* iin < PNG_MAX_ERROR_TEXT, so the following is safe: */ + buffer[iout] = '\0'; } } +#endif /* PNG_WARNINGS_SUPPORTED || PNG_ERROR_TEXT_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +PNG_FUNCTION(void,PNGAPI +png_chunk_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* PNG_READ_SUPPORTED && PNG_ERROR_TEXT_SUPPORTED */ + +#ifdef PNG_WARNINGS_SUPPORTED +void PNGAPI +png_chunk_warning(png_const_structrp png_ptr, png_const_charp warning_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* PNG_WARNINGS_SUPPORTED */ #ifdef PNG_READ_SUPPORTED +#ifdef PNG_BENIGN_ERRORS_SUPPORTED void PNGAPI -png_chunk_error(png_structp png_ptr, png_const_charp error_message) +png_chunk_benign_error(png_const_structrp png_ptr, png_const_charp + error_message) { - char msg[18+64]; - if (png_ptr == NULL) - png_error(png_ptr, error_message); + if (png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN) + png_chunk_warning(png_ptr, error_message); + else - { - png_format_buffer(png_ptr, msg, error_message); - png_error(png_ptr, msg); - } + png_chunk_error(png_ptr, error_message); } +#endif #endif /* PNG_READ_SUPPORTED */ -#endif /* !defined(PNG_NO_WARNINGS) || !defined(PNG_NO_ERROR_TEXT) */ -#ifndef PNG_NO_WARNINGS -void PNGAPI -png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +void /* PRIVATE */ +png_chunk_report(png_const_structrp png_ptr, png_const_charp message, int error) { - char msg[18+64]; - if (png_ptr == NULL) - png_warning(png_ptr, warning_message); - else + /* This is always supported, but for just read or just write it + * unconditionally does the right thing. + */ +# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) + if (png_ptr->mode & PNG_IS_READ_STRUCT) +# endif + +# ifdef PNG_READ_SUPPORTED + { + if (error < PNG_CHUNK_ERROR) + png_chunk_warning(png_ptr, message); + + else + png_chunk_benign_error(png_ptr, message); + } +# endif + +# if defined(PNG_READ_SUPPORTED) && defined(PNG_WRITE_SUPPORTED) + else if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) +# endif + +# ifdef PNG_WRITE_SUPPORTED + { + if (error < PNG_CHUNK_WRITE_ERROR) + png_app_warning(png_ptr, message); + + else + png_app_error(png_ptr, message); + } +# endif +} + +#ifdef PNG_ERROR_TEXT_SUPPORTED +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_FUNCTION(void, +png_fixed_error,(png_const_structrp png_ptr, png_const_charp name),PNG_NORETURN) +{ +# define fixed_message "fixed point overflow in " +# define fixed_message_ln ((sizeof fixed_message)-1) + int iin; + char msg[fixed_message_ln+PNG_MAX_ERROR_TEXT]; + memcpy(msg, fixed_message, fixed_message_ln); + iin = 0; + if (name != NULL) while (iin < (PNG_MAX_ERROR_TEXT-1) && name[iin] != 0) { - png_format_buffer(png_ptr, msg, warning_message); - png_warning(png_ptr, msg); + msg[fixed_message_ln + iin] = name[iin]; + ++iin; + } + msg[fixed_message_ln + iin] = 0; + png_error(png_ptr, msg); +} +#endif +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This API only exists if ANSI-C style error handling is used, + * otherwise it is necessary for png_default_error to be overridden. + */ +jmp_buf* PNGAPI +png_set_longjmp_fn(png_structrp png_ptr, png_longjmp_ptr longjmp_fn, + size_t jmp_buf_size) +{ + /* From libpng 1.6.0 the app gets one chance to set a 'jmpbuf_size' value + * and it must not change after that. Libpng doesn't care how big the + * buffer is, just that it doesn't change. + * + * If the buffer size is no *larger* than the size of jmp_buf when libpng is + * compiled a built in jmp_buf is returned; this preserves the pre-1.6.0 + * semantics that this call will not fail. If the size is larger, however, + * the buffer is allocated and this may fail, causing the function to return + * NULL. + */ + if (png_ptr == NULL) + return NULL; + + if (png_ptr->jmp_buf_ptr == NULL) + { + png_ptr->jmp_buf_size = 0; /* not allocated */ + + if (jmp_buf_size <= (sizeof png_ptr->jmp_buf_local)) + png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; + + else + { + png_ptr->jmp_buf_ptr = png_voidcast(jmp_buf *, + png_malloc_warn(png_ptr, jmp_buf_size)); + + if (png_ptr->jmp_buf_ptr == NULL) + return NULL; /* new NULL return on OOM */ + + png_ptr->jmp_buf_size = jmp_buf_size; + } + } + + else /* Already allocated: check the size */ + { + size_t size = png_ptr->jmp_buf_size; + + if (size == 0) + { + size = (sizeof png_ptr->jmp_buf_local); + if (png_ptr->jmp_buf_ptr != &png_ptr->jmp_buf_local) + { + /* This is an internal error in libpng: somehow we have been left + * with a stack allocated jmp_buf when the application regained + * control. It's always possible to fix this up, but for the moment + * this is a png_error because that makes it easy to detect. + */ + png_error(png_ptr, "Libpng jmp_buf still allocated"); + /* png_ptr->jmp_buf_ptr = &png_ptr->jmp_buf_local; */ + } + } + + if (size != jmp_buf_size) + { + png_warning(png_ptr, "Application jmp_buf size changed"); + return NULL; /* caller will probably crash: no choice here */ + } + } + + /* Finally fill in the function, now we have a satisfactory buffer. It is + * valid to change the function on every call. + */ + png_ptr->longjmp_fn = longjmp_fn; + return png_ptr->jmp_buf_ptr; +} + +void /* PRIVATE */ +png_free_jmpbuf(png_structrp png_ptr) +{ + if (png_ptr != NULL) + { + jmp_buf *jb = png_ptr->jmp_buf_ptr; + + /* A size of 0 is used to indicate a local, stack, allocation of the + * pointer; used here and in png.c + */ + if (jb != NULL && png_ptr->jmp_buf_size > 0) + { + + /* This stuff is so that a failure to free the error control structure + * does not leave libpng in a state with no valid error handling: the + * free always succeeds, if there is an error it gets ignored. + */ + if (jb != &png_ptr->jmp_buf_local) + { + /* Make an internal, libpng, jmp_buf to return here */ + jmp_buf free_jmp_buf; + + if (!setjmp(free_jmp_buf)) + { + png_ptr->jmp_buf_ptr = &free_jmp_buf; /* come back here */ + png_ptr->jmp_buf_size = 0; /* stack allocation */ + png_ptr->longjmp_fn = longjmp; + png_free(png_ptr, jb); /* Return to setjmp on error */ + } + } + } + + /* *Always* cancel everything out: */ + png_ptr->jmp_buf_size = 0; + png_ptr->jmp_buf_ptr = NULL; + png_ptr->longjmp_fn = 0; } } -#endif /* PNG_NO_WARNINGS */ - +#endif /* This is the default error handling function. Note that replacements for * this function MUST NOT RETURN, or the program will likely crash. This * function is used by default, or if the program supplies NULL for the * error function pointer in png_set_error_fn(). */ -static void /* PRIVATE */ -png_default_error(png_structp, png_const_charp error_message) +static PNG_FUNCTION(void /* PRIVATE */, +png_default_error,(png_const_structrp png_ptr, png_const_charp error_message), + PNG_NORETURN) { -#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_CONSOLE_IO_SUPPORTED #ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (*error_message == '#') + /* Check on NULL only added in 1.5.4 */ + if (error_message != NULL && *error_message == PNG_LITERAL_SHARP) { - int offset; - char error_number[16]; - for (offset=0; offset<15; offset++) - { - error_number[offset] = *(error_message+offset+1); - if (*(error_message+offset) == ' ') - break; - } - if((offset > 1) && (offset < 15)) - { - error_number[offset-1]='\0'; - fprintf(stderr, "libpng error no. %s: %s\n", error_number, - error_message+offset); - } - else - fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset); + /* Strip "#nnnn " from beginning of error message. */ + int offset; + char error_number[16]; + for (offset = 0; offset<15; offset++) + { + error_number[offset] = error_message[offset + 1]; + if (error_message[offset] == ' ') + break; + } + + if ((offset > 1) && (offset < 15)) + { + error_number[offset - 1] = '\0'; + fprintf(stderr, "libpng error no. %s: %s", + error_number, error_message + offset + 1); + fprintf(stderr, PNG_STRING_NEWLINE); + } + + else + { + fprintf(stderr, "libpng error: %s, offset=%d", + error_message, offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } } else #endif - fprintf(stderr, "libpng error: %s\n", error_message); -#endif - -#ifdef PNG_SETJMP_SUPPORTED - if (png_ptr) { -# ifdef USE_FAR_KEYWORD - { - jmp_buf jmpbuf; - png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf)); - longjmp(jmpbuf, 1); - } -# else - longjmp(png_ptr->jmpbuf, 1); -# endif + fprintf(stderr, "libpng error: %s", error_message ? error_message : + "undefined"); + fprintf(stderr, PNG_STRING_NEWLINE); } #else - PNG_ABORT(); -#endif -#ifdef PNG_NO_CONSOLE_IO - error_message = error_message; /* make compiler happy */ + PNG_UNUSED(error_message) /* Make compiler happy */ #endif + png_longjmp(png_ptr, 1); } -#ifndef PNG_NO_WARNINGS +PNG_FUNCTION(void,PNGAPI +png_longjmp,(png_const_structrp, int),PNG_NORETURN) +{ +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr && png_ptr->longjmp_fn && png_ptr->jmp_buf_ptr) + png_ptr->longjmp_fn(*png_ptr->jmp_buf_ptr, val); +#endif + + /* Here if not setjmp support or if png_ptr is null. */ + PNG_ABORT(); +} + +#ifdef PNG_WARNINGS_SUPPORTED /* This function is called when there is a warning, but the library thinks * it can continue anyway. Replacement functions don't have to do anything * here if you don't want them to. In the default configuration, png_ptr is * not used, but it is passed in case it may be useful. */ static void /* PRIVATE */ -png_default_warning(png_structp png_ptr, png_const_charp warning_message) +png_default_warning(png_const_structrp png_ptr, png_const_charp warning_message) { -#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_CONSOLE_IO_SUPPORTED # ifdef PNG_ERROR_NUMBERS_SUPPORTED - if (*warning_message == '#') + if (*warning_message == PNG_LITERAL_SHARP) { - int offset; - char warning_number[16]; - for (offset=0; offset<15; offset++) - { - warning_number[offset]=*(warning_message+offset+1); - if (*(warning_message+offset) == ' ') + int offset; + char warning_number[16]; + for (offset = 0; offset < 15; offset++) + { + warning_number[offset] = warning_message[offset + 1]; + if (warning_message[offset] == ' ') break; - } - if((offset > 1) && (offset < 15)) - { - warning_number[offset-1]='\0'; - fprintf(stderr, "libpng warning no. %s: %s\n", warning_number, - warning_message+offset); - } - else - fprintf(stderr, "libpng warning: %s\n", warning_message); + } + + if ((offset > 1) && (offset < 15)) + { + warning_number[offset + 1] = '\0'; + fprintf(stderr, "libpng warning no. %s: %s", + warning_number, warning_message + offset); + fprintf(stderr, PNG_STRING_NEWLINE); + } + + else + { + fprintf(stderr, "libpng warning: %s", + warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } } else # endif - fprintf(stderr, "libpng warning: %s\n", warning_message); + + { + fprintf(stderr, "libpng warning: %s", warning_message); + fprintf(stderr, PNG_STRING_NEWLINE); + } #else - warning_message = warning_message; /* make compiler happy */ + PNG_UNUSED(warning_message) /* Make compiler happy */ #endif - png_ptr = png_ptr; /* make compiler happy */ + PNG_UNUSED(png_ptr) /* Make compiler happy */ } -#endif /* PNG_NO_WARNINGS */ +#endif /* PNG_WARNINGS_SUPPORTED */ /* This function is called when the application wants to use another method * of handling errors and warnings. Note that the error function MUST NOT * return to the calling routine or serious problems will occur. The return - * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + * method used in the default routine calls longjmp(png_ptr->jmp_buf_ptr, 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) +png_set_error_fn(png_structrp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) { if (png_ptr == NULL) return; + png_ptr->error_ptr = error_ptr; png_ptr->error_fn = error_fn; +#ifdef PNG_WARNINGS_SUPPORTED png_ptr->warning_fn = warning_fn; +#else + PNG_UNUSED(warning_fn) +#endif } @@ -319,23 +822,111 @@ png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, * pointer before png_write_destroy and png_read_destroy are called. */ png_voidp PNGAPI -png_get_error_ptr(png_structp png_ptr) +png_get_error_ptr(png_const_structrp png_ptr) { if (png_ptr == NULL) return NULL; + return ((png_voidp)png_ptr->error_ptr); } #ifdef PNG_ERROR_NUMBERS_SUPPORTED void PNGAPI -png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +png_set_strip_error_numbers(png_structrp png_ptr, png_uint_32 strip_mode) { - if(png_ptr != NULL) + if (png_ptr != NULL) { - png_ptr->flags &= - ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS | + PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); } } #endif + +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) + /* Currently the above both depend on SETJMP_SUPPORTED, however it would be + * possible to implement without setjmp support just so long as there is some + * way to handle the error return here: + */ +PNG_FUNCTION(void /* PRIVATE */, +png_safe_error,(png_structp png_nonconst_ptr, png_const_charp error_message), + PNG_NORETURN) +{ + const png_const_structrp png_ptr = png_nonconst_ptr; + png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr); + + /* An error is always logged here, overwriting anything (typically a warning) + * that is already there: + */ + if (image != NULL) + { + png_safecat(image->message, (sizeof image->message), 0, error_message); + image->warning_or_error |= PNG_IMAGE_ERROR; + + /* Retrieve the jmp_buf from within the png_control, making this work for + * C++ compilation too is pretty tricky: C++ wants a pointer to the first + * element of a jmp_buf, but C doesn't tell us the type of that. + */ + if (image->opaque != NULL && image->opaque->error_buf != NULL) + longjmp(png_control_jmp_buf(image->opaque), 1); + + /* Missing longjmp buffer, the following is to help debugging: */ + { + size_t pos = png_safecat(image->message, (sizeof image->message), 0, + "bad longjmp: "); + png_safecat(image->message, (sizeof image->message), pos, + error_message); + } + } + + /* Here on an internal programming error. */ + abort(); +} + +#ifdef PNG_WARNINGS_SUPPORTED +void /* PRIVATE */ +png_safe_warning(png_structp png_nonconst_ptr, png_const_charp warning_message) +{ + const png_const_structrp png_ptr = png_nonconst_ptr; + png_imagep image = png_voidcast(png_imagep, png_ptr->error_ptr); + + /* A warning is only logged if there is no prior warning or error. */ + if (image->warning_or_error == 0) + { + png_safecat(image->message, (sizeof image->message), 0, warning_message); + image->warning_or_error |= PNG_IMAGE_WARNING; + } +} +#endif + +int /* PRIVATE */ +png_safe_execute(png_imagep image_in, int (*function)(png_voidp), png_voidp arg) +{ + volatile png_imagep image = image_in; + volatile int result; + volatile png_voidp saved_error_buf; + jmp_buf safe_jmpbuf; + + /* Safely execute function(arg) with png_error returning to this function. */ + saved_error_buf = image->opaque->error_buf; + result = setjmp(safe_jmpbuf) == 0; + + if (result) + { + + image->opaque->error_buf = safe_jmpbuf; + result = function(arg); + } + + image->opaque->error_buf = saved_error_buf; + + /* And do the cleanup prior to any failure return. */ + if (!result) + png_image_free(image); + + return result; +} +#endif /* SIMPLIFIED READ/WRITE */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngget.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngget.c index 36be6a6..80ab055 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngget.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngget.c @@ -1,615 +1,833 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) png_uint_32 PNGAPI -png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +png_get_valid(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 flag) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->valid & flag); - else - return(0); + + return(0); } -png_uint_32 PNGAPI -png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +png_size_t PNGAPI +png_get_rowbytes(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->rowbytes); - else - return(0); + + return(0); } -#if defined(PNG_INFO_IMAGE_SUPPORTED) +#ifdef PNG_INFO_IMAGE_SUPPORTED png_bytepp PNGAPI -png_get_rows(png_structp png_ptr, png_infop info_ptr) +png_get_rows(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->row_pointers); - else - return(0); + + return(0); } #endif #ifdef PNG_EASY_ACCESS_SUPPORTED -/* easy access to info, added in libpng-0.99 */ +/* 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) +png_get_image_width(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - { return info_ptr->width; - } + return (0); } png_uint_32 PNGAPI -png_get_image_height(png_structp png_ptr, png_infop info_ptr) +png_get_image_height(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - { return info_ptr->height; - } + return (0); } png_byte PNGAPI -png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +png_get_bit_depth(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - { return info_ptr->bit_depth; - } + return (0); } png_byte PNGAPI -png_get_color_type(png_structp png_ptr, png_infop info_ptr) +png_get_color_type(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - { return info_ptr->color_type; - } + return (0); } png_byte PNGAPI -png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +png_get_filter_type(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - { return info_ptr->filter_type; - } + return (0); } png_byte PNGAPI -png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +png_get_interlace_type(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - { return info_ptr->interlace_type; - } + return (0); } png_byte PNGAPI -png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +png_get_compression_type(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) - { return info_ptr->compression_type; - } + return (0); } png_uint_32 PNGAPI -png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +png_get_x_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp + info_ptr) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_pHYs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_pHYs) - { - png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter"); - if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) - return (0); - else return (info_ptr->x_pixels_per_unit); - } -#else - return (0); +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function", + "png_get_x_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER) + return (info_ptr->x_pixels_per_unit); + } #endif + return (0); } png_uint_32 PNGAPI -png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +png_get_y_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp + info_ptr) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_pHYs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_pHYs) +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter"); - if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) - return (0); - else return (info_ptr->y_pixels_per_unit); + png_debug1(1, "in %s retrieval function", + "png_get_y_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER) + return (info_ptr->y_pixels_per_unit); } -#else - return (0); #endif + return (0); } png_uint_32 PNGAPI -png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +png_get_pixels_per_meter(png_const_structrp png_ptr, png_const_inforp info_ptr) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_pHYs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_pHYs) +#ifdef PNG_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter"); - if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER || - info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) - return (0); - else return (info_ptr->x_pixels_per_unit); + png_debug1(1, "in %s retrieval function", "png_get_pixels_per_meter"); + + if (info_ptr->phys_unit_type == PNG_RESOLUTION_METER && + info_ptr->x_pixels_per_unit == info_ptr->y_pixels_per_unit) + return (info_ptr->x_pixels_per_unit); } -#else - return (0); #endif + return (0); } #ifdef PNG_FLOATING_POINT_SUPPORTED float PNGAPI -png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) +png_get_pixel_aspect_ratio(png_const_structrp png_ptr, png_const_inforp + info_ptr) +{ +#ifdef PNG_READ_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_pHYs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_pHYs) - { - png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio"); - if (info_ptr->x_pixels_per_unit == 0) - return ((float)0.0); - else + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio"); + + if (info_ptr->x_pixels_per_unit != 0) return ((float)((float)info_ptr->y_pixels_per_unit - /(float)info_ptr->x_pixels_per_unit)); + /(float)info_ptr->x_pixels_per_unit)); } #else - return (0.0); + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) #endif + return ((float)0.0); } #endif -png_int_32 PNGAPI -png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +#ifdef PNG_FIXED_POINT_SUPPORTED +png_fixed_point PNGAPI +png_get_pixel_aspect_ratio_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_oFFs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_oFFs) +#ifdef PNG_READ_pHYs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs) + && info_ptr->x_pixels_per_unit > 0 && info_ptr->y_pixels_per_unit > 0 + && info_ptr->x_pixels_per_unit <= PNG_UINT_31_MAX + && info_ptr->y_pixels_per_unit <= PNG_UINT_31_MAX) { - png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); - if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) - return (0); - else return (info_ptr->x_offset); + png_fixed_point res; + + png_debug1(1, "in %s retrieval function", "png_get_aspect_ratio_fixed"); + + /* The following casts work because a PNG 4 byte integer only has a valid + * range of 0..2^31-1; otherwise the cast might overflow. + */ + if (png_muldiv(&res, (png_int_32)info_ptr->y_pixels_per_unit, PNG_FP_1, + (png_int_32)info_ptr->x_pixels_per_unit)) + return res; } #else - return (0); + PNG_UNUSED(png_ptr) + PNG_UNUSED(info_ptr) #endif + + return 0; +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_debug1(1, "in %s retrieval function", "png_get_x_offset_microns"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER) + return (info_ptr->x_offset); + } +#endif + return (0); } png_int_32 PNGAPI -png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +png_get_y_offset_microns(png_const_structrp png_ptr, png_const_inforp info_ptr) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_oFFs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_oFFs) +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { - png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); - if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) - return (0); - else return (info_ptr->y_offset); + png_debug1(1, "in %s retrieval function", "png_get_y_offset_microns"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_MICROMETER) + return (info_ptr->y_offset); } -#else - return (0); #endif + return (0); } png_int_32 PNGAPI -png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +png_get_x_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_oFFs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_oFFs) +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { - png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); - if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) - return (0); - else return (info_ptr->x_offset); + png_debug1(1, "in %s retrieval function", "png_get_x_offset_pixels"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL) + return (info_ptr->x_offset); } -#else - return (0); #endif + return (0); } png_int_32 PNGAPI -png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +png_get_y_offset_pixels(png_const_structrp png_ptr, png_const_inforp info_ptr) { - if (png_ptr != NULL && info_ptr != NULL) -#if defined(PNG_oFFs_SUPPORTED) - if (info_ptr->valid & PNG_INFO_oFFs) +#ifdef PNG_oFFs_SUPPORTED + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { - png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); - if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) - return (0); - else return (info_ptr->y_offset); + png_debug1(1, "in %s retrieval function", "png_get_y_offset_pixels"); + + if (info_ptr->offset_unit_type == PNG_OFFSET_PIXEL) + return (info_ptr->y_offset); } -#else - return (0); #endif + return (0); } -#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) -png_uint_32 PNGAPI -png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +#ifdef PNG_INCH_CONVERSIONS_SUPPORTED +static png_uint_32 +ppi_from_ppm(png_uint_32 ppm) { - return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) - *.0254 +.5)); +#if 0 + /* The conversion is *(2.54/100), in binary (32 digits): + * .00000110100000001001110101001001 + */ + png_uint_32 t1001, t1101; + ppm >>= 1; /* .1 */ + t1001 = ppm + (ppm >> 3); /* .1001 */ + t1101 = t1001 + (ppm >> 1); /* .1101 */ + ppm >>= 20; /* .000000000000000000001 */ + t1101 += t1101 >> 15; /* .1101000000000001101 */ + t1001 >>= 11; /* .000000000001001 */ + t1001 += t1001 >> 12; /* .000000000001001000000001001 */ + ppm += t1001; /* .000000000001001000001001001 */ + ppm += t1101; /* .110100000001001110101001001 */ + return (ppm + 16) >> 5;/* .00000110100000001001110101001001 */ +#else + /* The argument is a PNG unsigned integer, so it is not permitted + * to be bigger than 2^31. + */ + png_fixed_point result; + if (ppm <= PNG_UINT_31_MAX && png_muldiv(&result, (png_int_32)ppm, 127, + 5000)) + return result; + + /* Overflow. */ + return 0; +#endif } png_uint_32 PNGAPI -png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +png_get_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) { - return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) - *.0254 +.5)); + return ppi_from_ppm(png_get_pixels_per_meter(png_ptr, info_ptr)); } png_uint_32 PNGAPI -png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +png_get_x_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) { - return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) - *.0254 +.5)); + return ppi_from_ppm(png_get_x_pixels_per_meter(png_ptr, info_ptr)); } +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_const_structrp png_ptr, png_const_inforp info_ptr) +{ + return ppi_from_ppm(png_get_y_pixels_per_meter(png_ptr, info_ptr)); +} + +#ifdef PNG_FIXED_POINT_SUPPORTED +static png_fixed_point +png_fixed_inches_from_microns(png_const_structrp png_ptr, png_int_32 microns) +{ + /* Convert from metres * 1,000,000 to inches * 100,000, meters to + * inches is simply *(100/2.54), so we want *(10/2.54) == 500/127. + * Notice that this can overflow - a warning is output and 0 is + * returned. + */ + return png_muldiv_warn(png_ptr, microns, 500, 127); +} + +png_fixed_point PNGAPI +png_get_x_offset_inches_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ + return png_fixed_inches_from_microns(png_ptr, + png_get_x_offset_microns(png_ptr, info_ptr)); +} +#endif + +#ifdef PNG_FIXED_POINT_SUPPORTED +png_fixed_point PNGAPI +png_get_y_offset_inches_fixed(png_const_structrp png_ptr, + png_const_inforp info_ptr) +{ + return png_fixed_inches_from_microns(png_ptr, + png_get_y_offset_microns(png_ptr, info_ptr)); +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED float PNGAPI -png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +png_get_x_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr) { - return ((float)png_get_x_offset_microns(png_ptr, info_ptr) - *.00003937); + /* To avoid the overflow do the conversion directly in floating + * point. + */ + return (float)(png_get_x_offset_microns(png_ptr, info_ptr) * .00003937); } +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED float PNGAPI -png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +png_get_y_offset_inches(png_const_structrp png_ptr, png_const_inforp info_ptr) { - return ((float)png_get_y_offset_microns(png_ptr, info_ptr) - *.00003937); + /* To avoid the overflow do the conversion directly in floating + * point. + */ + return (float)(png_get_y_offset_microns(png_ptr, info_ptr) * .00003937); } +#endif -#if defined(PNG_pHYs_SUPPORTED) +#ifdef PNG_pHYs_SUPPORTED png_uint_32 PNGAPI -png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, - png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +png_get_pHYs_dpi(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) { png_uint_32 retval = 0; if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - png_debug1(1, "in %s retrieval function\n", "pHYs"); + png_debug1(1, "in %s retrieval function", "pHYs"); + if (res_x != NULL) { *res_x = info_ptr->x_pixels_per_unit; retval |= PNG_INFO_pHYs; } + if (res_y != NULL) { *res_y = info_ptr->y_pixels_per_unit; retval |= PNG_INFO_pHYs; } + if (unit_type != NULL) { *unit_type = (int)info_ptr->phys_unit_type; retval |= PNG_INFO_pHYs; - if(*unit_type == 1) + + if (*unit_type == 1) { if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); } } } + return (retval); } #endif /* PNG_pHYs_SUPPORTED */ -#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS_SUPPORTED */ /* png_get_channels really belongs in here, too, but it's been around longer */ #endif /* PNG_EASY_ACCESS_SUPPORTED */ + png_byte PNGAPI -png_get_channels(png_structp png_ptr, png_infop info_ptr) +png_get_channels(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->channels); - else - return (0); + + return (0); } -png_bytep PNGAPI -png_get_signature(png_structp png_ptr, png_infop info_ptr) +#ifdef PNG_READ_SUPPORTED +png_const_bytep PNGAPI +png_get_signature(png_const_structrp png_ptr, png_const_inforp info_ptr) { if (png_ptr != NULL && info_ptr != NULL) return(info_ptr->signature); - else - return (NULL); -} -#if defined(PNG_bKGD_SUPPORTED) + return (NULL); +} +#endif + +#ifdef PNG_bKGD_SUPPORTED png_uint_32 PNGAPI -png_get_bKGD(png_structp png_ptr, png_infop info_ptr, +png_get_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, png_color_16p *background) { if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) - && background != NULL) + && background != NULL) { - png_debug1(1, "in %s retrieval function\n", "bKGD"); + png_debug1(1, "in %s retrieval function", "bKGD"); + *background = &(info_ptr->background); return (PNG_INFO_bKGD); } + return (0); } #endif -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_cHRM_SUPPORTED +/* The XYZ APIs were added in 1.5.5 to take advantage of the code added at the + * same time to correct the rgb grayscale coefficient defaults obtained from the + * cHRM chunk in 1.5.4 + */ +# ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI -png_get_cHRM(png_structp png_ptr, png_infop info_ptr, - double *white_x, double *white_y, double *red_x, double *red_y, - double *green_x, double *green_y, double *blue_x, double *blue_y) +png_get_cHRM(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) { - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + /* Quiet API change: this code used to only return the end points if a cHRM + * chunk was present, but the end points can also come from iCCP or sRGB + * chunks, so in 1.6.0 the png_get_ APIs return the end points regardless and + * the png_set_ APIs merely check that set end points are mutually + * consistent. + */ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) { - png_debug1(1, "in %s retrieval function\n", "cHRM"); + png_debug1(1, "in %s retrieval function", "cHRM"); + if (white_x != NULL) - *white_x = (double)info_ptr->x_white; + *white_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitex, "cHRM white X"); if (white_y != NULL) - *white_y = (double)info_ptr->y_white; + *white_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.whitey, "cHRM white Y"); if (red_x != NULL) - *red_x = (double)info_ptr->x_red; + *red_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redx, + "cHRM red X"); if (red_y != NULL) - *red_y = (double)info_ptr->y_red; + *red_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.redy, + "cHRM red Y"); if (green_x != NULL) - *green_x = (double)info_ptr->x_green; + *green_x = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greenx, "cHRM green X"); if (green_y != NULL) - *green_y = (double)info_ptr->y_green; + *green_y = png_float(png_ptr, + info_ptr->colorspace.end_points_xy.greeny, "cHRM green Y"); if (blue_x != NULL) - *blue_x = (double)info_ptr->x_blue; + *blue_x = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluex, + "cHRM blue X"); if (blue_y != NULL) - *blue_y = (double)info_ptr->y_blue; + *blue_y = png_float(png_ptr, info_ptr->colorspace.end_points_xy.bluey, + "cHRM blue Y"); return (PNG_INFO_cHRM); } + return (0); } -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED + png_uint_32 PNGAPI -png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, - png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, - png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, - png_fixed_point *blue_x, png_fixed_point *blue_y) +png_get_cHRM_XYZ(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *red_X, double *red_Y, double *red_Z, double *green_X, + double *green_Y, double *green_Z, double *blue_X, double *blue_Y, + double *blue_Z) { - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) { - png_debug1(1, "in %s retrieval function\n", "cHRM"); - if (white_x != NULL) - *white_x = info_ptr->int_x_white; - if (white_y != NULL) - *white_y = info_ptr->int_y_white; - if (red_x != NULL) - *red_x = info_ptr->int_x_red; - if (red_y != NULL) - *red_y = info_ptr->int_y_red; - if (green_x != NULL) - *green_x = info_ptr->int_x_green; - if (green_y != NULL) - *green_y = info_ptr->int_y_green; - if (blue_x != NULL) - *blue_x = info_ptr->int_x_blue; - if (blue_y != NULL) - *blue_y = info_ptr->int_y_blue; + png_debug1(1, "in %s retrieval function", "cHRM_XYZ(float)"); + + if (red_X != NULL) + *red_X = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_X, + "cHRM red X"); + if (red_Y != NULL) + *red_Y = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Y, + "cHRM red Y"); + if (red_Z != NULL) + *red_Z = png_float(png_ptr, info_ptr->colorspace.end_points_XYZ.red_Z, + "cHRM red Z"); + if (green_X != NULL) + *green_X = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_X, "cHRM green X"); + if (green_Y != NULL) + *green_Y = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_Y, "cHRM green Y"); + if (green_Z != NULL) + *green_Z = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.green_Z, "cHRM green Z"); + if (blue_X != NULL) + *blue_X = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_X, "cHRM blue X"); + if (blue_Y != NULL) + *blue_Y = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_Y, "cHRM blue Y"); + if (blue_Z != NULL) + *blue_Z = png_float(png_ptr, + info_ptr->colorspace.end_points_XYZ.blue_Z, "cHRM blue Z"); return (PNG_INFO_cHRM); } + return (0); } -#endif +# endif + +# ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *int_red_X, png_fixed_point *int_red_Y, + png_fixed_point *int_red_Z, png_fixed_point *int_green_X, + png_fixed_point *int_green_Y, png_fixed_point *int_green_Z, + png_fixed_point *int_blue_X, png_fixed_point *int_blue_Y, + png_fixed_point *int_blue_Z) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + png_debug1(1, "in %s retrieval function", "cHRM_XYZ"); + + if (int_red_X != NULL) + *int_red_X = info_ptr->colorspace.end_points_XYZ.red_X; + if (int_red_Y != NULL) + *int_red_Y = info_ptr->colorspace.end_points_XYZ.red_Y; + if (int_red_Z != NULL) + *int_red_Z = info_ptr->colorspace.end_points_XYZ.red_Z; + if (int_green_X != NULL) + *int_green_X = info_ptr->colorspace.end_points_XYZ.green_X; + if (int_green_Y != NULL) + *int_green_Y = info_ptr->colorspace.end_points_XYZ.green_Y; + if (int_green_Z != NULL) + *int_green_Z = info_ptr->colorspace.end_points_XYZ.green_Z; + if (int_blue_X != NULL) + *int_blue_X = info_ptr->colorspace.end_points_XYZ.blue_X; + if (int_blue_Y != NULL) + *int_blue_Y = info_ptr->colorspace.end_points_XYZ.blue_Y; + if (int_blue_Z != NULL) + *int_blue_Z = info_ptr->colorspace.end_points_XYZ.blue_Z; + return (PNG_INFO_cHRM); + } + + return (0); +} + +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + png_debug1(1, "in %s retrieval function", "cHRM"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_ENDPOINTS)) + { + if (white_x != NULL) + *white_x = info_ptr->colorspace.end_points_xy.whitex; + if (white_y != NULL) + *white_y = info_ptr->colorspace.end_points_xy.whitey; + if (red_x != NULL) + *red_x = info_ptr->colorspace.end_points_xy.redx; + if (red_y != NULL) + *red_y = info_ptr->colorspace.end_points_xy.redy; + if (green_x != NULL) + *green_x = info_ptr->colorspace.end_points_xy.greenx; + if (green_y != NULL) + *green_y = info_ptr->colorspace.end_points_xy.greeny; + if (blue_x != NULL) + *blue_x = info_ptr->colorspace.end_points_xy.bluex; + if (blue_y != NULL) + *blue_y = info_ptr->colorspace.end_points_xy.bluey; + return (PNG_INFO_cHRM); + } + + return (0); +} +# endif #endif -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_gAMA_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED png_uint_32 PNGAPI -png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +png_get_gAMA_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_fixed_point *file_gamma) { - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) - && file_gamma != NULL) + png_debug1(1, "in %s retrieval function", "gAMA"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && + file_gamma != NULL) { - png_debug1(1, "in %s retrieval function\n", "gAMA"); - *file_gamma = (double)info_ptr->gamma; + *file_gamma = info_ptr->colorspace.gamma; return (PNG_INFO_gAMA); } + return (0); } -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED +# endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI -png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, - png_fixed_point *int_file_gamma) +png_get_gAMA(png_const_structrp png_ptr, png_const_inforp info_ptr, + double *file_gamma) { - if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) - && int_file_gamma != NULL) + png_debug1(1, "in %s retrieval function", "gAMA(float)"); + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) && + file_gamma != NULL) { - png_debug1(1, "in %s retrieval function\n", "gAMA"); - *int_file_gamma = info_ptr->int_gamma; + *file_gamma = png_float(png_ptr, info_ptr->colorspace.gamma, + "png_get_gAMA"); return (PNG_INFO_gAMA); } + return (0); } -#endif +# endif #endif -#if defined(PNG_sRGB_SUPPORTED) +#ifdef PNG_sRGB_SUPPORTED png_uint_32 PNGAPI -png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +png_get_sRGB(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *file_srgb_intent) { + png_debug1(1, "in %s retrieval function", "sRGB"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) - && file_srgb_intent != NULL) + && file_srgb_intent != NULL) { - png_debug1(1, "in %s retrieval function\n", "sRGB"); - *file_srgb_intent = (int)info_ptr->srgb_intent; + *file_srgb_intent = info_ptr->colorspace.rendering_intent; return (PNG_INFO_sRGB); } + return (0); } #endif -#if defined(PNG_iCCP_SUPPORTED) +#ifdef PNG_iCCP_SUPPORTED png_uint_32 PNGAPI -png_get_iCCP(png_structp png_ptr, png_infop info_ptr, - png_charpp name, int *compression_type, - png_charpp profile, png_uint_32 *proflen) +png_get_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, + png_charpp name, int *compression_type, + png_bytepp profile, png_uint_32 *proflen) { + png_debug1(1, "in %s retrieval function", "iCCP"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) - && name != NULL && profile != NULL && proflen != NULL) + && name != NULL && compression_type != NULL && profile != NULL && + proflen != NULL) { - png_debug1(1, "in %s retrieval function\n", "iCCP"); *name = info_ptr->iccp_name; *profile = info_ptr->iccp_profile; - /* compression_type is a dummy so the API won't have to change - if we introduce multiple compression types later. */ - *proflen = (int)info_ptr->iccp_proflen; - *compression_type = (int)info_ptr->iccp_compression; + *proflen = png_get_uint_32(info_ptr->iccp_profile); + /* This is somewhat irrelevant since the profile data returned has + * actually been uncompressed. + */ + *compression_type = PNG_COMPRESSION_TYPE_BASE; return (PNG_INFO_iCCP); } + return (0); } #endif -#if defined(PNG_sPLT_SUPPORTED) -png_uint_32 PNGAPI -png_get_sPLT(png_structp png_ptr, png_infop info_ptr, - png_sPLT_tpp spalettes) +#ifdef PNG_sPLT_SUPPORTED +int PNGAPI +png_get_sPLT(png_const_structrp png_ptr, png_inforp info_ptr, + png_sPLT_tpp spalettes) { if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) { - *spalettes = info_ptr->splt_palettes; - return ((png_uint_32)info_ptr->splt_palettes_num); + *spalettes = info_ptr->splt_palettes; + return info_ptr->splt_palettes_num; } + return (0); } #endif -#if defined(PNG_hIST_SUPPORTED) +#ifdef PNG_hIST_SUPPORTED png_uint_32 PNGAPI -png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +png_get_hIST(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_16p *hist) { + png_debug1(1, "in %s retrieval function", "hIST"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) - && hist != NULL) + && hist != NULL) { - png_debug1(1, "in %s retrieval function\n", "hIST"); *hist = info_ptr->hist; return (PNG_INFO_hIST); } + return (0); } #endif png_uint_32 PNGAPI -png_get_IHDR(png_structp png_ptr, png_infop info_ptr, - png_uint_32 *width, png_uint_32 *height, int *bit_depth, - int *color_type, int *interlace_type, int *compression_type, - int *filter_type) - +png_get_IHDR(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) { - if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL && - bit_depth != NULL && color_type != NULL) - { - png_debug1(1, "in %s retrieval function\n", "IHDR"); - *width = info_ptr->width; - *height = info_ptr->height; - *bit_depth = info_ptr->bit_depth; - if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16) - png_error(png_ptr, "Invalid bit depth"); - *color_type = info_ptr->color_type; - if (info_ptr->color_type > 6) - png_error(png_ptr, "Invalid color type"); - if (compression_type != NULL) - *compression_type = info_ptr->compression_type; - if (filter_type != NULL) - *filter_type = info_ptr->filter_type; - if (interlace_type != NULL) - *interlace_type = info_ptr->interlace_type; + png_debug1(1, "in %s retrieval function", "IHDR"); - /* check for potential overflow of rowbytes */ - if (*width == 0 || *width > PNG_UINT_31_MAX) - png_error(png_ptr, "Invalid image width"); - if (*height == 0 || *height > PNG_UINT_31_MAX) - png_error(png_ptr, "Invalid image height"); - if (info_ptr->width > (PNG_UINT_32_MAX - >> 3) /* 8-byte RGBA pixels */ - - 64 /* bigrowbuf hack */ - - 1 /* filter byte */ - - 7*8 /* rounding of width to multiple of 8 pixels */ - - 8) /* extra max_pixel_depth pad */ - { - png_warning(png_ptr, - "Width too large for libpng to process image data."); - } - return (1); - } - return (0); + if (png_ptr == NULL || info_ptr == NULL || width == NULL || + height == NULL || bit_depth == NULL || color_type == NULL) + return (0); + + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + *color_type = info_ptr->color_type; + + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* This is redundant if we can be sure that the info_ptr values were all + * assigned in png_set_IHDR(). We do the check anyhow in case an + * application has ignored our advice not to mess with the members + * of info_ptr directly. + */ + png_check_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + + return (1); } -#if defined(PNG_oFFs_SUPPORTED) +#ifdef PNG_oFFs_SUPPORTED png_uint_32 PNGAPI -png_get_oFFs(png_structp png_ptr, png_infop info_ptr, - png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +png_get_oFFs(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) { + png_debug1(1, "in %s retrieval function", "oFFs"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) - && offset_x != NULL && offset_y != NULL && unit_type != NULL) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) { - png_debug1(1, "in %s retrieval function\n", "oFFs"); *offset_x = info_ptr->x_offset; *offset_y = info_ptr->y_offset; *unit_type = (int)info_ptr->offset_unit_type; return (PNG_INFO_oFFs); } + return (0); } #endif -#if defined(PNG_pCAL_SUPPORTED) +#ifdef PNG_pCAL_SUPPORTED png_uint_32 PNGAPI -png_get_pCAL(png_structp png_ptr, png_infop info_ptr, - png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, - png_charp *units, png_charpp *params) +png_get_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) { + png_debug1(1, "in %s retrieval function", "pCAL"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) - && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && - nparams != NULL && units != NULL && params != NULL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) { - png_debug1(1, "in %s retrieval function\n", "pCAL"); *purpose = info_ptr->pcal_purpose; *X0 = info_ptr->pcal_X0; *X1 = info_ptr->pcal_X1; @@ -619,283 +837,341 @@ png_get_pCAL(png_structp png_ptr, png_infop info_ptr, *params = info_ptr->pcal_params; return (PNG_INFO_pCAL); } + return (0); } #endif -#if defined(PNG_sCAL_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_sCAL_SUPPORTED +# ifdef PNG_FIXED_POINT_SUPPORTED +# if defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) || \ + defined(PNG_FLOATING_POINT_SUPPORTED) png_uint_32 PNGAPI -png_get_sCAL(png_structp png_ptr, png_infop info_ptr, - int *unit, double *width, double *height) +png_get_sCAL_fixed(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, png_fixed_point *width, png_fixed_point *height) { - if (png_ptr != NULL && info_ptr != NULL && + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) - { - *unit = info_ptr->scal_unit; - *width = info_ptr->scal_pixel_width; - *height = info_ptr->scal_pixel_height; - return (PNG_INFO_sCAL); - } - return(0); -} -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -png_uint_32 PNGAPI -png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, - int *unit, png_charpp width, png_charpp height) -{ - if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_sCAL)) - { - *unit = info_ptr->scal_unit; - *width = info_ptr->scal_s_width; - *height = info_ptr->scal_s_height; - return (PNG_INFO_sCAL); - } - return(0); -} -#endif -#endif -#endif + { + *unit = info_ptr->scal_unit; + /*TODO: make this work without FP support; the API is currently eliminated + * if neither floating point APIs nor internal floating point arithmetic + * are enabled. + */ + *width = png_fixed(png_ptr, atof(info_ptr->scal_s_width), "sCAL width"); + *height = png_fixed(png_ptr, atof(info_ptr->scal_s_height), + "sCAL height"); + return (PNG_INFO_sCAL); + } -#if defined(PNG_pHYs_SUPPORTED) + return(0); +} +# endif /* FLOATING_ARITHMETIC */ +# endif /* FIXED_POINT */ +# ifdef PNG_FLOATING_POINT_SUPPORTED png_uint_32 PNGAPI -png_get_pHYs(png_structp png_ptr, png_infop info_ptr, - png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +png_get_sCAL(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = atof(info_ptr->scal_s_width); + *height = atof(info_ptr->scal_s_height); + return (PNG_INFO_sCAL); + } + + return(0); +} +# endif /* FLOATING POINT */ +png_uint_32 PNGAPI +png_get_sCAL_s(png_const_structrp png_ptr, png_const_inforp info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + + return(0); +} +#endif /* sCAL */ + +#ifdef PNG_pHYs_SUPPORTED +png_uint_32 PNGAPI +png_get_pHYs(png_const_structrp png_ptr, png_const_inforp info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) { png_uint_32 retval = 0; + png_debug1(1, "in %s retrieval function", "pHYs"); + if (png_ptr != NULL && info_ptr != NULL && - (info_ptr->valid & PNG_INFO_pHYs)) + (info_ptr->valid & PNG_INFO_pHYs)) { - png_debug1(1, "in %s retrieval function\n", "pHYs"); if (res_x != NULL) { *res_x = info_ptr->x_pixels_per_unit; retval |= PNG_INFO_pHYs; } + if (res_y != NULL) { *res_y = info_ptr->y_pixels_per_unit; retval |= PNG_INFO_pHYs; } + if (unit_type != NULL) { *unit_type = (int)info_ptr->phys_unit_type; retval |= PNG_INFO_pHYs; } } + return (retval); } -#endif +#endif /* pHYs */ png_uint_32 PNGAPI -png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, - int *num_palette) +png_get_PLTE(png_const_structrp png_ptr, png_inforp info_ptr, + png_colorp *palette, int *num_palette) { + png_debug1(1, "in %s retrieval function", "PLTE"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) && palette != NULL) { - png_debug1(1, "in %s retrieval function\n", "PLTE"); *palette = info_ptr->palette; *num_palette = info_ptr->num_palette; - png_debug1(3, "num_palette = %d\n", *num_palette); + png_debug1(3, "num_palette = %d", *num_palette); return (PNG_INFO_PLTE); } + return (0); } -#if defined(PNG_sBIT_SUPPORTED) +#ifdef PNG_sBIT_SUPPORTED png_uint_32 PNGAPI -png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +png_get_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, + png_color_8p *sig_bit) { + png_debug1(1, "in %s retrieval function", "sBIT"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) - && sig_bit != NULL) + && sig_bit != NULL) { - png_debug1(1, "in %s retrieval function\n", "sBIT"); *sig_bit = &(info_ptr->sig_bit); return (PNG_INFO_sBIT); } + return (0); } #endif -#if defined(PNG_TEXT_SUPPORTED) -png_uint_32 PNGAPI -png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, - int *num_text) +#ifdef PNG_TEXT_SUPPORTED +int PNGAPI +png_get_text(png_const_structrp png_ptr, png_inforp info_ptr, + png_textp *text_ptr, int *num_text) { if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) { - png_debug1(1, "in %s retrieval function\n", - (png_ptr->chunk_name[0] == '\0' ? "text" - : (png_const_charp)png_ptr->chunk_name)); + png_debug1(1, "in 0x%lx retrieval function", + (unsigned long)png_ptr->chunk_name); + if (text_ptr != NULL) *text_ptr = info_ptr->text; + if (num_text != NULL) *num_text = info_ptr->num_text; - return ((png_uint_32)info_ptr->num_text); + + return info_ptr->num_text; } + if (num_text != NULL) - *num_text = 0; + *num_text = 0; + return(0); } #endif -#if defined(PNG_tIME_SUPPORTED) +#ifdef PNG_tIME_SUPPORTED png_uint_32 PNGAPI -png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +png_get_tIME(png_const_structrp png_ptr, png_inforp info_ptr, + png_timep *mod_time) { + png_debug1(1, "in %s retrieval function", "tIME"); + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) && mod_time != NULL) { - png_debug1(1, "in %s retrieval function\n", "tIME"); *mod_time = &(info_ptr->mod_time); return (PNG_INFO_tIME); } + return (0); } #endif -#if defined(PNG_tRNS_SUPPORTED) +#ifdef PNG_tRNS_SUPPORTED png_uint_32 PNGAPI -png_get_tRNS(png_structp png_ptr, png_infop info_ptr, - png_bytep *trans, int *num_trans, png_color_16p *trans_values) +png_get_tRNS(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytep *trans_alpha, int *num_trans, png_color_16p *trans_color) { png_uint_32 retval = 0; if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { - png_debug1(1, "in %s retrieval function\n", "tRNS"); + png_debug1(1, "in %s retrieval function", "tRNS"); + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (trans != NULL) - { - *trans = info_ptr->trans; - retval |= PNG_INFO_tRNS; - } - if (trans_values != NULL) - *trans_values = &(info_ptr->trans_values); + if (trans_alpha != NULL) + { + *trans_alpha = info_ptr->trans_alpha; + retval |= PNG_INFO_tRNS; + } + + if (trans_color != NULL) + *trans_color = &(info_ptr->trans_color); } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ { - if (trans_values != NULL) - { - *trans_values = &(info_ptr->trans_values); - retval |= PNG_INFO_tRNS; - } - if(trans != NULL) - *trans = NULL; + if (trans_color != NULL) + { + *trans_color = &(info_ptr->trans_color); + retval |= PNG_INFO_tRNS; + } + + if (trans_alpha != NULL) + *trans_alpha = NULL; } - if(num_trans != NULL) + + if (num_trans != NULL) { *num_trans = info_ptr->num_trans; retval |= PNG_INFO_tRNS; } } + return (retval); } #endif -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) -png_uint_32 PNGAPI -png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, - png_unknown_chunkpp unknowns) +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +int PNGAPI +png_get_unknown_chunks(png_const_structrp png_ptr, png_inforp info_ptr, + png_unknown_chunkpp unknowns) { if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) { - *unknowns = info_ptr->unknown_chunks; - return ((png_uint_32)info_ptr->unknown_chunks_num); + *unknowns = info_ptr->unknown_chunks; + return info_ptr->unknown_chunks_num; } + return (0); } #endif -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED png_byte PNGAPI -png_get_rgb_to_gray_status (png_structp png_ptr) +png_get_rgb_to_gray_status (png_const_structrp png_ptr) { - return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); + return (png_byte)(png_ptr ? png_ptr->rgb_to_gray_status : 0); } #endif -#if defined(PNG_USER_CHUNKS_SUPPORTED) +#ifdef PNG_USER_CHUNKS_SUPPORTED png_voidp PNGAPI -png_get_user_chunk_ptr(png_structp png_ptr) +png_get_user_chunk_ptr(png_const_structrp png_ptr) { - return (png_ptr? png_ptr->user_chunk_ptr : NULL); + return (png_ptr ? png_ptr->user_chunk_ptr : NULL); } #endif -#ifdef PNG_WRITE_SUPPORTED -png_uint_32 PNGAPI -png_get_compression_buffer_size(png_structp png_ptr) +png_size_t PNGAPI +png_get_compression_buffer_size(png_const_structrp png_ptr) { - return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L); -} -#endif + if (png_ptr == NULL) + return 0; -#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); -} +# ifdef PNG_WRITE_SUPPORTED + if (png_ptr->mode & PNG_IS_READ_STRUCT) +# endif + { +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + return png_ptr->IDAT_read_size; +# else + return PNG_IDAT_READ_SIZE; +# endif + } -/* 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; +# ifdef PNG_WRITE_SUPPORTED + else + return png_ptr->zbuffer_size; +# endif } - /* 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 */ +/* These functions were added to libpng 1.2.6 and were enabled + * by default in libpng-1.4.0 */ png_uint_32 PNGAPI -png_get_user_width_max (png_structp png_ptr) +png_get_user_width_max (png_const_structrp png_ptr) { - return (png_ptr? png_ptr->user_width_max : 0); + return (png_ptr ? png_ptr->user_width_max : 0); } + png_uint_32 PNGAPI -png_get_user_height_max (png_structp png_ptr) +png_get_user_height_max (png_const_structrp png_ptr) { - return (png_ptr? png_ptr->user_height_max : 0); + return (png_ptr ? png_ptr->user_height_max : 0); +} + +/* This function was added to libpng 1.4.0 */ +png_uint_32 PNGAPI +png_get_chunk_cache_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_cache_max : 0); +} + +/* This function was added to libpng 1.4.1 */ +png_alloc_size_t PNGAPI +png_get_chunk_malloc_max (png_const_structrp png_ptr) +{ + return (png_ptr ? png_ptr->user_chunk_malloc_max : 0); } #endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ +/* These functions were added to libpng 1.4.0 */ +#ifdef PNG_IO_STATE_SUPPORTED +png_uint_32 PNGAPI +png_get_io_state (png_const_structrp png_ptr) +{ + return png_ptr->io_state; +} + +png_uint_32 PNGAPI +png_get_io_chunk_type (png_const_structrp png_ptr) +{ + return png_ptr->chunk_name; +} +#endif /* ?PNG_IO_STATE_SUPPORTED */ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED +# ifdef PNG_GET_PALETTE_MAX_SUPPORTED +int PNGAPI +png_get_palette_max(png_const_structp png_ptr, png_const_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return png_ptr->num_palette_max; + + return (-1); +} +# endif +#endif #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pnginfo.h b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pnginfo.h new file mode 100644 index 0000000..898a406 --- /dev/null +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pnginfo.h @@ -0,0 +1,260 @@ + +/* pnginfo.h - header file for PNG reference library + * + * Copyright (c) 1998-2013 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.) + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + + /* 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, using png_set_*() functions, 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 libpng-1.5.0 this was moved into a separate private + * file that is not visible to applications. + * + * 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. + */ +#ifndef PNGINFO_H +#define PNGINFO_H + +struct png_info_def +{ + /* The following are necessary for every PNG file */ + png_uint_32 width; /* width of image in pixels (from IHDR) */ + png_uint_32 height; /* height of image in pixels (from IHDR) */ + png_uint_32 valid; /* valid chunk data (see PNG_INFO_ below) */ + png_size_t rowbytes; /* bytes needed to hold an untransformed row */ + png_colorp palette; /* array of color values (valid & PNG_INFO_PLTE) */ + png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */ + png_uint_16 num_trans; /* number of transparent palette color (tRNS) */ + png_byte bit_depth; /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */ + png_byte color_type; /* see PNG_COLOR_TYPE_ below (from IHDR) */ + /* The following three should have been named *_method not *_type */ + png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */ + png_byte filter_type; /* must be PNG_FILTER_TYPE_BASE (from IHDR) */ + png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + + /* The following are set by png_set_IHDR, called from the application on + * write, but the are never actually used by the write code. + */ + 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 */ + +#ifdef PNG_READ_SUPPORTED + /* This is never set during write */ + png_byte signature[8]; /* magic bytes read by libpng from start of file */ +#endif + + /* 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_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + /* png_colorspace only contains 'flags' if neither GAMMA or COLORSPACE are + * defined. When COLORSPACE is switched on all the colorspace-defining + * chunks should be enabled, when GAMMA is switched on all the gamma-defining + * chunks should be enabled. If this is not done it becomes possible to read + * inconsistent PNG files and assign a probably incorrect interpretation to + * the information. (In other words, by carefully choosing which chunks to + * recognize the system configuration can select an interpretation for PNG + * files containing ambiguous data and this will result in inconsistent + * behavior between different libpng builds!) + */ + png_colorspace colorspace; +#endif + +#ifdef PNG_iCCP_SUPPORTED + /* iCCP chunk data. */ + png_charp iccp_name; /* profile name */ + png_bytep iccp_profile; /* International Color Consortium profile data */ + png_uint_32 iccp_proflen; /* ICC profile data length */ +#endif + +#ifdef 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 or comments to write */ + int max_text; /* current size of text array */ + png_textp text; /* array of comments read or comments to write */ +#endif /* PNG_TEXT_SUPPORTED */ + +#ifdef 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 + +#ifdef 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_alpha; /* alpha values for paletted image */ + png_color_16 trans_color; /* 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 + +#ifdef 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 + +#ifdef 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 + +#ifdef 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_pCAL_SUPPORTED + /* The pCAL chunk describes a transformation between the stored pixel + * values and original physical data values used to create the image. + * The integer range [0, 2^bit_depth - 1] maps to the floating-point + * range given by [pcal_X0, pcal_X1], and are further transformed by a + * (possibly non-linear) transformation function given by "pcal_type" + * and "pcal_params" into "pcal_units". Please see the PNG_EQUATION_ + * defines below, and the PNG-Group's PNG extensions document for a + * complete description of the transformations and how they should be + * implemented, and for a description of the ASCII parameter strings. + * Data values are valid if (valid & PNG_INFO_pCAL) non-zero. + */ + png_charp pcal_purpose; /* pCAL chunk description string */ + png_int_32 pcal_X0; /* minimum value */ + png_int_32 pcal_X1; /* maximum value */ + png_charp pcal_units; /* Latin-1 string giving physical units */ + png_charpp pcal_params; /* ASCII strings containing parameter values */ + png_byte pcal_type; /* equation type (see PNG_EQUATION_ below) */ + png_byte pcal_nparams; /* number of parameters given in pcal_params */ +#endif + +/* New members added in libpng-1.0.6 */ + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ + +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + /* Storage for unknown chunks that the library doesn't recognize. */ + png_unknown_chunkp unknown_chunks; + + /* The type of this field is limited by the type of + * png_struct::user_chunk_cache_max, else overflow can occur. + */ + int unknown_chunks_num; +#endif + +#ifdef PNG_sPLT_SUPPORTED + /* Data on sPLT chunks (there may be more than one). */ + png_sPLT_tp splt_palettes; + int splt_palettes_num; /* Match type returned by png_get API */ +#endif + +#ifdef 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. Data values are valid if (valid & PNG_INFO_sCAL) is + * non-zero. + */ + png_byte scal_unit; /* unit of physical scale */ + png_charp scal_s_width; /* string containing height */ + png_charp scal_s_height; /* string containing width */ +#endif + +#ifdef 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 + +}; +#endif /* PNGINFO_H */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngmem.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngmem.c index 6aa70af..43e2948 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngmem.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngmem.c @@ -1,12 +1,15 @@ /* 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 + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * This file provides a location for all memory allocation. Users who * need special memory handling are expected to supply replacement * functions for png_malloc() and png_free(), and to use @@ -14,569 +17,232 @@ * identify the replacement functions. */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) - -/* Borland DOS special memory handler */ -#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) -/* if you change this, be sure to change the one in png.h also */ - -/* Allocate memory for a png_struct. The malloc and memset can be replaced - by a single call to calloc() if this is thought to improve performance. */ -png_voidp /* PRIVATE */ -png_create_struct(int type) -{ -#ifdef PNG_USER_MEM_SUPPORTED - return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); -} - -/* Alternate version of png_create_struct, for use with user-defined malloc. */ -png_voidp /* PRIVATE */ -png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) -{ -#endif /* PNG_USER_MEM_SUPPORTED */ - png_size_t size; - png_voidp struct_ptr; - - if (type == PNG_STRUCT_INFO) - size = png_sizeof(png_info); - else if (type == PNG_STRUCT_PNG) - size = png_sizeof(png_struct); - else - return (png_get_copyright(NULL)); - -#ifdef PNG_USER_MEM_SUPPORTED - if(malloc_fn != NULL) - { - png_struct dummy_struct; - png_structp png_ptr = &dummy_struct; - png_ptr->mem_ptr=mem_ptr; - struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); - } - else -#endif /* PNG_USER_MEM_SUPPORTED */ - struct_ptr = (png_voidp)farmalloc(size); - if (struct_ptr != NULL) - png_memset(struct_ptr, 0, size); - return (struct_ptr); -} - -/* Free memory allocated by a png_create_struct() call */ +/* Free a png_struct */ void /* PRIVATE */ -png_destroy_struct(png_voidp struct_ptr) +png_destroy_png_struct(png_structrp png_ptr) { -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); -} - -/* Free memory allocated by a png_create_struct() call */ -void /* PRIVATE */ -png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, - png_voidp mem_ptr) -{ -#endif - if (struct_ptr != NULL) + if (png_ptr != NULL) { -#ifdef PNG_USER_MEM_SUPPORTED - if(free_fn != NULL) - { - png_struct dummy_struct; - png_structp png_ptr = &dummy_struct; - png_ptr->mem_ptr=mem_ptr; - (*(free_fn))(png_ptr, struct_ptr); - return; - } -#endif /* PNG_USER_MEM_SUPPORTED */ - farfree (struct_ptr); + /* png_free might call png_error and may certainly call + * png_get_mem_ptr, so fake a temporary png_struct to support this. + */ + png_struct dummy_struct = *png_ptr; + memset(png_ptr, 0, (sizeof *png_ptr)); + png_free(&dummy_struct, png_ptr); + +# ifdef PNG_SETJMP_SUPPORTED + /* We may have a jmp_buf left to deallocate. */ + png_free_jmpbuf(&dummy_struct); +# endif } } /* Allocate memory. For reasonable files, size should never exceed * 64K. However, zlib may allocate more then 64K if you don't tell - * it not to. See zconf.h and png.h for more information. zlib does + * it not to. See zconf.h and png.h for more information. zlib does * need to allocate exactly 64K, so whatever you call here must * have the ability to do that. - * - * Borland seems to have a problem in DOS mode for exactly 64K. - * It gives you a segment with an offset of 8 (perhaps to store its - * memory stuff). zlib doesn't like this at all, so we have to - * detect and deal with it. This code should not be needed in - * Windows or OS/2 modes, and only in 16 bit mode. This code has - * been updated by Alexander Lehmann for version 0.89 to waste less - * memory. - * - * Note that we can't use png_size_t for the "size" declaration, - * since on some systems a png_size_t is a 16-bit quantity, and as a - * result, we would be truncating potentially larger memory requests - * (which should cause a fatal error) and introducing major problems. */ - -png_voidp PNGAPI -png_malloc(png_structp png_ptr, png_uint_32 size) +PNG_FUNCTION(png_voidp,PNGAPI +png_calloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) { png_voidp ret; - if (png_ptr == NULL || size == 0) - return (NULL); + ret = png_malloc(png_ptr, size); -#ifdef PNG_USER_MEM_SUPPORTED - if(png_ptr->malloc_fn != NULL) - ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); - else - ret = (png_malloc_default(png_ptr, size)); - if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out of memory!"); - return (ret); + if (ret != NULL) + memset(ret, 0, size); + + return ret; } -png_voidp PNGAPI -png_malloc_default(png_structp png_ptr, png_uint_32 size) +/* png_malloc_base, an internal function added at libpng 1.6.0, does the work of + * allocating memory, taking into account limits and PNG_USER_MEM_SUPPORTED. + * Checking and error handling must happen outside this routine; it returns NULL + * if the allocation cannot be done (for any reason.) + */ +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_malloc_base,(png_const_structrp, png_alloc_size_t size), + PNG_ALLOCATED) +{ + /* Moved to png_malloc_base from png_malloc_default in 1.6.0; the DOS + * allocators have also been removed in 1.6.0, so any 16-bit system now has + * to implement a user memory handler. This checks to be sure it isn't + * called with big numbers. + */ +#ifdef PNG_USER_MEM_SUPPORTED + PNG_UNUSED(png_ptr) +#endif + if (size > 0 && size <= PNG_SIZE_MAX +# ifdef PNG_MAX_MALLOC_64K + && size <= 65536U +# endif + ) + { +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr != NULL && png_ptr->malloc_fn != NULL) + return png_ptr->malloc_fn(png_constcast(png_structrp,png_ptr), size); + + else +#endif + return malloc((size_t)size); /* checked for truncation above */ + } + + else + return NULL; +} + +/* This is really here only to work round a spurious warning in GCC 4.6 and 4.7 + * that arises because of the checks in png_realloc_array that are repeated in + * png_malloc_array. + */ +static png_voidp +png_malloc_array_checked(png_const_structrp png_ptr, int nelements, + size_t element_size) +{ + png_alloc_size_t req = nelements; /* known to be > 0 */ + + if (req <= PNG_SIZE_MAX/element_size) + return png_malloc_base(png_ptr, req * element_size); + + /* The failure case when the request is too large */ + return NULL; +} + +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_malloc_array,(png_const_structrp png_ptr, int nelements, + size_t element_size),PNG_ALLOCATED) +{ + if (nelements <= 0 || element_size == 0) + png_error(png_ptr, "internal error: array alloc"); + + return png_malloc_array_checked(png_ptr, nelements, element_size); +} + +PNG_FUNCTION(png_voidp /* PRIVATE */, +png_realloc_array,(png_const_structrp png_ptr, png_const_voidp old_array, + int old_elements, int add_elements, size_t element_size),PNG_ALLOCATED) +{ + /* These are internal errors: */ + if (add_elements <= 0 || element_size == 0 || old_elements < 0 || + (old_array == NULL && old_elements > 0)) + png_error(png_ptr, "internal error: array realloc"); + + /* Check for overflow on the elements count (so the caller does not have to + * check.) + */ + if (add_elements <= INT_MAX - old_elements) + { + png_voidp new_array = png_malloc_array_checked(png_ptr, + old_elements+add_elements, element_size); + + if (new_array != NULL) + { + /* Because png_malloc_array worked the size calculations below cannot + * overflow. + */ + if (old_elements > 0) + memcpy(new_array, old_array, element_size*(unsigned)old_elements); + + memset((char*)new_array + element_size*(unsigned)old_elements, 0, + element_size*(unsigned)add_elements); + + return new_array; + } + } + + return NULL; /* error */ +} + +/* Various functions that have different error handling are derived from this. + * png_malloc always exists, but if PNG_USER_MEM_SUPPORTED is defined a separate + * function png_malloc_default is also provided. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc,(png_const_structrp png_ptr, png_alloc_size_t size),PNG_ALLOCATED) { png_voidp ret; -#endif /* PNG_USER_MEM_SUPPORTED */ - if (png_ptr == NULL || size == 0) - return (NULL); + if (png_ptr == NULL) + return NULL; -#ifdef PNG_MAX_MALLOC_64K - if (size > (png_uint_32)65536L) - { - png_warning(png_ptr, "Cannot Allocate > 64K"); - ret = NULL; - } - else -#endif + ret = png_malloc_base(png_ptr, size); - if (size != (size_t)size) - ret = NULL; - else if (size == (png_uint_32)65536L) - { - if (png_ptr->offset_table == NULL) - { - /* try to see if we need to do any of this fancy stuff */ - ret = farmalloc(size); - if (ret == NULL || ((png_size_t)ret & 0xffff)) - { - int num_blocks; - png_uint_32 total_size; - png_bytep table; - int i; - png_byte huge * hptr; - - if (ret != NULL) - { - farfree(ret); - ret = NULL; - } - - if(png_ptr->zlib_window_bits > 14) - num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); - else - num_blocks = 1; - if (png_ptr->zlib_mem_level >= 7) - num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); - else - num_blocks++; - - total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; - - table = farmalloc(total_size); - - if (table == NULL) - { -#ifndef PNG_USER_MEM_SUPPORTED - if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */ - else - png_warning(png_ptr, "Out Of Memory."); -#endif - return (NULL); - } - - if ((png_size_t)table & 0xfff0) - { -#ifndef PNG_USER_MEM_SUPPORTED - if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, - "Farmalloc didn't return normalized pointer"); - else - png_warning(png_ptr, - "Farmalloc didn't return normalized pointer"); -#endif - return (NULL); - } - - png_ptr->offset_table = table; - png_ptr->offset_table_ptr = farmalloc(num_blocks * - png_sizeof (png_bytep)); - - if (png_ptr->offset_table_ptr == NULL) - { -#ifndef PNG_USER_MEM_SUPPORTED - if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out Of memory."); /* Note "O" and "M" */ - else - png_warning(png_ptr, "Out Of memory."); -#endif - return (NULL); - } - - hptr = (png_byte huge *)table; - if ((png_size_t)hptr & 0xf) - { - hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); - hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ - } - for (i = 0; i < num_blocks; i++) - { - png_ptr->offset_table_ptr[i] = (png_bytep)hptr; - hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ - } - - png_ptr->offset_table_number = num_blocks; - png_ptr->offset_table_count = 0; - png_ptr->offset_table_count_free = 0; - } - } - - if (png_ptr->offset_table_count >= png_ptr->offset_table_number) - { -#ifndef PNG_USER_MEM_SUPPORTED - if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */ - else - png_warning(png_ptr, "Out of Memory."); -#endif - return (NULL); - } - - ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; - } - else - ret = farmalloc(size); - -#ifndef PNG_USER_MEM_SUPPORTED if (ret == NULL) - { - if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */ - else - png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */ - } -#endif + png_error(png_ptr, "Out of memory"); /* 'm' means png_malloc */ - return (ret); + return ret; } -/* free a pointer allocated by png_malloc(). In the default - configuration, png_ptr is not used, but is passed in case it - is needed. If ptr is NULL, return without taking any action. */ -void PNGAPI -png_free(png_structp png_ptr, png_voidp ptr) -{ - if (png_ptr == NULL || ptr == NULL) - return; - #ifdef PNG_USER_MEM_SUPPORTED - if (png_ptr->free_fn != NULL) - { - (*(png_ptr->free_fn))(png_ptr, ptr); - return; - } - else png_free_default(png_ptr, ptr); -} - -void PNGAPI -png_free_default(png_structp png_ptr, png_voidp ptr) -{ -#endif /* PNG_USER_MEM_SUPPORTED */ - - if(png_ptr == NULL) return; - - if (png_ptr->offset_table != NULL) - { - int i; - - for (i = 0; i < png_ptr->offset_table_count; i++) - { - if (ptr == png_ptr->offset_table_ptr[i]) - { - ptr = NULL; - png_ptr->offset_table_count_free++; - break; - } - } - if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) - { - farfree(png_ptr->offset_table); - farfree(png_ptr->offset_table_ptr); - png_ptr->offset_table = NULL; - png_ptr->offset_table_ptr = NULL; - } - } - - if (ptr != NULL) - { - farfree(ptr); - } -} - -#else /* Not the Borland DOS special memory handler */ - -/* Allocate memory for a png_struct or a png_info. The malloc and - memset can be replaced by a single call to calloc() if this is thought - to improve performance noticably. */ -png_voidp /* PRIVATE */ -png_create_struct(int type) -{ -#ifdef PNG_USER_MEM_SUPPORTED - return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); -} - -/* Allocate memory for a png_struct or a png_info. The malloc and - memset can be replaced by a single call to calloc() if this is thought - to improve performance noticably. */ -png_voidp /* PRIVATE */ -png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) -{ -#endif /* PNG_USER_MEM_SUPPORTED */ - png_size_t size; - png_voidp struct_ptr; - - if (type == PNG_STRUCT_INFO) - size = png_sizeof(png_info); - else if (type == PNG_STRUCT_PNG) - size = png_sizeof(png_struct); - else - return (NULL); - -#ifdef PNG_USER_MEM_SUPPORTED - if(malloc_fn != NULL) - { - png_struct dummy_struct; - png_structp png_ptr = &dummy_struct; - png_ptr->mem_ptr=mem_ptr; - struct_ptr = (*(malloc_fn))(png_ptr, size); - if (struct_ptr != NULL) - png_memset(struct_ptr, 0, size); - return (struct_ptr); - } -#endif /* PNG_USER_MEM_SUPPORTED */ - -#if defined(__TURBOC__) && !defined(__FLAT__) - struct_ptr = (png_voidp)farmalloc(size); -#else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - struct_ptr = (png_voidp)halloc(size,1); -# else - struct_ptr = (png_voidp)malloc(size); -# endif -#endif - if (struct_ptr != NULL) - png_memset(struct_ptr, 0, size); - - return (struct_ptr); -} - - -/* Free memory allocated by a png_create_struct() call */ -void /* PRIVATE */ -png_destroy_struct(png_voidp struct_ptr) -{ -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); -} - -/* Free memory allocated by a png_create_struct() call */ -void /* PRIVATE */ -png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, - png_voidp mem_ptr) -{ -#endif /* PNG_USER_MEM_SUPPORTED */ - if (struct_ptr != NULL) - { -#ifdef PNG_USER_MEM_SUPPORTED - if(free_fn != NULL) - { - png_struct dummy_struct; - png_structp png_ptr = &dummy_struct; - png_ptr->mem_ptr=mem_ptr; - (*(free_fn))(png_ptr, struct_ptr); - return; - } -#endif /* PNG_USER_MEM_SUPPORTED */ -#if defined(__TURBOC__) && !defined(__FLAT__) - farfree(struct_ptr); -#else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - hfree(struct_ptr); -# else - free(struct_ptr); -# endif -#endif - } -} - -/* Allocate memory. For reasonable files, size should never exceed - 64K. However, zlib may allocate more then 64K if you don't tell - it not to. See zconf.h and png.h for more information. zlib does - need to allocate exactly 64K, so whatever you call here must - have the ability to do that. */ - -png_voidp PNGAPI -png_malloc(png_structp png_ptr, png_uint_32 size) +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc_default,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED PNG_DEPRECATED) { png_voidp ret; -#ifdef PNG_USER_MEM_SUPPORTED - if (png_ptr == NULL || size == 0) - return (NULL); + if (png_ptr == NULL) + return NULL; - if(png_ptr->malloc_fn != NULL) - ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); - else - ret = (png_malloc_default(png_ptr, size)); - if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out of Memory!"); - return (ret); + /* Passing 'NULL' here bypasses the application provided memory handler. */ + ret = png_malloc_base(NULL/*use malloc*/, size); + + if (ret == NULL) + png_error(png_ptr, "Out of Memory"); /* 'M' means png_malloc_default */ + + return ret; } - -png_voidp PNGAPI -png_malloc_default(png_structp png_ptr, png_uint_32 size) -{ - png_voidp ret; #endif /* PNG_USER_MEM_SUPPORTED */ - if (png_ptr == NULL || size == 0) - return (NULL); - -#ifdef PNG_MAX_MALLOC_64K - if (size > (png_uint_32)65536L) +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will issue a png_warning and return NULL instead of issuing a + * png_error, if it fails to allocate the requested memory. + */ +PNG_FUNCTION(png_voidp,PNGAPI +png_malloc_warn,(png_const_structrp png_ptr, png_alloc_size_t size), + PNG_ALLOCATED) +{ + if (png_ptr != NULL) { -#ifndef PNG_USER_MEM_SUPPORTED - if(png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Cannot Allocate > 64K"); - else -#endif - return NULL; + png_voidp ret = png_malloc_base(png_ptr, size); + + if (ret != NULL) + return ret; + + png_warning(png_ptr, "Out of memory"); } -#endif - /* Check for overflow */ -#if defined(__TURBOC__) && !defined(__FLAT__) - if (size != (unsigned long)size) - ret = NULL; - else - ret = farmalloc(size); -#else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - if (size != (unsigned long)size) - ret = NULL; - else - ret = halloc(size, 1); -# else - if (size != (size_t)size) - ret = NULL; - else - ret = malloc((size_t)size); -# endif -#endif - -#ifndef PNG_USER_MEM_SUPPORTED - if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) - png_error(png_ptr, "Out of Memory"); -#endif - - return (ret); + return NULL; } /* Free a pointer allocated by png_malloc(). If ptr is NULL, return - without taking any action. */ + * without taking any action. + */ void PNGAPI -png_free(png_structp png_ptr, png_voidp ptr) +png_free(png_const_structrp png_ptr, png_voidp ptr) { if (png_ptr == NULL || ptr == NULL) return; #ifdef PNG_USER_MEM_SUPPORTED if (png_ptr->free_fn != NULL) - { - (*(png_ptr->free_fn))(png_ptr, ptr); - return; - } - else png_free_default(png_ptr, ptr); + png_ptr->free_fn(png_constcast(png_structrp,png_ptr), ptr); + + else + png_free_default(png_ptr, ptr); } -void PNGAPI -png_free_default(png_structp png_ptr, png_voidp ptr) + +PNG_FUNCTION(void,PNGAPI +png_free_default,(png_const_structrp png_ptr, png_voidp ptr),PNG_DEPRECATED) { if (png_ptr == NULL || ptr == NULL) return; - #endif /* PNG_USER_MEM_SUPPORTED */ -#if defined(__TURBOC__) && !defined(__FLAT__) - farfree(ptr); -#else -# if defined(_MSC_VER) && defined(MAXSEG_64K) - hfree(ptr); -# else free(ptr); -# endif -#endif -} - -#endif /* Not Borland DOS special memory handler */ - -#if defined(PNG_1_0_X) -# define png_malloc_warn png_malloc -#else -/* This function was added at libpng version 1.2.3. The png_malloc_warn() - * function will set up png_malloc() to issue a png_warning and return NULL - * instead of issuing a png_error, if it fails to allocate the requested - * memory. - */ -png_voidp PNGAPI -png_malloc_warn(png_structp png_ptr, png_uint_32 size) -{ - png_voidp ptr; - png_uint_32 save_flags; - if(png_ptr == NULL) return (NULL); - - save_flags=png_ptr->flags; - png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; - ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); - png_ptr->flags=save_flags; - return(ptr); -} -#endif - -png_voidp PNGAPI -png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2, - png_uint_32 length) -{ - png_size_t size; - - size = (png_size_t)length; - if ((png_uint_32)size != length) - png_error(png_ptr,"Overflow in png_memcpy_check."); - - return(png_memcpy (s1, s2, size)); -} - -png_voidp PNGAPI -png_memset_check (png_structp png_ptr, png_voidp s1, int value, - png_uint_32 length) -{ - png_size_t size; - - size = (png_size_t)length; - if ((png_uint_32)size != length) - png_error(png_ptr,"Overflow in png_memset_check."); - - return (png_memset (s1, value, size)); - } #ifdef PNG_USER_MEM_SUPPORTED @@ -584,13 +250,14 @@ png_memset_check (png_structp png_ptr, png_voidp s1, int value, * of allocating and freeing memory. */ void PNGAPI -png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr +png_set_mem_fn(png_structrp png_ptr, png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn) { - if(png_ptr != NULL) { - png_ptr->mem_ptr = mem_ptr; - png_ptr->malloc_fn = malloc_fn; - png_ptr->free_fn = free_fn; + if (png_ptr != NULL) + { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; } } @@ -599,10 +266,12 @@ png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr * pointer before png_write_destroy and png_read_destroy are called. */ png_voidp PNGAPI -png_get_mem_ptr(png_structp png_ptr) +png_get_mem_ptr(png_const_structrp png_ptr) { - if(png_ptr == NULL) return (NULL); - return ((png_voidp)png_ptr->mem_ptr); + if (png_ptr == NULL) + return NULL; + + return png_ptr->mem_ptr; } #endif /* PNG_USER_MEM_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpread.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpread.c index 5e84c4a..a133331 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpread.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpread.c @@ -1,19 +1,21 @@ /* 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 + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" #ifdef PNG_PROGRESSIVE_READ_SUPPORTED -/* push model modes */ +/* Push model modes */ #define PNG_READ_SIG_MODE 0 #define PNG_READ_CHUNK_MODE 1 #define PNG_READ_IDAT_MODE 2 @@ -25,10 +27,12 @@ #define PNG_ERROR_MODE 8 void PNGAPI -png_process_data(png_structp png_ptr, png_infop info_ptr, - png_bytep buffer, png_size_t buffer_size) +png_process_data(png_structrp png_ptr, png_inforp info_ptr, + png_bytep buffer, png_size_t buffer_size) { - if(png_ptr == NULL) return; + if (png_ptr == NULL || info_ptr == NULL) + return; + png_push_restore_buffer(png_ptr, buffer, buffer_size); while (png_ptr->buffer_size) @@ -37,13 +41,73 @@ png_process_data(png_structp png_ptr, png_infop info_ptr, } } +png_size_t PNGAPI +png_process_data_pause(png_structrp png_ptr, int save) +{ + if (png_ptr != NULL) + { + /* It's easiest for the caller if we do the save, then the caller doesn't + * have to supply the same data again: + */ + if (save) + png_push_save_buffer(png_ptr); + else + { + /* This includes any pending saved bytes: */ + png_size_t remaining = png_ptr->buffer_size; + png_ptr->buffer_size = 0; + + /* So subtract the saved buffer size, unless all the data + * is actually 'saved', in which case we just return 0 + */ + if (png_ptr->save_buffer_size < remaining) + return remaining - png_ptr->save_buffer_size; + } + } + + return 0; +} + +png_uint_32 PNGAPI +png_process_data_skip(png_structrp png_ptr) +{ + png_uint_32 remaining = 0; + + if (png_ptr != NULL && png_ptr->process_mode == PNG_SKIP_MODE && + png_ptr->skip_length > 0) + { + /* At the end of png_process_data the buffer size must be 0 (see the loop + * above) so we can detect a broken call here: + */ + if (png_ptr->buffer_size != 0) + png_error(png_ptr, + "png_process_data_skip called inside png_process_data"); + + /* If is impossible for there to be a saved buffer at this point - + * otherwise we could not be in SKIP mode. This will also happen if + * png_process_skip is called inside png_process_data (but only very + * rarely.) + */ + if (png_ptr->save_buffer_size != 0) + png_error(png_ptr, "png_process_data_skip called with saved data"); + + remaining = png_ptr->skip_length; + png_ptr->skip_length = 0; + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + + return remaining; +} + /* 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) +png_process_some_data(png_structrp png_ptr, png_inforp info_ptr) { - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + switch (png_ptr->process_mode) { case PNG_READ_SIG_MODE: @@ -51,42 +115,25 @@ png_process_some_data(png_structp png_ptr, png_infop info_ptr) png_push_read_sig(png_ptr, info_ptr); break; } + case PNG_READ_CHUNK_MODE: { png_push_read_chunk(png_ptr, info_ptr); break; } + case PNG_READ_IDAT_MODE: { png_push_read_IDAT(png_ptr); break; } -#if defined(PNG_READ_tEXt_SUPPORTED) - case PNG_READ_tEXt_MODE: - { - png_push_read_tEXt(png_ptr, info_ptr); - break; - } -#endif -#if defined(PNG_READ_zTXt_SUPPORTED) - case PNG_READ_zTXt_MODE: - { - png_push_read_zTXt(png_ptr, info_ptr); - break; - } -#endif -#if defined(PNG_READ_iTXt_SUPPORTED) - case PNG_READ_iTXt_MODE: - { - png_push_read_iTXt(png_ptr, info_ptr); - break; - } -#endif + case PNG_SKIP_MODE: { png_push_crc_finish(png_ptr); break; } + default: { png_ptr->buffer_size = 0; @@ -102,7 +149,7 @@ png_process_some_data(png_structp png_ptr, png_infop info_ptr) * routine. */ void /* PRIVATE */ -png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +png_push_read_sig(png_structrp png_ptr, png_inforp info_ptr) { png_size_t num_checked = png_ptr->sig_bytes, num_to_check = 8 - num_checked; @@ -113,14 +160,15 @@ png_push_read_sig(png_structp png_ptr, png_infop info_ptr) } png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), - num_to_check); - png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check); + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes + num_to_check); if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) { if (num_checked < 4 && png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) png_error(png_ptr, "Not a PNG file"); + else png_error(png_ptr, "PNG file corrupted by ASCII conversion"); } @@ -134,65 +182,13 @@ png_push_read_sig(png_structp png_ptr, png_infop info_ptr) } void /* PRIVATE */ -png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +png_push_read_chunk(png_structrp png_ptr, png_inforp info_ptr) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST PNG_IHDR; - PNG_CONST PNG_IDAT; - PNG_CONST PNG_IEND; - PNG_CONST PNG_PLTE; -#if defined(PNG_READ_bKGD_SUPPORTED) - PNG_CONST PNG_bKGD; + png_uint_32 chunk_name; +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; /* unknown handling method */ #endif -#if defined(PNG_READ_cHRM_SUPPORTED) - PNG_CONST PNG_cHRM; -#endif -#if defined(PNG_READ_gAMA_SUPPORTED) - PNG_CONST PNG_gAMA; -#endif -#if defined(PNG_READ_hIST_SUPPORTED) - PNG_CONST PNG_hIST; -#endif -#if defined(PNG_READ_iCCP_SUPPORTED) - PNG_CONST PNG_iCCP; -#endif -#if defined(PNG_READ_iTXt_SUPPORTED) - PNG_CONST PNG_iTXt; -#endif -#if defined(PNG_READ_oFFs_SUPPORTED) - PNG_CONST PNG_oFFs; -#endif -#if defined(PNG_READ_pCAL_SUPPORTED) - PNG_CONST PNG_pCAL; -#endif -#if defined(PNG_READ_pHYs_SUPPORTED) - PNG_CONST PNG_pHYs; -#endif -#if defined(PNG_READ_sBIT_SUPPORTED) - PNG_CONST PNG_sBIT; -#endif -#if defined(PNG_READ_sCAL_SUPPORTED) - PNG_CONST PNG_sCAL; -#endif -#if defined(PNG_READ_sRGB_SUPPORTED) - PNG_CONST PNG_sRGB; -#endif -#if defined(PNG_READ_sPLT_SUPPORTED) - PNG_CONST PNG_sPLT; -#endif -#if defined(PNG_READ_tEXt_SUPPORTED) - PNG_CONST PNG_tEXt; -#endif -#if defined(PNG_READ_tIME_SUPPORTED) - PNG_CONST PNG_tIME; -#endif -#if defined(PNG_READ_tRNS_SUPPORTED) - PNG_CONST PNG_tRNS; -#endif -#if defined(PNG_READ_zTXt_SUPPORTED) - PNG_CONST PNG_zTXt; -#endif -#endif /* PNG_USE_LOCAL_ARRAYS */ + /* First we make sure we have enough data for the 4 byte chunk name * and the 4 byte chunk length before proceeding with decoding the * chunk data. To fully decode each of these chunks, we also make @@ -202,6 +198,7 @@ png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) { png_byte chunk_length[4]; + png_byte chunk_tag[4]; if (png_ptr->buffer_size < 8) { @@ -210,61 +207,87 @@ png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) } png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); png_reset_crc(png_ptr); - png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_crc_read(png_ptr, chunk_tag, 4); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); + png_check_chunk_name(png_ptr, png_ptr->chunk_name); png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; } - if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - if(png_ptr->mode & PNG_AFTER_IDAT) - png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + chunk_name = png_ptr->chunk_name; - if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + if (chunk_name == png_IDAT) { + if (png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->mode |= PNG_HAVE_IDAT; + + if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_benign_error(png_ptr, "Too many IDATs found"); + } + + if (chunk_name == png_IHDR) + { + if (png_ptr->push_length != 13) + png_error(png_ptr, "Invalid IHDR length"); + if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); } - else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + + else if (chunk_name == png_IEND) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); png_ptr->process_mode = PNG_READ_DONE_MODE; png_push_have_end(png_ptr, info_ptr); } + #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } - if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - png_ptr->mode |= PNG_HAVE_IDAT; - png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); - if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, keep); + + if (chunk_name == png_PLTE) png_ptr->mode |= PNG_HAVE_PLTE; - else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - { - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before IDAT"); - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - png_error(png_ptr, "Missing PLTE before IDAT"); - } } + #endif - else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + else if (chunk_name == png_PLTE) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { @@ -273,222 +296,239 @@ png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) } png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); } - else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + + else if (chunk_name == png_IDAT) { - /* If we reach an IDAT chunk, this means we have read all of the - * header chunks, and we can start reading the image (or if this - * is called after the image has been read - we have an error). - */ - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before IDAT"); - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - png_error(png_ptr, "Missing PLTE before IDAT"); - - if (png_ptr->mode & PNG_HAVE_IDAT) - { - if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) - if (png_ptr->push_length == 0) - return; - - if (png_ptr->mode & PNG_AFTER_IDAT) - png_error(png_ptr, "Too many IDAT's found"); - } - png_ptr->idat_size = png_ptr->push_length; - png_ptr->mode |= PNG_HAVE_IDAT; png_ptr->process_mode = PNG_READ_IDAT_MODE; png_push_have_info(png_ptr, info_ptr); - png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.avail_out = + (uInt) PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; png_ptr->zstream.next_out = png_ptr->row_buf; return; } -#if defined(PNG_READ_gAMA_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (png_ptr->chunk_name == png_gAMA) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_sBIT_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) +#ifdef PNG_READ_sBIT_SUPPORTED + else if (png_ptr->chunk_name == png_sBIT) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_cHRM_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) +#ifdef PNG_READ_cHRM_SUPPORTED + else if (png_ptr->chunk_name == png_cHRM) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_sRGB_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_iCCP_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) +#ifdef PNG_READ_iCCP_SUPPORTED + else if (png_ptr->chunk_name == png_iCCP) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_sPLT_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_tRNS_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_bKGD_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_hIST_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_pHYs_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_oFFs_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); } #endif -#if defined(PNG_READ_pCAL_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_sCAL_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_tIME_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_tEXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } - png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + + png_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_zTXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } - png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + + png_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); } + #endif -#if defined(PNG_READ_iTXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) { if (png_ptr->push_length + 4 > png_ptr->buffer_size) { png_push_save_buffer(png_ptr); return; } - png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + + png_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); } + #endif else { @@ -497,50 +537,64 @@ png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) png_push_save_buffer(png_ptr); return; } - png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length, + PNG_HANDLE_CHUNK_AS_DEFAULT); } png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; } void /* PRIVATE */ -png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +png_push_crc_skip(png_structrp png_ptr, png_uint_32 skip) { png_ptr->process_mode = PNG_SKIP_MODE; png_ptr->skip_length = skip; } void /* PRIVATE */ -png_push_crc_finish(png_structp png_ptr) +png_push_crc_finish(png_structrp png_ptr) { if (png_ptr->skip_length && png_ptr->save_buffer_size) { - png_size_t save_size; + png_size_t save_size = png_ptr->save_buffer_size; + png_uint_32 skip_length = png_ptr->skip_length; + + /* We want the smaller of 'skip_length' and 'save_buffer_size', but + * they are of different types and we don't know which variable has the + * fewest bits. Carefully select the smaller and cast it to the type of + * the larger - this cannot overflow. Do not cast in the following test + * - it will break on either 16 or 64 bit platforms. + */ + if (skip_length < save_size) + save_size = (png_size_t)skip_length; - if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) - save_size = (png_size_t)png_ptr->skip_length; else - save_size = png_ptr->save_buffer_size; + skip_length = (png_uint_32)save_size; png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); - png_ptr->skip_length -= save_size; + png_ptr->skip_length -= skip_length; png_ptr->buffer_size -= save_size; png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } if (png_ptr->skip_length && png_ptr->current_buffer_size) { - png_size_t save_size; + png_size_t save_size = png_ptr->current_buffer_size; + png_uint_32 skip_length = png_ptr->skip_length; + + /* We want the smaller of 'skip_length' and 'current_buffer_size', here, + * the same problem exists as above and the same solution. + */ + if (skip_length < save_size) + save_size = (png_size_t)skip_length; - if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) - save_size = (png_size_t)png_ptr->skip_length; else - save_size = png_ptr->current_buffer_size; + skip_length = (png_uint_32)save_size; png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); - png_ptr->skip_length -= save_size; + png_ptr->skip_length -= skip_length; png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; @@ -558,12 +612,14 @@ png_push_crc_finish(png_structp png_ptr) } } -void PNGAPI +void PNGCBAPI png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) { png_bytep ptr; - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + ptr = buffer; if (png_ptr->save_buffer_size) { @@ -571,10 +627,11 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) if (length < png_ptr->save_buffer_size) save_size = length; + else save_size = png_ptr->save_buffer_size; - png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + memcpy(ptr, png_ptr->save_buffer_ptr, save_size); length -= save_size; ptr += save_size; png_ptr->buffer_size -= save_size; @@ -587,10 +644,11 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) if (length < png_ptr->current_buffer_size) save_size = length; + else save_size = png_ptr->current_buffer_size; - png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + memcpy(ptr, png_ptr->current_buffer_ptr, save_size); png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; @@ -598,46 +656,54 @@ png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) } void /* PRIVATE */ -png_push_save_buffer(png_structp png_ptr) +png_push_save_buffer(png_structrp png_ptr) { if (png_ptr->save_buffer_size) { if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) { - png_size_t i,istop; + png_size_t i, istop; png_bytep sp; png_bytep dp; istop = png_ptr->save_buffer_size; for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; - i < istop; i++, sp++, dp++) + i < istop; i++, sp++, dp++) { *dp = *sp; } } } if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > - png_ptr->save_buffer_max) + png_ptr->save_buffer_max) { png_size_t new_max; png_bytep old_buffer; if (png_ptr->save_buffer_size > PNG_SIZE_MAX - - (png_ptr->current_buffer_size + 256)) + (png_ptr->current_buffer_size + 256)) { - png_error(png_ptr, "Potential overflow of save_buffer"); + png_error(png_ptr, "Potential overflow of save_buffer"); } + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; old_buffer = png_ptr->save_buffer; - png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr, - (png_uint_32)new_max); - png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_ptr->save_buffer = (png_bytep)png_malloc_warn(png_ptr, + (png_size_t)new_max); + + if (png_ptr->save_buffer == NULL) + { + png_free(png_ptr, old_buffer); + png_error(png_ptr, "Insufficient memory for save_buffer"); + } + + memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); png_free(png_ptr, old_buffer); png_ptr->save_buffer_max = new_max; } if (png_ptr->current_buffer_size) { - png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); png_ptr->save_buffer_size += png_ptr->current_buffer_size; png_ptr->current_buffer_size = 0; @@ -647,7 +713,7 @@ png_push_save_buffer(png_structp png_ptr) } void /* PRIVATE */ -png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, +png_push_restore_buffer(png_structrp png_ptr, png_bytep buffer, png_size_t buffer_length) { png_ptr->current_buffer = buffer; @@ -657,15 +723,14 @@ png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, } void /* PRIVATE */ -png_push_read_IDAT(png_structp png_ptr) +png_push_read_IDAT(png_structrp png_ptr) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST PNG_IDAT; -#endif if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) { png_byte chunk_length[4]; + png_byte chunk_tag[4]; + /* TODO: this code can be commoned up with the same code in push_read */ if (png_ptr->buffer_size < 8) { png_push_save_buffer(png_ptr); @@ -673,62 +738,73 @@ png_push_read_IDAT(png_structp png_ptr) } png_push_fill_buffer(png_ptr, chunk_length, 4); - png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_ptr->push_length = png_get_uint_31(png_ptr, chunk_length); png_reset_crc(png_ptr); - png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_crc_read(png_ptr, chunk_tag, 4); + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(chunk_tag); png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; - if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if (png_ptr->chunk_name != png_IDAT) { png_ptr->process_mode = PNG_READ_CHUNK_MODE; - if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) png_error(png_ptr, "Not enough compressed data"); + return; } png_ptr->idat_size = png_ptr->push_length; } + if (png_ptr->idat_size && png_ptr->save_buffer_size) { - png_size_t save_size; + png_size_t save_size = png_ptr->save_buffer_size; + png_uint_32 idat_size = png_ptr->idat_size; + + /* We want the smaller of 'idat_size' and 'current_buffer_size', but they + * are of different types and we don't know which variable has the fewest + * bits. Carefully select the smaller and cast it to the type of the + * larger - this cannot overflow. Do not cast in the following test - it + * will break on either 16 or 64 bit platforms. + */ + if (idat_size < save_size) + save_size = (png_size_t)idat_size; - if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) - { - save_size = (png_size_t)png_ptr->idat_size; - /* check for overflow */ - if((png_uint_32)save_size != png_ptr->idat_size) - png_error(png_ptr, "save_size overflowed in pngpread"); - } else - save_size = png_ptr->save_buffer_size; + idat_size = (png_uint_32)save_size; png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); - if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) - png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); - png_ptr->idat_size -= save_size; + + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->idat_size -= idat_size; png_ptr->buffer_size -= save_size; png_ptr->save_buffer_size -= save_size; png_ptr->save_buffer_ptr += save_size; } + if (png_ptr->idat_size && png_ptr->current_buffer_size) { - png_size_t save_size; + png_size_t save_size = png_ptr->current_buffer_size; + png_uint_32 idat_size = png_ptr->idat_size; + + /* We want the smaller of 'idat_size' and 'current_buffer_size', but they + * are of different types and we don't know which variable has the fewest + * bits. Carefully select the smaller and cast it to the type of the + * larger - this cannot overflow. + */ + if (idat_size < save_size) + save_size = (png_size_t)idat_size; - if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) - { - save_size = (png_size_t)png_ptr->idat_size; - /* check for overflow */ - if((png_uint_32)save_size != png_ptr->idat_size) - png_error(png_ptr, "save_size overflowed in pngpread"); - } else - save_size = png_ptr->current_buffer_size; + idat_size = (png_uint_32)save_size; png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); - if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) - png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); - png_ptr->idat_size -= save_size; + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= idat_size; png_ptr->buffer_size -= save_size; png_ptr->current_buffer_size -= save_size; png_ptr->current_buffer_ptr += save_size; @@ -744,101 +820,170 @@ png_push_read_IDAT(png_structp png_ptr) png_crc_finish(png_ptr, 0); png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->zowner = 0; } } void /* PRIVATE */ -png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, +png_process_IDAT_data(png_structrp png_ptr, png_bytep buffer, png_size_t buffer_length) { - int ret; - - if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length) - png_error(png_ptr, "Extra compression data"); + /* The caller checks for a non-zero buffer length. */ + if (!(buffer_length > 0) || buffer == NULL) + png_error(png_ptr, "No IDAT data (internal error)"); + /* This routine must process all the data it has been given + * before returning, calling the row callback as required to + * handle the uncompressed results. + */ png_ptr->zstream.next_in = buffer; + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ png_ptr->zstream.avail_in = (uInt)buffer_length; - for(;;) + + /* Keep going until the decompressed data is all processed + * or the stream marked as finished. + */ + while (png_ptr->zstream.avail_in > 0 && + !(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) { - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); - if (ret != Z_OK) - { - if (ret == Z_STREAM_END) - { - if (png_ptr->zstream.avail_in) - png_error(png_ptr, "Extra compressed data"); - if (!(png_ptr->zstream.avail_out)) - { - png_push_process_row(png_ptr); - } + int ret; - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; - break; - } - else if (ret == Z_BUF_ERROR) - break; - else - png_error(png_ptr, "Decompression Error"); - } - if (!(png_ptr->zstream.avail_out)) + /* We have data for zlib, but we must check that zlib + * has someplace to put the results. It doesn't matter + * if we don't expect any results -- it may be the input + * data is just the LZ end code. + */ + if (!(png_ptr->zstream.avail_out > 0)) { - if (( -#if defined(PNG_READ_INTERLACING_SUPPORTED) - png_ptr->interlaced && png_ptr->pass > 6) || - (!png_ptr->interlaced && -#endif - png_ptr->row_number == png_ptr->num_rows)) - { - if (png_ptr->zstream.avail_in) - { - png_warning(png_ptr, "Too much data in IDAT chunks"); - } + /* TODO: WARNING: TRUNCATION ERROR: DANGER WILL ROBINSON: */ + png_ptr->zstream.avail_out = (uInt)(PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1); - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; - break; - } - png_push_process_row(png_ptr); - png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; png_ptr->zstream.next_out = png_ptr->row_buf; } - else - break; + + /* Using Z_SYNC_FLUSH here means that an unterminated + * LZ stream (a stream with a missing end code) can still + * be handled, otherwise (Z_NO_FLUSH) a future zlib + * implementation might defer output and therefore + * change the current behavior (see comments in inflate.c + * for why this doesn't happen at present with zlib 1.2.5). + */ + ret = inflate(&png_ptr->zstream, Z_SYNC_FLUSH); + + /* Check for any failure before proceeding. */ + if (ret != Z_OK && ret != Z_STREAM_END) + { + /* Terminate the decompression. */ + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->zowner = 0; + + /* This may be a truncated stream (missing or + * damaged end code). Treat that as a warning. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + png_warning(png_ptr, "Truncated compressed data in IDAT"); + + else + png_error(png_ptr, "Decompression error in IDAT"); + + /* Skip the check on unprocessed input */ + return; + } + + /* Did inflate output any data? */ + if (png_ptr->zstream.next_out != png_ptr->row_buf) + { + /* Is this unexpected data after the last row? + * If it is, artificially terminate the LZ output + * here. + */ + if (png_ptr->row_number >= png_ptr->num_rows || + png_ptr->pass > 6) + { + /* Extra data. */ + png_warning(png_ptr, "Extra compressed data in IDAT"); + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + png_ptr->zowner = 0; + + /* Do no more processing; skip the unprocessed + * input check below. + */ + return; + } + + /* Do we have a complete row? */ + if (png_ptr->zstream.avail_out == 0) + png_push_process_row(png_ptr); + } + + /* And check for the end of the stream. */ + if (ret == Z_STREAM_END) + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; } + + /* All the data should have been processed, if anything + * is left at this point we have bytes of IDAT data + * after the zlib end code. + */ + if (png_ptr->zstream.avail_in > 0) + png_warning(png_ptr, "Extra compression data in IDAT"); } void /* PRIVATE */ -png_push_process_row(png_structp png_ptr) +png_push_process_row(png_structrp png_ptr) { - png_ptr->row_info.color_type = png_ptr->color_type; - png_ptr->row_info.width = png_ptr->iwidth; - png_ptr->row_info.channels = png_ptr->channels; - png_ptr->row_info.bit_depth = png_ptr->bit_depth; - png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + /* 1.5.6: row_info moved out of png_struct to a local here. */ + png_row_info row_info; - png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, - png_ptr->row_info.width); + row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ + row_info.color_type = png_ptr->color_type; + row_info.bit_depth = png_ptr->bit_depth; + row_info.channels = png_ptr->channels; + row_info.pixel_depth = png_ptr->pixel_depth; + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - png_read_filter_row(png_ptr, &(png_ptr->row_info), - png_ptr->row_buf + 1, png_ptr->prev_row + 1, - (int)(png_ptr->row_buf[0])); + if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) + { + if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) + png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, + png_ptr->prev_row + 1, png_ptr->row_buf[0]); + else + png_error(png_ptr, "bad adaptive filter value"); + } - png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, - png_ptr->rowbytes + 1); + /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before + * 1.5.6, while the buffer really is this big in current versions of libpng + * it may not be in the future, so this was changed just to copy the + * interlaced row count: + */ + memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); - if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) - png_do_read_transformations(png_ptr); +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->transformations) + png_do_read_transformations(png_ptr, &row_info); +#endif -#if defined(PNG_READ_INTERLACING_SUPPORTED) - /* blow up interlaced rows to full size */ + /* The transformed pixel depth should match the depth now in row_info. */ + if (png_ptr->transformed_pixel_depth == 0) + { + png_ptr->transformed_pixel_depth = row_info.pixel_depth; + if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) + png_error(png_ptr, "progressive row overflow"); + } + + else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) + png_error(png_ptr, "internal progressive row size calculation error"); + + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { if (png_ptr->pass < 6) -/* old interface (pre-1.0.9): - png_do_read_interlace(&(png_ptr->row_info), - png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); - */ - png_do_read_interlace(png_ptr); + png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, + png_ptr->transformations); switch (png_ptr->pass) { @@ -848,31 +993,36 @@ png_push_process_row(png_structp png_ptr) for (i = 0; i < 8 && png_ptr->pass == 0; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); - png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */ + png_read_push_finish_row(png_ptr); /* Updates png_ptr->pass */ } - if (png_ptr->pass == 2) /* pass 1 might be empty */ + + if (png_ptr->pass == 2) /* Pass 1 might be empty */ { for (i = 0; i < 4 && png_ptr->pass == 2; i++) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } + if (png_ptr->pass == 4 && png_ptr->height <= 4) { for (i = 0; i < 2 && png_ptr->pass == 4; i++) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } + if (png_ptr->pass == 6 && png_ptr->height <= 4) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } + break; } + case 1: { int i; @@ -881,99 +1031,123 @@ png_push_process_row(png_structp png_ptr) png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } - if (png_ptr->pass == 2) /* skip top 4 generated rows */ + + if (png_ptr->pass == 2) /* Skip top 4 generated rows */ { for (i = 0; i < 4 && png_ptr->pass == 2; i++) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } + break; } + case 2: { int i; + for (i = 0; i < 4 && png_ptr->pass == 2; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } + for (i = 0; i < 4 && png_ptr->pass == 2; i++) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } - if (png_ptr->pass == 4) /* pass 3 might be empty */ + + if (png_ptr->pass == 4) /* Pass 3 might be empty */ { for (i = 0; i < 2 && png_ptr->pass == 4; i++) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } + break; } + case 3: { int i; + for (i = 0; i < 4 && png_ptr->pass == 3; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } - if (png_ptr->pass == 4) /* skip top two generated rows */ + + if (png_ptr->pass == 4) /* Skip top two generated rows */ { for (i = 0; i < 2 && png_ptr->pass == 4; i++) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } + break; } + case 4: { int i; + for (i = 0; i < 2 && png_ptr->pass == 4; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } + for (i = 0; i < 2 && png_ptr->pass == 4; i++) { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } - if (png_ptr->pass == 6) /* pass 5 might be empty */ + + if (png_ptr->pass == 6) /* Pass 5 might be empty */ { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } + break; } + case 5: { int i; + for (i = 0; i < 2 && png_ptr->pass == 5; i++) { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); } - if (png_ptr->pass == 6) /* skip top generated row */ + + if (png_ptr->pass == 6) /* Skip top generated row */ { - png_push_have_row(png_ptr, png_bytep_NULL); + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } + break; } + + default: case 6: { png_push_have_row(png_ptr, png_ptr->row_buf + 1); png_read_push_finish_row(png_ptr); + if (png_ptr->pass != 6) break; - png_push_have_row(png_ptr, png_bytep_NULL); + + png_push_have_row(png_ptr, NULL); png_read_push_finish_row(png_ptr); } } @@ -987,26 +1161,26 @@ png_push_process_row(png_structp png_ptr) } void /* PRIVATE */ -png_read_push_finish_row(png_structp png_ptr) +png_read_push_finish_row(png_structrp png_ptr) { -#ifdef PNG_USE_LOCAL_ARRAYS - /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ +#ifdef PNG_READ_INTERLACING_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}; + /* Start of interlace block */ + static PNG_CONST png_byte 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}; + /* Offset to next interlace block */ + static PNG_CONST png_byte 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}; + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte 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}; + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte 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}; + static PNG_CONST png_byte png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; */ #endif @@ -1014,562 +1188,91 @@ png_read_push_finish_row(png_structp png_ptr) if (png_ptr->row_number < png_ptr->num_rows) return; +#ifdef PNG_READ_INTERLACING_SUPPORTED if (png_ptr->interlaced) { png_ptr->row_number = 0; - png_memset_check(png_ptr, png_ptr->prev_row, 0, - png_ptr->rowbytes + 1); + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + do { png_ptr->pass++; if ((png_ptr->pass == 1 && png_ptr->width < 5) || (png_ptr->pass == 3 && png_ptr->width < 3) || (png_ptr->pass == 5 && png_ptr->width < 2)) - png_ptr->pass++; + png_ptr->pass++; if (png_ptr->pass > 7) png_ptr->pass--; + if (png_ptr->pass >= 7) break; png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; - - png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1; + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; if (png_ptr->transformations & PNG_INTERLACE) break; png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); } -} - -#if defined(PNG_READ_tEXt_SUPPORTED) -void /* PRIVATE */ -png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 - length) -{ - if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) - { - png_error(png_ptr, "Out of place tEXt"); - info_ptr = info_ptr; /* to quiet some compiler warnings */ - } - -#ifdef PNG_MAX_MALLOC_64K - png_ptr->skip_length = 0; /* This may not be necessary */ - - if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ - { - png_warning(png_ptr, "tEXt chunk too large to fit in memory"); - png_ptr->skip_length = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; - } -#endif - - png_ptr->current_text = (png_charp)png_malloc(png_ptr, - (png_uint_32)(length+1)); - png_ptr->current_text[length] = '\0'; - png_ptr->current_text_ptr = png_ptr->current_text; - png_ptr->current_text_size = (png_size_t)length; - png_ptr->current_text_left = (png_size_t)length; - png_ptr->process_mode = PNG_READ_tEXt_MODE; +#endif /* PNG_READ_INTERLACING_SUPPORTED */ } void /* PRIVATE */ -png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) -{ - if (png_ptr->buffer_size && png_ptr->current_text_left) - { - png_size_t text_size; - - if (png_ptr->buffer_size < png_ptr->current_text_left) - text_size = png_ptr->buffer_size; - else - text_size = png_ptr->current_text_left; - png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); - png_ptr->current_text_left -= text_size; - png_ptr->current_text_ptr += text_size; - } - if (!(png_ptr->current_text_left)) - { - png_textp text_ptr; - png_charp text; - png_charp key; - int ret; - - if (png_ptr->buffer_size < 4) - { - png_push_save_buffer(png_ptr); - return; - } - - png_push_crc_finish(png_ptr); - -#if defined(PNG_MAX_MALLOC_64K) - if (png_ptr->skip_length) - return; -#endif - - key = png_ptr->current_text; - - for (text = key; *text; text++) - /* empty loop */ ; - - if (text < key + png_ptr->current_text_size) - text++; - - text_ptr = (png_textp)png_malloc(png_ptr, - (png_uint_32)png_sizeof(png_text)); - text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; - text_ptr->key = key; -#ifdef PNG_iTXt_SUPPORTED - text_ptr->lang = NULL; - text_ptr->lang_key = NULL; -#endif - text_ptr->text = text; - - ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); - - png_free(png_ptr, key); - png_free(png_ptr, text_ptr); - png_ptr->current_text = NULL; - - if (ret) - png_warning(png_ptr, "Insufficient memory to store text chunk."); - } -} -#endif - -#if defined(PNG_READ_zTXt_SUPPORTED) -void /* PRIVATE */ -png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 - length) -{ - if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) - { - png_error(png_ptr, "Out of place zTXt"); - info_ptr = info_ptr; /* to quiet some compiler warnings */ - } - -#ifdef PNG_MAX_MALLOC_64K - /* We can't handle zTXt chunks > 64K, since we don't have enough space - * to be able to store the uncompressed data. Actually, the threshold - * is probably around 32K, but it isn't as definite as 64K is. - */ - if (length > (png_uint_32)65535L) - { - png_warning(png_ptr, "zTXt chunk too large to fit in memory"); - png_push_crc_skip(png_ptr, length); - return; - } -#endif - - png_ptr->current_text = (png_charp)png_malloc(png_ptr, - (png_uint_32)(length+1)); - png_ptr->current_text[length] = '\0'; - png_ptr->current_text_ptr = png_ptr->current_text; - png_ptr->current_text_size = (png_size_t)length; - png_ptr->current_text_left = (png_size_t)length; - png_ptr->process_mode = PNG_READ_zTXt_MODE; -} - -void /* PRIVATE */ -png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) -{ - if (png_ptr->buffer_size && png_ptr->current_text_left) - { - png_size_t text_size; - - if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) - text_size = png_ptr->buffer_size; - else - text_size = png_ptr->current_text_left; - png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); - png_ptr->current_text_left -= text_size; - png_ptr->current_text_ptr += text_size; - } - if (!(png_ptr->current_text_left)) - { - png_textp text_ptr; - png_charp text; - png_charp key; - int ret; - png_size_t text_size, key_size; - - if (png_ptr->buffer_size < 4) - { - png_push_save_buffer(png_ptr); - return; - } - - png_push_crc_finish(png_ptr); - - key = png_ptr->current_text; - - for (text = key; *text; text++) - /* empty loop */ ; - - /* zTXt can't have zero text */ - if (text >= key + png_ptr->current_text_size) - { - png_ptr->current_text = NULL; - png_free(png_ptr, key); - return; - } - - text++; - - if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */ - { - png_ptr->current_text = NULL; - png_free(png_ptr, key); - return; - } - - text++; - - png_ptr->zstream.next_in = (png_bytep )text; - png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - - (text - key)); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - - key_size = text - key; - text_size = 0; - text = NULL; - ret = Z_STREAM_END; - - while (png_ptr->zstream.avail_in) - { - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) - { - inflateReset(&png_ptr->zstream); - png_ptr->zstream.avail_in = 0; - png_ptr->current_text = NULL; - png_free(png_ptr, key); - png_free(png_ptr, text); - return; - } - if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) - { - if (text == NULL) - { - text = (png_charp)png_malloc(png_ptr, - (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out - + key_size + 1)); - png_memcpy(text + key_size, png_ptr->zbuf, - png_ptr->zbuf_size - png_ptr->zstream.avail_out); - png_memcpy(text, key, key_size); - text_size = key_size + png_ptr->zbuf_size - - png_ptr->zstream.avail_out; - *(text + text_size) = '\0'; - } - else - { - png_charp tmp; - - tmp = text; - text = (png_charp)png_malloc(png_ptr, text_size + - (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out - + 1)); - png_memcpy(text, tmp, text_size); - png_free(png_ptr, tmp); - png_memcpy(text + text_size, png_ptr->zbuf, - png_ptr->zbuf_size - png_ptr->zstream.avail_out); - text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; - *(text + text_size) = '\0'; - } - if (ret != Z_STREAM_END) - { - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - } - } - else - { - break; - } - - if (ret == Z_STREAM_END) - break; - } - - inflateReset(&png_ptr->zstream); - png_ptr->zstream.avail_in = 0; - - if (ret != Z_STREAM_END) - { - png_ptr->current_text = NULL; - png_free(png_ptr, key); - png_free(png_ptr, text); - return; - } - - png_ptr->current_text = NULL; - png_free(png_ptr, key); - key = text; - text += key_size; - - text_ptr = (png_textp)png_malloc(png_ptr, - (png_uint_32)png_sizeof(png_text)); - text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; - text_ptr->key = key; -#ifdef PNG_iTXt_SUPPORTED - text_ptr->lang = NULL; - text_ptr->lang_key = NULL; -#endif - text_ptr->text = text; - - ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); - - png_free(png_ptr, key); - png_free(png_ptr, text_ptr); - - if (ret) - png_warning(png_ptr, "Insufficient memory to store text chunk."); - } -} -#endif - -#if defined(PNG_READ_iTXt_SUPPORTED) -void /* PRIVATE */ -png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 - length) -{ - if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) - { - png_error(png_ptr, "Out of place iTXt"); - info_ptr = info_ptr; /* to quiet some compiler warnings */ - } - -#ifdef PNG_MAX_MALLOC_64K - png_ptr->skip_length = 0; /* This may not be necessary */ - - if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ - { - png_warning(png_ptr, "iTXt chunk too large to fit in memory"); - png_ptr->skip_length = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; - } -#endif - - png_ptr->current_text = (png_charp)png_malloc(png_ptr, - (png_uint_32)(length+1)); - png_ptr->current_text[length] = '\0'; - png_ptr->current_text_ptr = png_ptr->current_text; - png_ptr->current_text_size = (png_size_t)length; - png_ptr->current_text_left = (png_size_t)length; - png_ptr->process_mode = PNG_READ_iTXt_MODE; -} - -void /* PRIVATE */ -png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) -{ - - if (png_ptr->buffer_size && png_ptr->current_text_left) - { - png_size_t text_size; - - if (png_ptr->buffer_size < png_ptr->current_text_left) - text_size = png_ptr->buffer_size; - else - text_size = png_ptr->current_text_left; - png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); - png_ptr->current_text_left -= text_size; - png_ptr->current_text_ptr += text_size; - } - if (!(png_ptr->current_text_left)) - { - png_textp text_ptr; - png_charp key; - int comp_flag; - png_charp lang; - png_charp lang_key; - png_charp text; - int ret; - - if (png_ptr->buffer_size < 4) - { - png_push_save_buffer(png_ptr); - return; - } - - png_push_crc_finish(png_ptr); - -#if defined(PNG_MAX_MALLOC_64K) - if (png_ptr->skip_length) - return; -#endif - - key = png_ptr->current_text; - - for (lang = key; *lang; lang++) - /* empty loop */ ; - - if (lang < key + png_ptr->current_text_size - 3) - lang++; - - comp_flag = *lang++; - lang++; /* skip comp_type, always zero */ - - for (lang_key = lang; *lang_key; lang_key++) - /* empty loop */ ; - lang_key++; /* skip NUL separator */ - - text=lang_key; - if (lang_key < key + png_ptr->current_text_size - 1) - { - for (; *text; text++) - /* empty loop */ ; - } - - if (text < key + png_ptr->current_text_size) - text++; - - text_ptr = (png_textp)png_malloc(png_ptr, - (png_uint_32)png_sizeof(png_text)); - text_ptr->compression = comp_flag + 2; - text_ptr->key = key; - text_ptr->lang = lang; - text_ptr->lang_key = lang_key; - text_ptr->text = text; - text_ptr->text_length = 0; - text_ptr->itxt_length = png_strlen(text); - - ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); - - png_ptr->current_text = NULL; - - png_free(png_ptr, text_ptr); - if (ret) - png_warning(png_ptr, "Insufficient memory to store iTXt chunk."); - } -} -#endif - -/* This function is called when we haven't found a handler for this - * chunk. If there isn't a problem with the chunk itself (ie a bad chunk - * name or a critical chunk), the chunk is (currently) silently ignored. - */ -void /* PRIVATE */ -png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 - length) -{ - png_uint_32 skip=0; - png_check_chunk_name(png_ptr, png_ptr->chunk_name); - - if (!(png_ptr->chunk_name[0] & 0x20)) - { -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) - if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != - PNG_HANDLE_CHUNK_ALWAYS -#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) - && png_ptr->read_user_chunk_fn == NULL -#endif - ) -#endif - png_chunk_error(png_ptr, "unknown critical chunk"); - - info_ptr = info_ptr; /* to quiet some compiler warnings */ - } - -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) - if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) - { -#ifdef PNG_MAX_MALLOC_64K - if (length > (png_uint_32)65535L) - { - png_warning(png_ptr, "unknown chunk too large to fit in memory"); - skip = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; - } -#endif - png_strncpy((png_charp)png_ptr->unknown_chunk.name, - (png_charp)png_ptr->chunk_name, 5); - png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); - png_ptr->unknown_chunk.size = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); -#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) - if(png_ptr->read_user_chunk_fn != NULL) - { - /* callback to user unknown chunk handler */ - int ret; - ret = (*(png_ptr->read_user_chunk_fn)) - (png_ptr, &png_ptr->unknown_chunk); - if (ret < 0) - png_chunk_error(png_ptr, "error in user chunk"); - if (ret == 0) - { - if (!(png_ptr->chunk_name[0] & 0x20)) - if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != - PNG_HANDLE_CHUNK_ALWAYS) - png_chunk_error(png_ptr, "unknown critical chunk"); - png_set_unknown_chunks(png_ptr, info_ptr, - &png_ptr->unknown_chunk, 1); - } - } -#else - png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); -#endif - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; - } - else -#endif - skip=length; - png_push_crc_skip(png_ptr, skip); -} - -void /* PRIVATE */ -png_push_have_info(png_structp png_ptr, png_infop info_ptr) +png_push_have_info(png_structrp png_ptr, png_inforp info_ptr) { if (png_ptr->info_fn != NULL) (*(png_ptr->info_fn))(png_ptr, info_ptr); } void /* PRIVATE */ -png_push_have_end(png_structp png_ptr, png_infop info_ptr) +png_push_have_end(png_structrp png_ptr, png_inforp info_ptr) { if (png_ptr->end_fn != NULL) (*(png_ptr->end_fn))(png_ptr, info_ptr); } void /* PRIVATE */ -png_push_have_row(png_structp png_ptr, png_bytep row) +png_push_have_row(png_structrp png_ptr, png_bytep row) { if (png_ptr->row_fn != NULL) (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, (int)png_ptr->pass); } +#ifdef PNG_READ_INTERLACING_SUPPORTED void PNGAPI -png_progressive_combine_row (png_structp png_ptr, - png_bytep old_row, png_bytep new_row) +png_progressive_combine_row(png_const_structrp png_ptr, png_bytep old_row, + png_const_bytep new_row) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST int FARDATA png_pass_dsp_mask[7] = - {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; -#endif - if(png_ptr == NULL) return; - if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ - png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); + if (png_ptr == NULL) + return; + + /* new_row is a flag here - if it is NULL then the app callback was called + * from an empty row (see the calls to png_struct::row_fn below), otherwise + * it must be png_ptr->row_buf+1 + */ + if (new_row != NULL) + png_combine_row(png_ptr, old_row, 1/*display*/); } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ void PNGAPI -png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, - png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, - png_progressive_end_ptr end_fn) +png_set_progressive_read_fn(png_structrp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) { - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + png_ptr->info_fn = info_fn; png_ptr->row_fn = row_fn; png_ptr->end_fn = end_fn; @@ -1578,9 +1281,11 @@ png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, } png_voidp PNGAPI -png_get_progressive_ptr(png_structp png_ptr) +png_get_progressive_ptr(png_const_structrp png_ptr) { - if(png_ptr == NULL) return (NULL); + if (png_ptr == NULL) + return (NULL); + return png_ptr->io_ptr; } #endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpriv.h b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpriv.h new file mode 100644 index 0000000..a93a877 --- /dev/null +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngpriv.h @@ -0,0 +1,1904 @@ + +/* pngpriv.h - private declarations for use inside libpng + * + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2013 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.) + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* The symbols declared in this file (including the functions declared + * as extern) are PRIVATE. They are not part of the libpng public + * interface, and are not recommended for use by regular applications. + * Some of them may become public in the future; others may stay private, + * change in an incompatible way, or even disappear. + * Although the libpng users are not forbidden to include this header, + * they should be well aware of the issues that may arise from doing so. + */ + +#ifndef PNGPRIV_H +#define PNGPRIV_H + +/* Feature Test Macros. The following are defined here to ensure that correctly + * implemented libraries reveal the APIs libpng needs to build and hide those + * that are not needed and potentially damaging to the compilation. + * + * Feature Test Macros must be defined before any system header is included (see + * POSIX 1003.1 2.8.2 "POSIX Symbols." + * + * These macros only have an effect if the operating system supports either + * POSIX 1003.1 or C99, or both. On other operating systems (particularly + * Windows/Visual Studio) there is no effect; the OS specific tests below are + * still required (as of 2011-05-02.) + */ +#define _POSIX_SOURCE 1 /* Just the POSIX 1003.1 and C89 APIs */ + +#ifndef PNG_VERSION_INFO_ONLY +/* Standard library headers not required by png.h: */ +# include +# include +#endif + +#define PNGLIB_BUILD /*libpng is being built, not used*/ + +/* If HAVE_CONFIG_H is defined during the build then the build system must + * provide an appropriate "config.h" file on the include path. The header file + * must provide definitions as required below (search for "HAVE_CONFIG_H"); + * see configure.ac for more details of the requirements. The macro + * "PNG_NO_CONFIG_H" is provided for maintainers to test for dependencies on + * 'configure'; define this macro to prevent the configure build including the + * configure generated config.h. Libpng is expected to compile without *any* + * special build system support on a reasonably ANSI-C compliant system. + */ +#if defined(HAVE_CONFIG_H) && !defined(PNG_NO_CONFIG_H) +# include + + /* Pick up the definition of 'restrict' from config.h if it was read: */ +# define PNG_RESTRICT restrict +#endif + +/* To support symbol prefixing it is necessary to know *before* including png.h + * whether the fixed point (and maybe other) APIs are exported, because if they + * are not internal definitions may be required. This is handled below just + * before png.h is included, but load the configuration now if it is available. + */ +#ifndef PNGLCONF_H +# include "pnglibconf.h" +#endif + +/* Local renames may change non-exported API functions from png.h */ +#if defined(PNG_PREFIX) && !defined(PNGPREFIX_H) +# include "pngprefix.h" +#endif + +#ifdef PNG_USER_CONFIG +# include "pngusr.h" + /* These should have been defined in pngusr.h */ +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD "Custom libpng build" +# endif +# ifndef PNG_USER_DLLFNAME_POSTFIX +# define PNG_USER_DLLFNAME_POSTFIX "Cb" +# endif +#endif + +/* Is this a build of a DLL where compilation of the object modules requires + * different preprocessor settings to those required for a simple library? If + * so PNG_BUILD_DLL must be set. + * + * If libpng is used inside a DLL but that DLL does not export the libpng APIs + * PNG_BUILD_DLL must not be set. To avoid the code below kicking in build a + * static library of libpng then link the DLL against that. + */ +#ifndef PNG_BUILD_DLL +# ifdef DLL_EXPORT + /* This is set by libtool when files are compiled for a DLL; libtool + * always compiles twice, even on systems where it isn't necessary. Set + * PNG_BUILD_DLL in case it is necessary: + */ +# define PNG_BUILD_DLL +# else +# ifdef _WINDLL + /* This is set by the Microsoft Visual Studio IDE in projects that + * build a DLL. It can't easily be removed from those projects (it + * isn't visible in the Visual Studio UI) so it is a fairly reliable + * indication that PNG_IMPEXP needs to be set to the DLL export + * attributes. + */ +# define PNG_BUILD_DLL +# else +# ifdef __DLL__ + /* This is set by the Borland C system when compiling for a DLL + * (as above.) + */ +# define PNG_BUILD_DLL +# else + /* Add additional compiler cases here. */ +# endif +# endif +# endif +#endif /* Setting PNG_BUILD_DLL if required */ + +/* See pngconf.h for more details: the builder of the library may set this on + * the command line to the right thing for the specific compilation system or it + * may be automagically set above (at present we know of no system where it does + * need to be set on the command line.) + * + * PNG_IMPEXP must be set here when building the library to prevent pngconf.h + * setting it to the "import" setting for a DLL build. + */ +#ifndef PNG_IMPEXP +# ifdef PNG_BUILD_DLL +# define PNG_IMPEXP PNG_DLL_EXPORT +# else + /* Not building a DLL, or the DLL doesn't require specific export + * definitions. + */ +# define PNG_IMPEXP +# endif +#endif + +/* No warnings for private or deprecated functions in the build: */ +#ifndef PNG_DEPRECATED +# define PNG_DEPRECATED +#endif +#ifndef PNG_PRIVATE +# define PNG_PRIVATE +#endif + +/* Symbol preprocessing support. + * + * To enable listing global, but internal, symbols the following macros should + * always be used to declare an extern data or function object in this file. + */ +#ifndef PNG_INTERNAL_DATA +# define PNG_INTERNAL_DATA(type, name, array) extern type name array +#endif + +#ifndef PNG_INTERNAL_FUNCTION +# define PNG_INTERNAL_FUNCTION(type, name, args, attributes)\ + extern PNG_FUNCTION(type, name, args, PNG_EMPTY attributes) +#endif + +/* If floating or fixed point APIs are disabled they may still be compiled + * internally. To handle this make sure they are declared as the appropriate + * internal extern function (otherwise the symbol prefixing stuff won't work and + * the functions will be used without definitions.) + * + * NOTE: although all the API functions are declared here they are not all + * actually built! Because the declarations are still made it is necessary to + * fake out types that they depend on. + */ +#ifndef PNG_FP_EXPORT +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FP_EXPORT(ordinal, type, name, args)\ + PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY); +# ifndef PNG_VERSION_INFO_ONLY + typedef struct png_incomplete png_double; + typedef png_double* png_doublep; + typedef const png_double* png_const_doublep; + typedef png_double** png_doublepp; +# endif +# endif +#endif +#ifndef PNG_FIXED_EXPORT +# ifndef PNG_FIXED_POINT_SUPPORTED +# define PNG_FIXED_EXPORT(ordinal, type, name, args)\ + PNG_INTERNAL_FUNCTION(type, name, args, PNG_EMPTY); +# endif +#endif + +#include "png.h" + +/* pngconf.h does not set PNG_DLL_EXPORT unless it is required, so: */ +#ifndef PNG_DLL_EXPORT +# define PNG_DLL_EXPORT +#endif + +/* SECURITY and SAFETY: + * + * By default libpng is built without any internal limits on image size, + * individual heap (png_malloc) allocations or the total amount of memory used. + * If PNG_SAFE_LIMITS_SUPPORTED is defined, however, the limits below are used + * (unless individually overridden). These limits are believed to be fairly + * safe, but builders of secure systems should verify the values against the + * real system capabilities. + */ +#ifdef PNG_SAFE_LIMITS_SUPPORTED + /* 'safe' limits */ +# ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000 +# endif +# ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000 +# endif +# ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 128 +# endif +# ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 8000000 +# endif +#else + /* values for no limits */ +# ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 0x7fffffff +# endif +# ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 0x7fffffff +# endif +# ifndef PNG_USER_CHUNK_CACHE_MAX +# define PNG_USER_CHUNK_CACHE_MAX 0 +# endif +# ifndef PNG_USER_CHUNK_MALLOC_MAX +# define PNG_USER_CHUNK_MALLOC_MAX 0 +# endif +#endif + +/* Moved to pngpriv.h at libpng-1.5.0 */ +/* NOTE: some of these may have been used in external applications as + * these definitions were exposed in pngconf.h prior to 1.5. + */ + +/* 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. + * + * zlib provides 'MAXSEG_64K' which, if defined, indicates the + * same limit and pngconf.h (already included) sets the limit + * if certain operating systems are detected. + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +#ifndef PNG_UNUSED +/* Unused formal parameter warnings are silenced using the following macro + * which is expected to have no bad effects on performance (optimizing + * compilers will probably remove it entirely). Note that if you replace + * it with something other than whitespace, you must include the terminating + * semicolon. + */ +# define PNG_UNUSED(param) (void)param; +#endif + +/* 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 + +/* If warnings or errors are turned off the code is disabled or redirected here. + * From 1.5.4 functions have been added to allow very limited formatting of + * error and warning messages - this code will also be disabled here. + */ +#ifdef PNG_WARNINGS_SUPPORTED +# define PNG_WARNING_PARAMETERS(p) png_warning_parameters p; +#else +# define png_warning(s1,s2) ((void)(s1)) +# define png_chunk_warning(s1,s2) ((void)(s1)) +# define png_warning_parameter(p,number,string) ((void)0) +# define png_warning_parameter_unsigned(p,number,format,value) ((void)0) +# define png_warning_parameter_signed(p,number,format,value) ((void)0) +# define png_formatted_warning(pp,p,message) ((void)(pp)) +# define PNG_WARNING_PARAMETERS(p) +#endif +#ifndef PNG_ERROR_TEXT_SUPPORTED +# define png_error(s1,s2) png_err(s1) +# define png_chunk_error(s1,s2) png_err(s1) +# define png_fixed_error(s1,s2) png_err(s1) +#endif + +/* C allows up-casts from (void*) to any pointer and (const void*) to any + * pointer to a const object. C++ regards this as a type error and requires an + * explicit, static, cast and provides the static_cast<> rune to ensure that + * const is not cast away. + */ +#ifdef __cplusplus +# define png_voidcast(type, value) static_cast(value) +# define png_constcast(type, value) const_cast(value) +# define png_aligncast(type, value) \ + static_cast(static_cast(value)) +# define png_aligncastconst(type, value) \ + static_cast(static_cast(value)) +#else +# define png_voidcast(type, value) (value) +# define png_constcast(type, value) ((type)(value)) +# define png_aligncast(type, value) ((void*)(value)) +# define png_aligncastconst(type, value) ((const void*)(value)) +#endif /* __cplusplus */ + +/* Some fixed point APIs are still required even if not exported because + * they get used by the corresponding floating point APIs. This magic + * deals with this: + */ +#ifdef PNG_FIXED_POINT_SUPPORTED +# define PNGFAPI PNGAPI +#else +# define PNGFAPI /* PRIVATE */ +#endif + +#ifndef PNG_VERSION_INFO_ONLY +/* 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) ||\ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED) + /* png.c requires the following ANSI-C constants if the conversion of + * floating point to ASCII is implemented therein: + * + * DBL_DIG Maximum number of decimal digits (can be set to any constant) + * DBL_MIN Smallest normalized fp number (can be set to an arbitrary value) + * DBL_MAX Maximum floating point number (can be set to an arbitrary value) + */ +# include + +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +#if defined(WIN32) || defined(_Windows) || defined(_WINDOWS) || \ + defined(_WIN32) || defined(__WIN32__) +# include /* defines _WINDOWS_ macro */ +#endif +#endif /* PNG_VERSION_INFO_ONLY */ + +/* Moved here around 1.5.0beta36 from pngconf.h */ +/* Users may want to use these so they are not private. Any library + * functions that are passed far data must be model-independent. + */ + +/* Memory model/platform independent fns */ +#ifndef PNG_ABORT +# ifdef _WINDOWS_ +# define PNG_ABORT() ExitProcess(0) +# else +# define PNG_ABORT() abort() +# endif +#endif + +/* These macros may need to be architecture dependent. */ +#define PNG_ALIGN_NONE 0 /* do not use data alignment */ +#define PNG_ALIGN_ALWAYS 1 /* assume unaligned accesses are OK */ +#ifdef offsetof +# define PNG_ALIGN_OFFSET 2 /* use offsetof to determine alignment */ +#else +# define PNG_ALIGN_OFFSET -1 /* prevent the use of this */ +#endif +#define PNG_ALIGN_SIZE 3 /* use sizeof to determine alignment */ + +#ifndef PNG_ALIGN_TYPE + /* Default to using aligned access optimizations and requiring alignment to a + * multiple of the data type size. Override in a compiler specific fashion + * if necessary by inserting tests here: + */ +# define PNG_ALIGN_TYPE PNG_ALIGN_SIZE +#endif + +#if PNG_ALIGN_TYPE == PNG_ALIGN_SIZE + /* This is used because in some compiler implementations non-aligned + * structure members are supported, so the offsetof approach below fails. + * Set PNG_ALIGN_SIZE=0 for compiler combinations where unaligned access + * is good for performance. Do not do this unless you have tested the result + * and understand it. + */ +# define png_alignof(type) (sizeof (type)) +#else +# if PNG_ALIGN_TYPE == PNG_ALIGN_OFFSET +# define png_alignof(type) offsetof(struct{char c; type t;}, t) +# else +# if PNG_ALIGN_TYPE == PNG_ALIGN_ALWAYS +# define png_alignof(type) (1) +# endif + /* Else leave png_alignof undefined to prevent use thereof */ +# endif +#endif + +/* This implicitly assumes alignment is always to a power of 2. */ +#ifdef png_alignof +# define png_isaligned(ptr, type)\ + ((((const char*)ptr-(const char*)0) & (png_alignof(type)-1)) == 0) +#else +# define png_isaligned(ptr, type) 0 +#endif + +/* End of memory model/platform independent support */ +/* End of 1.5.0beta36 move from pngconf.h */ + +/* CONSTANTS and UTILITY MACROS + * These are used internally by libpng and not exposed in the API + */ + +/* Various modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. Three of these + * are defined in png.h because they need to be visible to applications + * that call png_set_unknown_chunk(). + */ +/* #define PNG_HAVE_IHDR 0x01 (defined in png.h) */ +/* #define PNG_HAVE_PLTE 0x02 (defined in png.h) */ +#define PNG_HAVE_IDAT 0x04 +/* #define PNG_AFTER_IDAT 0x08 (defined in png.h) */ +#define PNG_HAVE_IEND 0x10 + /* 0x20 (unused) */ + /* 0x40 (unused) */ + /* 0x80 (unused) */ +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + /* 0x4000 (unused) */ +#define PNG_IS_READ_STRUCT 0x8000 /* Else is a write struct */ + +/* Flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_QUANTIZE 0x0040 +#define PNG_COMPOSE 0x0080 /* Was PNG_BACKGROUND */ +#define PNG_BACKGROUND_EXPAND 0x0100 +#define PNG_EXPAND_16 0x0200 /* Added to libpng 1.5.2 */ +#define PNG_16_TO_8 0x0400 /* Becomes 'chop' in 1.5.4 */ +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000 +#define PNG_PACKSWAP 0x10000 +#define PNG_SWAP_ALPHA 0x20000 +#define PNG_STRIP_ALPHA 0x40000 +#define PNG_INVERT_ALPHA 0x80000 +#define PNG_USER_TRANSFORM 0x100000 +#define PNG_RGB_TO_GRAY_ERR 0x200000 +#define PNG_RGB_TO_GRAY_WARN 0x400000 +#define PNG_RGB_TO_GRAY 0x600000 /* two bits, RGB_TO_GRAY_ERR|WARN */ +#define PNG_ENCODE_ALPHA 0x800000 /* Added to libpng-1.5.4 */ +#define PNG_ADD_ALPHA 0x1000000 /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000 /* Added to libpng-1.2.9 */ +#define PNG_SCALE_16_TO_8 0x4000000 /* Added to libpng-1.5.4 */ + /* 0x8000000 unused */ + /* 0x10000000 unused */ + /* 0x20000000 unused */ + /* 0x40000000 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_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#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_ZSTREAM_INITIALIZED 0x0002 /* Added to libpng-1.6.0 */ + /* 0x0004 unused */ +#define PNG_FLAG_ZSTREAM_ENDED 0x0008 /* Added to libpng-1.6.0 */ + /* 0x0010 unused */ + /* 0x0020 unused */ +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_ASSUME_sRGB 0x1000 /* Added to libpng-1.5.4 */ +#define PNG_FLAG_OPTIMIZE_ALPHA 0x2000 /* Added to libpng-1.5.4 */ +#define PNG_FLAG_DETECT_UNINITIALIZED 0x4000 /* Added to libpng-1.5.4 */ +/* #define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000 */ +/* #define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000 */ +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000 +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000 +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000 +#define PNG_FLAG_BENIGN_ERRORS_WARN 0x100000 /* Added to libpng-1.4.0 */ +#define PNG_FLAG_APP_WARNINGS_WARN 0x200000 /* Added to libpng-1.6.0 */ +#define PNG_FLAG_APP_ERRORS_WARN 0x400000 /* Added to libpng-1.6.0 */ + /* 0x800000 unused */ + /* 0x1000000 unused */ + /* 0x2000000 unused */ + /* 0x4000000 unused */ + /* 0x8000000 unused */ + /* 0x10000000 unused */ + /* 0x20000000 unused */ + /* 0x40000000 unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* Save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.6.0: scale a 16-bit value in the range 0..65535 to 0..255 + * by dividing by 257 *with rounding*. This macro is exact for the given range. + * See the discourse in pngrtran.c png_do_scale_16_to_8. The values in the + * macro were established by experiment (modifying the added value). The macro + * has a second variant that takes a value already scaled by 255 and divides by + * 65535 - this has a maximum error of .502. Over the range 0..65535*65535 it + * only gives off-by-one errors and only for 0.5% (1 in 200) of the values. + */ +#define PNG_DIV65535(v24) (((v24) + 32895) >> 16) +#define PNG_DIV257(v16) PNG_DIV65535((png_uint_32)(v16) * 255) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((png_size_t)(width) * (((png_size_t)(pixel_bits)) >> 3)) : \ + (( ((png_size_t)(width) * ((png_size_t)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + * ideal-delta..ideal+delta. Each argument is evaluated twice. + * "ideal" and "delta" should be constants, normally simple + * integers, "value" a variable. Added to libpng-1.2.6 JB + */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* Conversions between fixed and floating point, only defined if + * required (to make sure the code doesn't accidentally use float + * when it is supposedly disabled.) + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +/* The floating point conversion can't overflow, though it can and + * does lose accuracy relative to the original fixed point value. + * In practice this doesn't matter because png_fixed_point only + * stores numbers with very low precision. The png_ptr and s + * arguments are unused by default but are there in case error + * checking becomes a requirement. + */ +#define png_float(png_ptr, fixed, s) (.00001 * (fixed)) + +/* The fixed point conversion performs range checking and evaluates + * its argument multiple times, so must be used with care. The + * range checking uses the PNG specification values for a signed + * 32 bit fixed point value except that the values are deliberately + * rounded-to-zero to an integral value - 21474 (21474.83 is roughly + * (2^31-1) * 100000). 's' is a string that describes the value being + * converted. + * + * NOTE: this macro will raise a png_error if the range check fails, + * therefore it is normally only appropriate to use this on values + * that come from API calls or other sources where an out of range + * error indicates a programming error, not a data error! + * + * NOTE: by default this is off - the macro is not used - because the + * function call saves a lot of code. + */ +#ifdef PNG_FIXED_POINT_MACRO_SUPPORTED +#define png_fixed(png_ptr, fp, s) ((fp) <= 21474 && (fp) >= -21474 ?\ + ((png_fixed_point)(100000 * (fp))) : (png_fixed_error(png_ptr, s),0)) +#endif +/* else the corresponding function is defined below, inside the scope of the + * cplusplus test. + */ +#endif + +/* Constants for known chunk types. If you need to add a chunk, define the name + * here. For historical reasons these constants have the form png_; i.e. + * the prefix is lower case. Please use decimal values as the parameters to + * match the ISO PNG specification and to avoid relying on the C locale + * interpretation of character values. + * + * Prior to 1.5.6 these constants were strings, as of 1.5.6 png_uint_32 values + * are computed and a new macro (PNG_STRING_FROM_CHUNK) added to allow a string + * to be generated if required. + * + * PNG_32b correctly produces a value shifted by up to 24 bits, even on + * architectures where (int) is only 16 bits. + */ +#define PNG_32b(b,s) ((png_uint_32)(b) << (s)) +#define PNG_CHUNK(b1,b2,b3,b4) \ + (PNG_32b(b1,24) | PNG_32b(b2,16) | PNG_32b(b3,8) | PNG_32b(b4,0)) + +#define png_IHDR PNG_CHUNK( 73, 72, 68, 82) +#define png_IDAT PNG_CHUNK( 73, 68, 65, 84) +#define png_IEND PNG_CHUNK( 73, 69, 78, 68) +#define png_PLTE PNG_CHUNK( 80, 76, 84, 69) +#define png_bKGD PNG_CHUNK( 98, 75, 71, 68) +#define png_cHRM PNG_CHUNK( 99, 72, 82, 77) +#define png_gAMA PNG_CHUNK(103, 65, 77, 65) +#define png_hIST PNG_CHUNK(104, 73, 83, 84) +#define png_iCCP PNG_CHUNK(105, 67, 67, 80) +#define png_iTXt PNG_CHUNK(105, 84, 88, 116) +#define png_oFFs PNG_CHUNK(111, 70, 70, 115) +#define png_pCAL PNG_CHUNK(112, 67, 65, 76) +#define png_sCAL PNG_CHUNK(115, 67, 65, 76) +#define png_pHYs PNG_CHUNK(112, 72, 89, 115) +#define png_sBIT PNG_CHUNK(115, 66, 73, 84) +#define png_sPLT PNG_CHUNK(115, 80, 76, 84) +#define png_sRGB PNG_CHUNK(115, 82, 71, 66) +#define png_sTER PNG_CHUNK(115, 84, 69, 82) +#define png_tEXt PNG_CHUNK(116, 69, 88, 116) +#define png_tIME PNG_CHUNK(116, 73, 77, 69) +#define png_tRNS PNG_CHUNK(116, 82, 78, 83) +#define png_zTXt PNG_CHUNK(122, 84, 88, 116) + +/* The following will work on (signed char*) strings, whereas the get_uint_32 + * macro will fail on top-bit-set values because of the sign extension. + */ +#define PNG_CHUNK_FROM_STRING(s)\ + PNG_CHUNK(0xff&(s)[0], 0xff&(s)[1], 0xff&(s)[2], 0xff&(s)[3]) + +/* This uses (char), not (png_byte) to avoid warnings on systems where (char) is + * signed and the argument is a (char[]) This macro will fail miserably on + * systems where (char) is more than 8 bits. + */ +#define PNG_STRING_FROM_CHUNK(s,c)\ + (void)(((char*)(s))[0]=(char)((c)>>24), ((char*)(s))[1]=(char)((c)>>16),\ + ((char*)(s))[2]=(char)((c)>>8), ((char*)(s))[3]=(char)((c))) + +/* Do the same but terminate with a null character. */ +#define PNG_CSTRING_FROM_CHUNK(s,c)\ + (void)(PNG_STRING_FROM_CHUNK(s,c), ((char*)(s))[4] = 0) + +/* Test on flag values as defined in the spec (section 5.4): */ +#define PNG_CHUNK_ANCILLIARY(c) (1 & ((c) >> 29)) +#define PNG_CHUNK_CRITICAL(c) (!PNG_CHUNK_ANCILLIARY(c)) +#define PNG_CHUNK_PRIVATE(c) (1 & ((c) >> 21)) +#define PNG_CHUNK_RESERVED(c) (1 & ((c) >> 13)) +#define PNG_CHUNK_SAFE_TO_COPY(c) (1 & ((c) >> 5)) + +/* Gamma values (new at libpng-1.5.4): */ +#define PNG_GAMMA_MAC_OLD 151724 /* Assume '1.8' is really 2.2/1.45! */ +#define PNG_GAMMA_MAC_INVERSE 65909 +#define PNG_GAMMA_sRGB_INVERSE 45455 + +/* Almost everything below is C specific; the #defines above can be used in + * non-C code (so long as it is C-preprocessed) the rest of this stuff cannot. + */ +#ifndef PNG_VERSION_INFO_ONLY + +#include "pngstruct.h" +#include "pnginfo.h" + +/* This is used for 16 bit gamma tables -- only the top level pointers are + * const; this could be changed: + */ +typedef const png_uint_16p * png_const_uint_16pp; + +/* Added to libpng-1.5.7: sRGB conversion tables */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_table, [256]); + /* Convert from an sRGB encoded value 0..255 to a 16-bit linear value, + * 0..65535. This table gives the closest 16-bit answers (no errors). + */ +#endif + +PNG_INTERNAL_DATA(const png_uint_16, png_sRGB_base, [512]); +PNG_INTERNAL_DATA(const png_byte, png_sRGB_delta, [512]); + +#define PNG_sRGB_FROM_LINEAR(linear) ((png_byte)((png_sRGB_base[(linear)>>15] +\ + ((((linear)&0x7fff)*png_sRGB_delta[(linear)>>15])>>12)) >> 8)) + /* Given a value 'linear' in the range 0..255*65535 calculate the 8-bit sRGB + * encoded value with maximum error 0.646365. Note that the input is not a + * 16-bit value; it has been multiplied by 255! */ +#endif /* PNG_SIMPLIFIED_READ/WRITE */ + + +/* Internal functions; these are not exported from a DLL however because they + * are used within several of the C source files they have to be C extern. + * + * All of these functions must be declared with PNG_INTERNAL_FUNCTION. + */ + +/* Zlib support */ +#define PNG_UNEXPECTED_ZLIB_RETURN (-7) +PNG_INTERNAL_FUNCTION(void, png_zstream_error,(png_structrp png_ptr, int ret), + PNG_EMPTY); + /* Used by the zlib handling functions to ensure that z_stream::msg is always + * set before they return. + */ + +#ifdef PNG_WRITE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_free_buffer_list,(png_structrp png_ptr, + png_compression_bufferp *list),PNG_EMPTY); + /* Free the buffer list used by the compressed write code. */ +#endif + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && \ + !defined(PNG_FIXED_POINT_MACRO_SUPPORTED) && \ + (defined(PNG_gAMA_SUPPORTED) || defined(PNG_cHRM_SUPPORTED) || \ + defined(PNG_sCAL_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)) || \ + (defined(PNG_sCAL_SUPPORTED) && \ + defined(PNG_FLOATING_ARITHMETIC_SUPPORTED)) +PNG_INTERNAL_FUNCTION(png_fixed_point,png_fixed,(png_const_structrp png_ptr, + double fp, png_const_charp text),PNG_EMPTY); +#endif + +/* Check the user version string for compatibility, returns false if the version + * numbers aren't compatible. + */ +PNG_INTERNAL_FUNCTION(int,png_user_version_check,(png_structrp png_ptr, + png_const_charp user_png_ver),PNG_EMPTY); + +/* Internal base allocator - no messages, NULL on failure to allocate. This + * does, however, call the application provided allocator and that could call + * png_error (although that would be a bug in the application implementation.) + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_base,(png_const_structrp png_ptr, + png_alloc_size_t size),PNG_ALLOCATED); + +#if defined(PNG_TEXT_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) ||\ + defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) +/* Internal array allocator, outputs no error or warning messages on failure, + * just returns NULL. + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_malloc_array,(png_const_structrp png_ptr, + int nelements, size_t element_size),PNG_ALLOCATED); + +/* The same but an existing array is extended by add_elements. This function + * also memsets the new elements to 0 and copies the old elements. The old + * array is not freed or altered. + */ +PNG_INTERNAL_FUNCTION(png_voidp,png_realloc_array,(png_const_structrp png_ptr, + png_const_voidp array, int old_elements, int add_elements, + size_t element_size),PNG_ALLOCATED); +#endif /* text, sPLT or unknown chunks */ + +/* Magic to create a struct when there is no struct to call the user supplied + * memory allocators. Because error handling has not been set up the memory + * handlers can't safely call png_error, but this is an obscure and undocumented + * restriction so libpng has to assume that the 'free' handler, at least, might + * call png_error. + */ +PNG_INTERNAL_FUNCTION(png_structp,png_create_png_struct, + (png_const_charp user_png_ver, png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warn_fn, png_voidp mem_ptr, png_malloc_ptr malloc_fn, + png_free_ptr free_fn),PNG_ALLOCATED); + +/* Free memory from internal libpng struct */ +PNG_INTERNAL_FUNCTION(void,png_destroy_png_struct,(png_structrp png_ptr), + PNG_EMPTY); + +/* Free an allocated jmp_buf (always succeeds) */ +PNG_INTERNAL_FUNCTION(void,png_free_jmpbuf,(png_structrp png_ptr),PNG_EMPTY); + +/* Function to allocate memory for zlib. PNGAPI is disallowed. */ +PNG_INTERNAL_FUNCTION(voidpf,png_zalloc,(voidpf png_ptr, uInt items, uInt size), + PNG_ALLOCATED); + +/* Function to free memory for zlib. PNGAPI is disallowed. */ +PNG_INTERNAL_FUNCTION(void,png_zfree,(voidpf png_ptr, voidpf ptr),PNG_EMPTY); + +/* Next four functions are used internally as callbacks. PNGCBAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3, changed to + * PNGCBAPI at 1.5.0 + */ + +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_read_data,(png_structp png_ptr, + png_bytep data, png_size_t length),PNG_EMPTY); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_push_fill_buffer,(png_structp png_ptr, + png_bytep buffer, png_size_t length),PNG_EMPTY); +#endif + +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_write_data,(png_structp png_ptr, + png_bytep data, png_size_t length),PNG_EMPTY); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_STDIO_SUPPORTED +PNG_INTERNAL_FUNCTION(void PNGCBAPI,png_default_flush,(png_structp png_ptr), + PNG_EMPTY); +# endif +#endif + +/* Reset the CRC variable */ +PNG_INTERNAL_FUNCTION(void,png_reset_crc,(png_structrp png_ptr),PNG_EMPTY); + +/* Write the "data" buffer to whatever output you are using */ +PNG_INTERNAL_FUNCTION(void,png_write_data,(png_structrp png_ptr, + png_const_bytep data, png_size_t length),PNG_EMPTY); + +/* Read and check the PNG file signature */ +PNG_INTERNAL_FUNCTION(void,png_read_sig,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); + +/* Read the chunk header (length + type name) */ +PNG_INTERNAL_FUNCTION(png_uint_32,png_read_chunk_header,(png_structrp png_ptr), + PNG_EMPTY); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_INTERNAL_FUNCTION(void,png_read_data,(png_structrp png_ptr, png_bytep data, + png_size_t length),PNG_EMPTY); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_INTERNAL_FUNCTION(void,png_crc_read,(png_structrp png_ptr, png_bytep buf, + png_uint_32 length),PNG_EMPTY); + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_INTERNAL_FUNCTION(int,png_crc_finish,(png_structrp png_ptr, + png_uint_32 skip),PNG_EMPTY); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_INTERNAL_FUNCTION(int,png_crc_error,(png_structrp png_ptr),PNG_EMPTY); + +/* 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_INTERNAL_FUNCTION(void,png_calculate_crc,(png_structrp png_ptr, + png_const_bytep ptr, png_size_t length),PNG_EMPTY); + +#ifdef PNG_WRITE_FLUSH_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_flush,(png_structrp png_ptr),PNG_EMPTY); +#endif + +/* Write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_INTERNAL_FUNCTION(void,png_write_IHDR,(png_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, int color_type, + int compression_method, int filter_method, int interlace_method),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_write_PLTE,(png_structrp png_ptr, + png_const_colorp palette, png_uint_32 num_pal),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_compress_IDAT,(png_structrp png_ptr, + png_const_bytep row_data, png_alloc_size_t row_data_length, int flush), + PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_write_IEND,(png_structrp png_ptr),PNG_EMPTY); + +#ifdef PNG_WRITE_gAMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_gAMA_fixed,(png_structrp png_ptr, + png_fixed_point file_gamma),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_sBIT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sBIT,(png_structrp png_ptr, + png_const_color_8p sbit, int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_cHRM_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_cHRM_fixed,(png_structrp png_ptr, + const png_xy *xy), PNG_EMPTY); + /* The xy value must have been previously validated */ +#endif + +#ifdef PNG_WRITE_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sRGB,(png_structrp png_ptr, + int intent),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_iCCP,(png_structrp png_ptr, + png_const_charp name, png_const_bytep profile), PNG_EMPTY); + /* The profile must have been previously validated for correctness, the + * length comes from the first four bytes. Only the base, deflate, + * compression is supported. + */ +#endif + +#ifdef PNG_WRITE_sPLT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sPLT,(png_structrp png_ptr, + png_const_sPLT_tp palette),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_tRNS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tRNS,(png_structrp png_ptr, + png_const_bytep trans, png_const_color_16p values, int number, + int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_bKGD_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_bKGD,(png_structrp png_ptr, + png_const_color_16p values, int color_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_hIST_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_hIST,(png_structrp png_ptr, + png_const_uint_16p hist, int num_hist),PNG_EMPTY); +#endif + +/* Chunks that have keywords */ +#ifdef PNG_WRITE_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tEXt,(png_structrp png_ptr, + png_const_charp key, png_const_charp text, png_size_t text_len),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_zTXt,(png_structrp png_ptr, png_const_charp + key, png_const_charp text, png_size_t text_len, int compression),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_iTXt,(png_structrp png_ptr, + int compression, png_const_charp key, png_const_charp lang, + png_const_charp lang_key, png_const_charp text),PNG_EMPTY); +#endif + +#ifdef PNG_TEXT_SUPPORTED /* Added at version 1.0.14 and 1.2.4 */ +PNG_INTERNAL_FUNCTION(int,png_set_text_2,(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_textp text_ptr, int num_text),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_oFFs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_oFFs,(png_structrp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_pCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_pCAL,(png_structrp png_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_const_charp units, png_charpp params),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_pHYs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_pHYs,(png_structrp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_tIME_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_tIME,(png_structrp png_ptr, + png_const_timep mod_time),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_sCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_write_sCAL_s,(png_structrp png_ptr, + int unit, png_const_charp width, png_const_charp height),PNG_EMPTY); +#endif + +/* Called when finished processing a row of data */ +PNG_INTERNAL_FUNCTION(void,png_write_finish_row,(png_structrp png_ptr), + PNG_EMPTY); + +/* Internal use only. Called before first row of data */ +PNG_INTERNAL_FUNCTION(void,png_write_start_row,(png_structrp png_ptr), + PNG_EMPTY); + +/* Combine a row of data, dealing with alpha, etc. if requested. 'row' is an + * array of png_ptr->width pixels. If the image is not interlaced or this + * is the final pass this just does a memcpy, otherwise the "display" flag + * is used to determine whether to copy pixels that are not in the current pass. + * + * Because 'png_do_read_interlace' (below) replicates pixels this allows this + * function to achieve the documented 'blocky' appearance during interlaced read + * if display is 1 and the 'sparkle' appearance, where existing pixels in 'row' + * are not changed if they are not in the current pass, when display is 0. + * + * 'display' must be 0 or 1, otherwise the memcpy will be done regardless. + * + * The API always reads from the png_struct row buffer and always assumes that + * it is full width (png_do_read_interlace has already been called.) + * + * This function is only ever used to write to row buffers provided by the + * caller of the relevant libpng API and the row must have already been + * transformed by the read transformations. + * + * The PNG_USE_COMPILE_TIME_MASKS option causes generation of pre-computed + * bitmasks for use within the code, otherwise runtime generated masks are used. + * The default is compile time masks. + */ +#ifndef PNG_USE_COMPILE_TIME_MASKS +# define PNG_USE_COMPILE_TIME_MASKS 1 +#endif +PNG_INTERNAL_FUNCTION(void,png_combine_row,(png_const_structrp png_ptr, + png_bytep row, int display),PNG_EMPTY); + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* Expand an interlaced row: the 'row_info' describes the pass data that has + * been read in and must correspond to the pixels in 'row', the pixels are + * expanded (moved apart) in 'row' to match the final layout, when doing this + * the pixels are *replicated* to the intervening space. This is essential for + * the correct operation of png_combine_row, above. + */ +PNG_INTERNAL_FUNCTION(void,png_do_read_interlace,(png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations),PNG_EMPTY); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +/* Grab pixels out of a row for an interlaced pass */ +PNG_INTERNAL_FUNCTION(void,png_do_write_interlace,(png_row_infop row_info, + png_bytep row, int pass),PNG_EMPTY); +#endif + +/* Unfilter a row: check the filter value before calling this, there is no point + * calling it for PNG_FILTER_VALUE_NONE. + */ +PNG_INTERNAL_FUNCTION(void,png_read_filter_row,(png_structrp pp, png_row_infop + row_info, png_bytep row, png_const_bytep prev_row, int filter),PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_neon,(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_neon,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); + +/* Choose the best filter to use and filter the row data */ +PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_read_IDAT_data,(png_structrp png_ptr, + png_bytep output, png_alloc_size_t avail_out),PNG_EMPTY); + /* Read 'avail_out' bytes of data from the IDAT stream. If the output buffer + * is NULL the function checks, instead, for the end of the stream. In this + * case a benign error will be issued if the stream end is not found or if + * extra data has to be consumed. + */ +PNG_INTERNAL_FUNCTION(void,png_read_finish_IDAT,(png_structrp png_ptr), + PNG_EMPTY); + /* This cleans up when the IDAT LZ stream does not end when the last image + * byte is read; there is still some pending input. + */ + +PNG_INTERNAL_FUNCTION(void,png_read_finish_row,(png_structrp png_ptr), + PNG_EMPTY); + /* Finish a row while reading, dealing with interlacing passes, etc. */ +#endif + +/* Initialize the row buffers, etc. */ +PNG_INTERNAL_FUNCTION(void,png_read_start_row,(png_structrp png_ptr),PNG_EMPTY); + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Optional call to update the users info structure */ +PNG_INTERNAL_FUNCTION(void,png_read_transform_info,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +#endif + +/* These are the functions that do the transformations */ +#ifdef PNG_READ_FILLER_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_filler,(png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags),PNG_EMPTY); +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_swap_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_write_swap_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_invert_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_write_invert_alpha,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_strip_channel,(png_row_infop row_info, + png_bytep row, int at_start),PNG_EMPTY); +#endif + +#ifdef PNG_16BIT_SUPPORTED +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_swap,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || \ + defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_packswap,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_do_rgb_to_gray,(png_structrp png_ptr, + png_row_infop row_info, png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_gray_to_rgb,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_unpack,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_unshift,(png_row_infop row_info, + png_bytep row, png_const_color_8p sig_bits),PNG_EMPTY); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_invert,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_scale_16_to_8,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_chop,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_quantize,(png_row_infop row_info, + png_bytep row, png_const_bytep palette_lookup, + png_const_bytep quantize_lookup),PNG_EMPTY); + +# ifdef PNG_CORRECT_PALETTE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_correct_palette,(png_structrp png_ptr, + png_colorp palette, int num_palette),PNG_EMPTY); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_bgr,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_PACK_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_pack,(png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth),PNG_EMPTY); +#endif + +#ifdef PNG_WRITE_SHIFT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_shift,(png_row_infop row_info, + png_bytep row, png_const_color_8p bit_depth),PNG_EMPTY); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_compose,(png_row_infop row_info, + png_bytep row, png_structrp png_ptr),PNG_EMPTY); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_gamma,(png_row_infop row_info, + png_bytep row, png_structrp png_ptr),PNG_EMPTY); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_encode_alpha,(png_row_infop row_info, + png_bytep row, png_structrp png_ptr),PNG_EMPTY); +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_expand_palette,(png_row_infop row_info, + png_bytep row, png_const_colorp palette, png_const_bytep trans, + int num_trans),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_do_expand,(png_row_infop row_info, + png_bytep row, png_const_color_16p trans_color),PNG_EMPTY); +#endif + +#ifdef PNG_READ_EXPAND_16_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_expand_16,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#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_INTERNAL_FUNCTION(void,png_handle_IHDR,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_handle_PLTE,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_handle_IEND,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); + +#ifdef PNG_READ_bKGD_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_bKGD,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_cHRM_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_cHRM,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_gAMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_gAMA,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_hIST_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_hIST,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_iCCP,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#ifdef PNG_READ_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_iTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_oFFs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_oFFs,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_pCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_pCAL,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_pHYs_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_pHYs,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sBIT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sBIT,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sCAL_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sCAL,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_sPLT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sPLT,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#ifdef PNG_READ_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_sRGB,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tEXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tIME_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tIME,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_tRNS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_tRNS,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +#ifdef PNG_READ_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_zTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +#endif + +PNG_INTERNAL_FUNCTION(void,png_check_chunk_name,(png_structrp png_ptr, + png_uint_32 chunk_name),PNG_EMPTY); + +#ifdef PNG_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_handle_unknown,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length, int keep),PNG_EMPTY); + /* This is the function that gets called for unknown chunks. The 'keep' + * argument is either non-zero for a known chunk that has been set to be + * handled as unknown or zero for an unknown chunk. By default the function + * just skips the chunk or errors out if it is critical. + */ + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_chunk_unknown_handling, + (png_const_structrp png_ptr, png_uint_32 chunk_name),PNG_EMPTY); + /* Exactly as the API png_handle_as_unknown() except that the argument is a + * 32-bit chunk name, not a string. + */ +#endif +#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ + +/* Handle the transformations for reading and writing */ +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_transformations,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); +#endif +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_write_transformations,(png_structrp png_ptr, + png_row_infop row_info),PNG_EMPTY); +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_init_read_transformations,(png_structrp png_ptr), + PNG_EMPTY); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_read_chunk,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_sig,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_check_crc,(png_structrp png_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_crc_skip,(png_structrp png_ptr, + png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_crc_finish,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_save_buffer,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_restore_buffer,(png_structrp png_ptr, + png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_IDAT,(png_structrp png_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_process_IDAT_data,(png_structrp png_ptr, + png_bytep buffer, png_size_t buffer_length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_process_row,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_handle_unknown,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_info,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_end,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_have_row,(png_structrp png_ptr, + png_bytep row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_end,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_process_some_data,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_push_finish_row,(png_structrp png_ptr), + PNG_EMPTY); +# ifdef PNG_READ_tEXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_tEXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_tEXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif +# ifdef PNG_READ_zTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_zTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_zTXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif +# ifdef PNG_READ_iTXt_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_push_handle_iTXt,(png_structrp png_ptr, + png_inforp info_ptr, png_uint_32 length),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_push_read_iTXt,(png_structrp png_ptr, + png_inforp info_ptr),PNG_EMPTY); +# endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_do_read_intrapixel,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_do_write_intrapixel,(png_row_infop row_info, + png_bytep row),PNG_EMPTY); +#endif + +/* Added at libpng version 1.6.0 */ +#ifdef PNG_GAMMA_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_colorspace_set_gamma,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_fixed_point gAMA), PNG_EMPTY); + /* Set the colorspace gamma with a value provided by the application or by + * the gAMA chunk on read. The value will override anything set by an ICC + * profile. + */ + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync_info,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Synchronize the info 'valid' flags with the colorspace */ + +PNG_INTERNAL_FUNCTION(void,png_colorspace_sync,(png_const_structrp png_ptr, + png_inforp info_ptr), PNG_EMPTY); + /* Copy the png_struct colorspace to the info_struct and call the above to + * synchronize the flags. Checks for NULL info_ptr and does nothing. + */ +#endif + +/* Added at libpng version 1.4.0 */ +#ifdef PNG_COLORSPACE_SUPPORTED +/* These internal functions are for maintaining the colorspace structure within + * a png_info or png_struct (or, indeed, both). + */ +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_chromaticities, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_xy *xy, + int preferred), PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_endpoints, + (png_const_structrp png_ptr, png_colorspacerp colorspace, const png_XYZ *XYZ, + int preferred), PNG_EMPTY); + +#ifdef PNG_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_sRGB,(png_const_structrp png_ptr, + png_colorspacerp colorspace, int intent), PNG_EMPTY); + /* This does set the colorspace gAMA and cHRM values too, but doesn't set the + * flags to write them, if it returns false there was a problem and an error + * message has already been output (but the colorspace may still need to be + * synced to record the invalid flag). + */ +#endif /* sRGB */ + +#ifdef PNG_iCCP_SUPPORTED +PNG_INTERNAL_FUNCTION(int,png_colorspace_set_ICC,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, png_const_bytep profile, int color_type), + PNG_EMPTY); + /* The 'name' is used for information only */ + +/* Routines for checking parts of an ICC profile. */ +PNG_INTERNAL_FUNCTION(int,png_icc_check_length,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length), PNG_EMPTY); +PNG_INTERNAL_FUNCTION(int,png_icc_check_header,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* first 132 bytes only */, int color_type), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(int,png_icc_check_tag_table,(png_const_structrp png_ptr, + png_colorspacerp colorspace, png_const_charp name, + png_uint_32 profile_length, + png_const_bytep profile /* header plus whole tag table */), PNG_EMPTY); +#ifdef PNG_sRGB_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_icc_set_sRGB,( + png_const_structrp png_ptr, png_colorspacerp colorspace, + png_const_bytep profile, uLong adler), PNG_EMPTY); + /* 'adler' is the Adler32 checksum of the uncompressed profile data. It may + * be zero to indicate that it is not available. It is used, if provided, + * as a fast check on the profile when checking to see if it is sRGB. + */ +#endif +#endif /* iCCP */ + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_colorspace_set_rgb_coefficients, + (png_structrp png_ptr), PNG_EMPTY); + /* Set the rgb_to_gray coefficients from the colorspace Y values */ +#endif /* READ_RGB_TO_GRAY */ +#endif /* COLORSPACE */ + +/* Added at libpng version 1.4.0 */ +PNG_INTERNAL_FUNCTION(void,png_check_IHDR,(png_const_structrp png_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type),PNG_EMPTY); + +/* Added at libpng version 1.5.10 */ +#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ + defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_do_check_palette_indexes, + (png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); +#endif + +#if defined(PNG_FLOATING_POINT_SUPPORTED) && defined(PNG_ERROR_TEXT_SUPPORTED) +PNG_INTERNAL_FUNCTION(void,png_fixed_error,(png_const_structrp png_ptr, + png_const_charp name),PNG_NORETURN); +#endif + +/* Puts 'string' into 'buffer' at buffer[pos], taking care never to overwrite + * the end. Always leaves the buffer nul terminated. Never errors out (and + * there is no error code.) + */ +PNG_INTERNAL_FUNCTION(size_t,png_safecat,(png_charp buffer, size_t bufsize, + size_t pos, png_const_charp string),PNG_EMPTY); + +/* Various internal functions to handle formatted warning messages, currently + * only implemented for warnings. + */ +#if defined(PNG_WARNINGS_SUPPORTED) || defined(PNG_TIME_RFC1123_SUPPORTED) +/* Utility to dump an unsigned value into a buffer, given a start pointer and + * and end pointer (which should point just *beyond* the end of the buffer!) + * Returns the pointer to the start of the formatted string. This utility only + * does unsigned values. + */ +PNG_INTERNAL_FUNCTION(png_charp,png_format_number,(png_const_charp start, + png_charp end, int format, png_alloc_size_t number),PNG_EMPTY); + +/* Convenience macro that takes an array: */ +#define PNG_FORMAT_NUMBER(buffer,format,number) \ + png_format_number(buffer, buffer + (sizeof buffer), format, number) + +/* Suggested size for a number buffer (enough for 64 bits and a sign!) */ +#define PNG_NUMBER_BUFFER_SIZE 24 + +/* These are the integer formats currently supported, the name is formed from + * the standard printf(3) format string. + */ +#define PNG_NUMBER_FORMAT_u 1 /* chose unsigned API! */ +#define PNG_NUMBER_FORMAT_02u 2 +#define PNG_NUMBER_FORMAT_d 1 /* chose signed API! */ +#define PNG_NUMBER_FORMAT_02d 2 +#define PNG_NUMBER_FORMAT_x 3 +#define PNG_NUMBER_FORMAT_02x 4 +#define PNG_NUMBER_FORMAT_fixed 5 /* choose the signed API */ +#endif + +#ifdef PNG_WARNINGS_SUPPORTED +/* New defines and members adding in libpng-1.5.4 */ +# define PNG_WARNING_PARAMETER_SIZE 32 +# define PNG_WARNING_PARAMETER_COUNT 8 /* Maximum 9; see pngerror.c */ + +/* An l-value of this type has to be passed to the APIs below to cache the + * values of the parameters to a formatted warning message. + */ +typedef char png_warning_parameters[PNG_WARNING_PARAMETER_COUNT][ + PNG_WARNING_PARAMETER_SIZE]; + +PNG_INTERNAL_FUNCTION(void,png_warning_parameter,(png_warning_parameters p, + int number, png_const_charp string),PNG_EMPTY); + /* Parameters are limited in size to PNG_WARNING_PARAMETER_SIZE characters, + * including the trailing '\0'. + */ +PNG_INTERNAL_FUNCTION(void,png_warning_parameter_unsigned, + (png_warning_parameters p, int number, int format, png_alloc_size_t value), + PNG_EMPTY); + /* Use png_alloc_size_t because it is an unsigned type as big as any we + * need to output. Use the following for a signed value. + */ +PNG_INTERNAL_FUNCTION(void,png_warning_parameter_signed, + (png_warning_parameters p, int number, int format, png_int_32 value), + PNG_EMPTY); + +PNG_INTERNAL_FUNCTION(void,png_formatted_warning,(png_const_structrp png_ptr, + png_warning_parameters p, png_const_charp message),PNG_EMPTY); + /* 'message' follows the X/Open approach of using @1, @2 to insert + * parameters previously supplied using the above functions. Errors in + * specifying the parameters will simply result in garbage substitutions. + */ +#endif + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +/* Application errors (new in 1.6); use these functions (declared below) for + * errors in the parameters or order of API function calls on read. The + * 'warning' should be used for an error that can be handled completely; the + * 'error' for one which can be handled safely but which may lose application + * information or settings. + * + * By default these both result in a png_error call prior to release, while in a + * released version the 'warning' is just a warning. However if the application + * explicitly disables benign errors (explicitly permitting the code to lose + * information) they both turn into warnings. + * + * If benign errors aren't supported they end up as the corresponding base call + * (png_warning or png_error.) + */ +PNG_INTERNAL_FUNCTION(void,png_app_warning,(png_const_structrp png_ptr, + png_const_charp message),PNG_EMPTY); + /* The application provided invalid parameters to an API function or called + * an API function at the wrong time, libpng can completely recover. + */ + +PNG_INTERNAL_FUNCTION(void,png_app_error,(png_const_structrp png_ptr, + png_const_charp message),PNG_EMPTY); + /* As above but libpng will ignore the call, or attempt some other partial + * recovery from the error. + */ +#else +# define png_app_warning(pp,s) png_warning(pp,s) +# define png_app_error(pp,s) png_error(pp,s) +#endif + +PNG_INTERNAL_FUNCTION(void,png_chunk_report,(png_const_structrp png_ptr, + png_const_charp message, int error),PNG_EMPTY); + /* Report a recoverable issue in chunk data. On read this is used to report + * a problem found while reading a particular chunk and the + * png_chunk_benign_error or png_chunk_warning function is used as + * appropriate. On write this is used to report an error that comes from + * data set via an application call to a png_set_ API and png_app_error or + * png_app_warning is used as appropriate. + * + * The 'error' parameter must have one of the following values: + */ +#define PNG_CHUNK_WARNING 0 /* never an error */ +#define PNG_CHUNK_WRITE_ERROR 1 /* an error only on write */ +#define PNG_CHUNK_ERROR 2 /* always an error */ + +/* ASCII to FP interfaces, currently only implemented if sCAL + * support is required. + */ +#if defined(PNG_sCAL_SUPPORTED) +/* MAX_DIGITS is actually the maximum number of characters in an sCAL + * width or height, derived from the precision (number of significant + * digits - a build time settable option) and assumptions about the + * maximum ridiculous exponent. + */ +#define PNG_sCAL_MAX_DIGITS (PNG_sCAL_PRECISION+1/*.*/+1/*E*/+10/*exponent*/) + +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_ascii_from_fp,(png_const_structrp png_ptr, + png_charp ascii, png_size_t size, double fp, unsigned int precision), + PNG_EMPTY); +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_ascii_from_fixed,(png_const_structrp png_ptr, + png_charp ascii, png_size_t size, png_fixed_point fp),PNG_EMPTY); +#endif /* FIXED_POINT */ +#endif /* sCAL */ + +#if defined(PNG_sCAL_SUPPORTED) || defined(PNG_pCAL_SUPPORTED) +/* An internal API to validate the format of a floating point number. + * The result is the index of the next character. If the number is + * not valid it will be the index of a character in the supposed number. + * + * The format of a number is defined in the PNG extensions specification + * and this API is strictly conformant to that spec, not anyone elses! + * + * The format as a regular expression is: + * + * [+-]?[0-9]+.?([Ee][+-]?[0-9]+)? + * + * or: + * + * [+-]?.[0-9]+(.[0-9]+)?([Ee][+-]?[0-9]+)? + * + * The complexity is that either integer or fraction must be present and the + * fraction is permitted to have no digits only if the integer is present. + * + * NOTE: The dangling E problem. + * There is a PNG valid floating point number in the following: + * + * PNG floating point numbers are not greedy. + * + * Working this out requires *TWO* character lookahead (because of the + * sign), the parser does not do this - it will fail at the 'r' - this + * doesn't matter for PNG sCAL chunk values, but it requires more care + * if the value were ever to be embedded in something more complex. Use + * ANSI-C strtod if you need the lookahead. + */ +/* State table for the parser. */ +#define PNG_FP_INTEGER 0 /* before or in integer */ +#define PNG_FP_FRACTION 1 /* before or in fraction */ +#define PNG_FP_EXPONENT 2 /* before or in exponent */ +#define PNG_FP_STATE 3 /* mask for the above */ +#define PNG_FP_SAW_SIGN 4 /* Saw +/- in current state */ +#define PNG_FP_SAW_DIGIT 8 /* Saw a digit in current state */ +#define PNG_FP_SAW_DOT 16 /* Saw a dot in current state */ +#define PNG_FP_SAW_E 32 /* Saw an E (or e) in current state */ +#define PNG_FP_SAW_ANY 60 /* Saw any of the above 4 */ + +/* These three values don't affect the parser. They are set but not used. + */ +#define PNG_FP_WAS_VALID 64 /* Preceding substring is a valid fp number */ +#define PNG_FP_NEGATIVE 128 /* A negative number, including "-0" */ +#define PNG_FP_NONZERO 256 /* A non-zero value */ +#define PNG_FP_STICKY 448 /* The above three flags */ + +/* This is available for the caller to store in 'state' if required. Do not + * call the parser after setting it (the parser sometimes clears it.) + */ +#define PNG_FP_INVALID 512 /* Available for callers as a distinct value */ + +/* Result codes for the parser (boolean - true meants ok, false means + * not ok yet.) + */ +#define PNG_FP_MAYBE 0 /* The number may be valid in the future */ +#define PNG_FP_OK 1 /* The number is valid */ + +/* Tests on the sticky non-zero and negative flags. To pass these checks + * the state must also indicate that the whole number is valid - this is + * achieved by testing PNG_FP_SAW_DIGIT (see the implementation for why this + * is equivalent to PNG_FP_OK above.) + */ +#define PNG_FP_NZ_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NEGATIVE | PNG_FP_NONZERO) + /* NZ_MASK: the string is valid and a non-zero negative value */ +#define PNG_FP_Z_MASK (PNG_FP_SAW_DIGIT | PNG_FP_NONZERO) + /* Z MASK: the string is valid and a non-zero value. */ + /* PNG_FP_SAW_DIGIT: the string is valid. */ +#define PNG_FP_IS_ZERO(state) (((state) & PNG_FP_Z_MASK) == PNG_FP_SAW_DIGIT) +#define PNG_FP_IS_POSITIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_Z_MASK) +#define PNG_FP_IS_NEGATIVE(state) (((state) & PNG_FP_NZ_MASK) == PNG_FP_NZ_MASK) + +/* The actual parser. This can be called repeatedly. It updates + * the index into the string and the state variable (which must + * be initialized to 0). It returns a result code, as above. There + * is no point calling the parser any more if it fails to advance to + * the end of the string - it is stuck on an invalid character (or + * terminated by '\0'). + * + * Note that the pointer will consume an E or even an E+ and then leave + * a 'maybe' state even though a preceding integer.fraction is valid. + * The PNG_FP_WAS_VALID flag indicates that a preceding substring was + * a valid number. It's possible to recover from this by calling + * the parser again (from the start, with state 0) but with a string + * that omits the last character (i.e. set the size to the index of + * the problem character.) This has not been tested within libpng. + */ +PNG_INTERNAL_FUNCTION(int,png_check_fp_number,(png_const_charp string, + png_size_t size, int *statep, png_size_tp whereami),PNG_EMPTY); + +/* This is the same but it checks a complete string and returns true + * only if it just contains a floating point number. As of 1.5.4 this + * function also returns the state at the end of parsing the number if + * it was valid (otherwise it returns 0.) This can be used for testing + * for negative or zero values using the sticky flag. + */ +PNG_INTERNAL_FUNCTION(int,png_check_fp_string,(png_const_charp string, + png_size_t size),PNG_EMPTY); +#endif /* pCAL || sCAL */ + +#if defined(PNG_READ_GAMMA_SUPPORTED) ||\ + defined(PNG_INCH_CONVERSIONS_SUPPORTED) || defined(PNG_READ_pHYs_SUPPORTED) +/* Added at libpng version 1.5.0 */ +/* This is a utility to provide a*times/div (rounded) and indicate + * if there is an overflow. The result is a boolean - false (0) + * for overflow, true (1) if no overflow, in which case *res + * holds the result. + */ +PNG_INTERNAL_FUNCTION(int,png_muldiv,(png_fixed_point_p res, png_fixed_point a, + png_int_32 multiplied_by, png_int_32 divided_by),PNG_EMPTY); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_INCH_CONVERSIONS_SUPPORTED) +/* Same deal, but issue a warning on overflow and return 0. */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_muldiv_warn, + (png_const_structrp png_ptr, png_fixed_point a, png_int_32 multiplied_by, + png_int_32 divided_by),PNG_EMPTY); +#endif + +#ifdef PNG_GAMMA_SUPPORTED +/* Calculate a reciprocal - used for gamma values. This returns + * 0 if the argument is 0 in order to maintain an undefined value; + * there are no warnings. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal,(png_fixed_point a), + PNG_EMPTY); + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* The same but gives a reciprocal of the product of two fixed point + * values. Accuracy is suitable for gamma calculations but this is + * not exact - use png_muldiv for that. Only required at present on read. + */ +PNG_INTERNAL_FUNCTION(png_fixed_point,png_reciprocal2,(png_fixed_point a, + png_fixed_point b),PNG_EMPTY); +#endif + +/* Return true if the gamma value is significantly different from 1.0 */ +PNG_INTERNAL_FUNCTION(int,png_gamma_significant,(png_fixed_point gamma_value), + PNG_EMPTY); +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED +/* Internal fixed point gamma correction. These APIs are called as + * required to convert single values - they don't need to be fast, + * they are not used when processing image pixel values. + * + * While the input is an 'unsigned' value it must actually be the + * correct bit value - 0..255 or 0..65535 as required. + */ +PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_correct,(png_structrp png_ptr, + unsigned int value, png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_uint_16,png_gamma_16bit_correct,(unsigned int value, + png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(png_byte,png_gamma_8bit_correct,(unsigned int value, + png_fixed_point gamma_value),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_destroy_gamma_table,(png_structrp png_ptr), + PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_build_gamma_table,(png_structrp png_ptr, + int bit_depth),PNG_EMPTY); +#endif + +/* SIMPLIFIED READ/WRITE SUPPORT */ +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) ||\ + defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) +/* The internal structure that png_image::opaque points to. */ +typedef struct png_control +{ + png_structp png_ptr; + png_infop info_ptr; + png_voidp error_buf; /* Always a jmp_buf at present. */ + + png_const_bytep memory; /* Memory buffer. */ + png_size_t size; /* Size of the memory buffer. */ + + unsigned int for_write :1; /* Otherwise it is a read structure */ + unsigned int owned_file :1; /* We own the file in io_ptr */ +} png_control; + +/* Return the pointer to the jmp_buf from a png_control: necessary because C + * does not reveal the type of the elements of jmp_buf. + */ +#ifdef __cplusplus +# define png_control_jmp_buf(pc) (((jmp_buf*)((pc)->error_buf))[0]) +#else +# define png_control_jmp_buf(pc) ((pc)->error_buf) +#endif + +/* Utility to safely execute a piece of libpng code catching and logging any + * errors that might occur. Returns true on success, false on failure (either + * of the function or as a result of a png_error.) + */ +PNG_INTERNAL_FUNCTION(void,png_safe_error,(png_structp png_ptr, + png_const_charp error_message),PNG_NORETURN); + +#ifdef PNG_WARNINGS_SUPPORTED +PNG_INTERNAL_FUNCTION(void,png_safe_warning,(png_structp png_ptr, + png_const_charp warning_message),PNG_EMPTY); +#else +# define png_safe_warning 0/*dummy argument*/ +#endif + +PNG_INTERNAL_FUNCTION(int,png_safe_execute,(png_imagep image, + int (*function)(png_voidp), png_voidp arg),PNG_EMPTY); + +/* Utility to log an error; this also cleans up the png_image; the function + * always returns 0 (false). + */ +PNG_INTERNAL_FUNCTION(int,png_image_error,(png_imagep image, + png_const_charp error_message),PNG_EMPTY); + +#ifndef PNG_SIMPLIFIED_READ_SUPPORTED +/* png_image_free is used by the write code but not exported */ +PNG_INTERNAL_FUNCTION(void, png_image_free, (png_imagep image), PNG_EMPTY); +#endif /* !SIMPLIFIED_READ */ + +#endif /* SIMPLIFIED READ/WRITE */ + +#ifdef PNG_FILTER_OPTIMIZATIONS +PNG_INTERNAL_FUNCTION(void, PNG_FILTER_OPTIMIZATIONS, (png_structp png_ptr, + unsigned int bpp), PNG_EMPTY); + /* This is the initialization function for hardware specific optimizations, + * one implementation (for ARM NEON machines) is contained in + * arm/filter_neon.c. It need not be defined - the generic code will be used + * if not. + */ +#endif + +/* Maintainer: Put new private prototypes here ^ */ + +//#include "pngdebug.h" + +#endif /* PNG_VERSION_INFO_ONLY */ +#endif /* PNGPRIV_H */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngread.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngread.c index 707561a..85a7d61 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngread.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngread.c @@ -1,312 +1,85 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * This file contains routines that an application calls directly to * read a PNG file or stream. */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" +#if defined(PNG_SIMPLIFIED_READ_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +# include +#endif -#if defined(PNG_READ_SUPPORTED) +#ifdef 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) +PNG_FUNCTION(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),PNG_ALLOCATED) { - -#ifdef PNG_USER_MEM_SUPPORTED - return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, - warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +#ifndef PNG_USER_MEM_SUPPORTED + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, NULL, NULL, NULL); +#else + return png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL); } -/* Alternate create PNG structure for reading, and allocate any memory needed. */ -png_structp PNGAPI -png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, - png_malloc_ptr malloc_fn, png_free_ptr free_fn) +/* Alternate create PNG structure for reading, and allocate any memory + * needed. + */ +PNG_FUNCTION(png_structp,PNGAPI +png_create_read_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) { + png_structp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); #endif /* PNG_USER_MEM_SUPPORTED */ - png_structp png_ptr; - -#ifdef PNG_SETJMP_SUPPORTED -#ifdef USE_FAR_KEYWORD - jmp_buf jmpbuf; -#endif -#endif - - int i; - - png_debug(1, "in png_create_read_struct\n"); -#ifdef PNG_USER_MEM_SUPPORTED - png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, - (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); -#else - png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); -#endif - if (png_ptr == NULL) - return (NULL); - - /* added at libpng-1.2.6 */ -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - png_ptr->user_width_max=PNG_USER_WIDTH_MAX; - png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; -#endif - -#ifdef PNG_SETJMP_SUPPORTED -#ifdef USE_FAR_KEYWORD - if (setjmp(jmpbuf)) -#else - if (setjmp(png_ptr->jmpbuf)) -#endif + if (png_ptr != NULL) { - png_free(png_ptr, png_ptr->zbuf); - png_ptr->zbuf=NULL; -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)png_ptr, - (png_free_ptr)free_fn, (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)png_ptr); -#endif - return (NULL); - } -#ifdef USE_FAR_KEYWORD - png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); -#endif -#endif + png_ptr->mode = PNG_IS_READ_STRUCT; -#ifdef PNG_USER_MEM_SUPPORTED - png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); -#endif + /* Added in libpng-1.6.0; this can be used to detect a read structure if + * required (it will be zero in a write structure.) + */ +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + png_ptr->IDAT_read_size = PNG_IDAT_READ_SIZE; +# endif - png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); +# ifdef PNG_BENIGN_READ_ERRORS_SUPPORTED + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; - i=0; - do - { - if(user_png_ver[i] != png_libpng_ver[i]) - png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; - } while (png_libpng_ver[i++]); + /* In stable builds only warn if an application error can be completely + * handled. + */ +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; +# endif +# endif - if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) - { - /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so - * we must recompile any applications that use any older library version. - * For versions after libpng 1.0, we will be compatible, so we need - * only check the first digit. - */ - if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || - (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || - (user_png_ver[0] == '0' && user_png_ver[2] < '9')) - { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char msg[80]; - if (user_png_ver) - { - png_snprintf(msg, 80, - "Application was compiled with png.h from libpng-%.20s", - user_png_ver); - png_warning(png_ptr, msg); - } - png_snprintf(msg, 80, - "Application is running with png.c from libpng-%.20s", - png_libpng_ver); - png_warning(png_ptr, msg); -#endif -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags=0; -#endif - png_error(png_ptr, - "Incompatible libpng version in application and library"); - } + /* TODO: delay this, it can be done in png_init_io (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. + */ + png_set_read_fn(png_ptr, NULL, 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); - png_ptr->zstream.zalloc = png_zalloc; - png_ptr->zstream.zfree = png_zfree; - png_ptr->zstream.opaque = (voidpf)png_ptr; - - switch (inflateInit(&png_ptr->zstream)) - { - case Z_OK: /* Do nothing */ break; - case Z_MEM_ERROR: - case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break; - case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break; - default: png_error(png_ptr, "Unknown zlib error"); - } - - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - - png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); - -#ifdef PNG_SETJMP_SUPPORTED -/* Applications that neglect to set up their own setjmp() and then encounter - a png_error() will longjmp here. Since the jmpbuf is then meaningless we - abort instead of returning. */ -#ifdef USE_FAR_KEYWORD - if (setjmp(jmpbuf)) - PNG_ABORT(); - png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); -#else - if (setjmp(png_ptr->jmpbuf)) - PNG_ABORT(); -#endif -#endif - return (png_ptr); + return png_ptr; } -#if defined(PNG_1_0_X) || defined(PNG_1_2_X) -/* Initialize PNG structure for reading, and allocate any memory needed. - This interface is deprecated in favour of the png_create_read_struct(), - and it will disappear as of libpng-1.3.0. */ -#undef png_read_init -void PNGAPI -png_read_init(png_structp png_ptr) -{ - /* We only come here via pre-1.0.7-compiled applications */ - png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0); -} -void PNGAPI -png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver, - png_size_t png_struct_size, png_size_t png_info_size) -{ - /* We only come here via pre-1.0.12-compiled applications */ - if(png_ptr == NULL) return; -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - if(png_sizeof(png_struct) > png_struct_size || - png_sizeof(png_info) > png_info_size) - { - char msg[80]; - png_ptr->warning_fn=NULL; - if (user_png_ver) - { - png_snprintf(msg, 80, - "Application was compiled with png.h from libpng-%.20s", - user_png_ver); - png_warning(png_ptr, msg); - } - png_snprintf(msg, 80, - "Application is running with png.c from libpng-%.20s", - png_libpng_ver); - png_warning(png_ptr, msg); - } -#endif - if(png_sizeof(png_struct) > png_struct_size) - { - png_ptr->error_fn=NULL; -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags=0; -#endif - png_error(png_ptr, - "The png struct allocated by the application for reading is too small."); - } - if(png_sizeof(png_info) > png_info_size) - { - png_ptr->error_fn=NULL; -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags=0; -#endif - png_error(png_ptr, - "The info struct allocated by application for reading is too small."); - } - png_read_init_3(&png_ptr, user_png_ver, png_struct_size); -} -#endif /* PNG_1_0_X || PNG_1_2_X */ - -void PNGAPI -png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, - png_size_t png_struct_size) -{ -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf tmp_jmp; /* to save current jump buffer */ -#endif - - int i=0; - - png_structp png_ptr=*ptr_ptr; - - if(png_ptr == NULL) return; - - do - { - if(user_png_ver[i] != png_libpng_ver[i]) - { -#ifdef PNG_LEGACY_SUPPORTED - png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; -#else - png_ptr->warning_fn=NULL; - png_warning(png_ptr, - "Application uses deprecated png_read_init() and should be recompiled."); - break; -#endif - } - } while (png_libpng_ver[i++]); - - png_debug(1, "in png_read_init_3\n"); - -#ifdef PNG_SETJMP_SUPPORTED - /* save jump buffer and error functions */ - png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); -#endif - - if(png_sizeof(png_struct) > png_struct_size) - { - png_destroy_struct(png_ptr); - *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); - png_ptr = *ptr_ptr; - } - - /* reset all variables to 0 */ - png_memset(png_ptr, 0, png_sizeof (png_struct)); - -#ifdef PNG_SETJMP_SUPPORTED - /* restore jump buffer */ - png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); -#endif - - /* added at libpng-1.2.6 */ -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - png_ptr->user_width_max=PNG_USER_WIDTH_MAX; - png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; -#endif - - /* initialize zbuf - compression buffer */ - png_ptr->zbuf_size = PNG_ZBUF_SIZE; - png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, - (png_uint_32)png_ptr->zbuf_size); - png_ptr->zstream.zalloc = png_zalloc; - png_ptr->zstream.zfree = png_zfree; - png_ptr->zstream.opaque = (voidpf)png_ptr; - - switch (inflateInit(&png_ptr->zstream)) - { - case Z_OK: /* Do nothing */ break; - case Z_MEM_ERROR: - case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break; - case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break; - default: png_error(png_ptr, "Unknown zlib error"); - } - - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - - png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); -} - -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the information before the actual image data. This has been * changed in v0.90 to allow reading a file that already has the magic * bytes read from the stream. You can tell libpng how many bytes have @@ -316,301 +89,294 @@ png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, * read if it is determined that this isn't a valid PNG file. */ void PNGAPI -png_read_info(png_structp png_ptr, png_infop info_ptr) +png_read_info(png_structrp png_ptr, png_inforp 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) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; +#endif + + png_debug(1, "in png_read_info"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Read and check the PNG file signature. */ + png_read_sig(png_ptr, info_ptr); + + for (;;) { - png_size_t num_checked = png_ptr->sig_bytes, - num_to_check = 8 - num_checked; + png_uint_32 length = png_read_chunk_header(png_ptr); + png_uint_32 chunk_name = png_ptr->chunk_name; - png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); - png_ptr->sig_bytes = 8; - - if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + /* IDAT logic needs to happen here to simplify getting the two flags + * right. + */ + if (chunk_name == png_IDAT) { - if (num_checked < 4 && - png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) - png_error(png_ptr, "Not a PNG file"); - else - png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_chunk_error(png_ptr, "Missing IHDR before IDAT"); + + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_chunk_error(png_ptr, "Missing PLTE before IDAT"); + + else if (png_ptr->mode & PNG_AFTER_IDAT) + png_chunk_benign_error(png_ptr, "Too many IDATs found"); + + png_ptr->mode |= PNG_HAVE_IDAT; } - if (num_checked < 3) - png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; - } - for(;;) - { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST PNG_IHDR; - PNG_CONST PNG_IDAT; - PNG_CONST PNG_IEND; - PNG_CONST PNG_PLTE; -#if defined(PNG_READ_bKGD_SUPPORTED) - PNG_CONST PNG_bKGD; -#endif -#if defined(PNG_READ_cHRM_SUPPORTED) - PNG_CONST PNG_cHRM; -#endif -#if defined(PNG_READ_gAMA_SUPPORTED) - PNG_CONST PNG_gAMA; -#endif -#if defined(PNG_READ_hIST_SUPPORTED) - PNG_CONST PNG_hIST; -#endif -#if defined(PNG_READ_iCCP_SUPPORTED) - PNG_CONST PNG_iCCP; -#endif -#if defined(PNG_READ_iTXt_SUPPORTED) - PNG_CONST PNG_iTXt; -#endif -#if defined(PNG_READ_oFFs_SUPPORTED) - PNG_CONST PNG_oFFs; -#endif -#if defined(PNG_READ_pCAL_SUPPORTED) - PNG_CONST PNG_pCAL; -#endif -#if defined(PNG_READ_pHYs_SUPPORTED) - PNG_CONST PNG_pHYs; -#endif -#if defined(PNG_READ_sBIT_SUPPORTED) - PNG_CONST PNG_sBIT; -#endif -#if defined(PNG_READ_sCAL_SUPPORTED) - PNG_CONST PNG_sCAL; -#endif -#if defined(PNG_READ_sPLT_SUPPORTED) - PNG_CONST PNG_sPLT; -#endif -#if defined(PNG_READ_sRGB_SUPPORTED) - PNG_CONST PNG_sRGB; -#endif -#if defined(PNG_READ_tEXt_SUPPORTED) - PNG_CONST PNG_tEXt; -#endif -#if defined(PNG_READ_tIME_SUPPORTED) - PNG_CONST PNG_tIME; -#endif -#if defined(PNG_READ_tRNS_SUPPORTED) - PNG_CONST PNG_tRNS; -#endif -#if defined(PNG_READ_zTXt_SUPPORTED) - PNG_CONST PNG_zTXt; -#endif -#endif /* PNG_USE_LOCAL_ARRAYS */ - png_byte chunk_length[4]; - png_uint_32 length; - - png_read_data(png_ptr, chunk_length, 4); - length = png_get_uint_31(png_ptr,chunk_length); - - png_reset_crc(png_ptr); - png_crc_read(png_ptr, png_ptr->chunk_name, 4); - - png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name, - length); + else if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; /* This should be a binary subdivision search or a hash for * matching the chunk name rather than a linear search. */ - if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - if(png_ptr->mode & PNG_AFTER_IDAT) - png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; - - if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + if (chunk_name == png_IHDR) png_handle_IHDR(png_ptr, info_ptr, length); - else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + + else if (chunk_name == png_IEND) png_handle_IEND(png_ptr, info_ptr, length); + #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) { - if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - png_ptr->mode |= PNG_HAVE_IDAT; - png_handle_unknown(png_ptr, info_ptr, length); - if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_unknown(png_ptr, info_ptr, length, keep); + + if (chunk_name == png_PLTE) png_ptr->mode |= PNG_HAVE_PLTE; - else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + + else if (chunk_name == png_IDAT) { - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before IDAT"); - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - png_error(png_ptr, "Missing PLTE before IDAT"); + png_ptr->idat_size = 0; /* It has been consumed */ break; } } #endif - else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + else if (chunk_name == png_PLTE) png_handle_PLTE(png_ptr, info_ptr, length); - else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - { - if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before IDAT"); - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - png_error(png_ptr, "Missing PLTE before IDAT"); + else if (chunk_name == png_IDAT) + { png_ptr->idat_size = length; - png_ptr->mode |= PNG_HAVE_IDAT; break; } -#if defined(PNG_READ_bKGD_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) png_handle_bKGD(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_cHRM_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + +#ifdef PNG_READ_cHRM_SUPPORTED + else if (chunk_name == png_cHRM) png_handle_cHRM(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_gAMA_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (chunk_name == png_gAMA) png_handle_gAMA(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_hIST_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) png_handle_hIST(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_oFFs_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) png_handle_oFFs(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_pCAL_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) png_handle_pCAL(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sCAL_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) png_handle_sCAL(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_pHYs_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) png_handle_pHYs(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sBIT_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + +#ifdef PNG_READ_sBIT_SUPPORTED + else if (chunk_name == png_sBIT) png_handle_sBIT(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sRGB_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) png_handle_sRGB(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_iCCP_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + +#ifdef PNG_READ_iCCP_SUPPORTED + else if (chunk_name == png_iCCP) png_handle_iCCP(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sPLT_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) png_handle_sPLT(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_tEXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) png_handle_tEXt(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_tIME_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) png_handle_tIME(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_tRNS_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) png_handle_tRNS(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_zTXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) png_handle_zTXt(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_iTXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) png_handle_iTXt(png_ptr, info_ptr, length); #endif + else - png_handle_unknown(png_ptr, info_ptr, length); + png_handle_unknown(png_ptr, info_ptr, length, + PNG_HANDLE_CHUNK_AS_DEFAULT); } } -#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -/* optional call to update the users info_ptr structure */ +/* Optional call to update the users info_ptr structure */ void PNGAPI -png_read_update_info(png_structp png_ptr, png_infop info_ptr) +png_read_update_info(png_structrp png_ptr, png_inforp info_ptr) { - png_debug(1, "in png_read_update_info\n"); - if(png_ptr == NULL) return; - if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) - png_read_start_row(png_ptr); - else - png_warning(png_ptr, - "Ignoring extra png_read_update_info() call; row buffer not reallocated"); - png_read_transform_info(png_ptr, info_ptr); + png_debug(1, "in png_read_update_info"); + + if (png_ptr != NULL) + { + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + { + png_read_start_row(png_ptr); + +# ifdef PNG_READ_TRANSFORMS_SUPPORTED + png_read_transform_info(png_ptr, info_ptr); +# else + PNG_UNUSED(info_ptr) +# endif + } + + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + else + png_app_error(png_ptr, + "png_read_update_info/png_start_read_image: duplicate call"); + } } -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Initialize palette, background, etc, after transformations * are set, but before any reading takes place. This allows * the user to obtain a gamma-corrected palette, for example. * If the user doesn't call this, we will do it ourselves. */ void PNGAPI -png_start_read_image(png_structp png_ptr) +png_start_read_image(png_structrp png_ptr) { - png_debug(1, "in png_start_read_image\n"); - if(png_ptr == NULL) return; - if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) - png_read_start_row(png_ptr); -} -#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + png_debug(1, "in png_start_read_image"); -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED + if (png_ptr != NULL) + { + if ((png_ptr->flags & PNG_FLAG_ROW_INIT) == 0) + png_read_start_row(png_ptr); + + /* New in 1.6.0 this avoids the bug of doing the initializations twice */ + else + png_app_error(png_ptr, + "png_start_read_image/png_read_update_info: duplicate call"); + } +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED void PNGAPI -png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +png_read_row(png_structrp png_ptr, png_bytep row, png_bytep dsp_row) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST PNG_IDAT; - PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, - 0xff}; - PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; -#endif - int ret; - if(png_ptr == NULL) return; - png_debug2(1, "in png_read_row (row %lu, pass %d)\n", - png_ptr->row_number, png_ptr->pass); + png_row_info row_info; + + if (png_ptr == NULL) + return; + + png_debug2(1, "in png_read_row (row %lu, pass %d)", + (unsigned long)png_ptr->row_number, png_ptr->pass); + + /* png_read_start_row sets the information (in particular iwidth) for this + * interlace pass. + */ if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) png_read_start_row(png_ptr); + + /* 1.5.6: row_info moved out of png_struct to a local here. */ + row_info.width = png_ptr->iwidth; /* NOTE: width of current interlaced row */ + row_info.color_type = png_ptr->color_type; + row_info.bit_depth = png_ptr->bit_depth; + row_info.channels = png_ptr->channels; + row_info.pixel_depth = png_ptr->pixel_depth; + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) { - /* check for transforms that have been set but were defined out */ + /* 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."); + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined"); #endif + #if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) if (png_ptr->transformations & PNG_FILLER) - png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined."); + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined"); #endif -#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED) + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + !defined(PNG_READ_PACKSWAP_SUPPORTED) if (png_ptr->transformations & PNG_PACKSWAP) - png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined."); + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined"); #endif + #if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) if (png_ptr->transformations & PNG_PACK) - png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined."); + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined"); #endif + #if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) if (png_ptr->transformations & PNG_SHIFT) - png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined."); + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined"); #endif + #if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) if (png_ptr->transformations & PNG_BGR) - png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined."); + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined"); #endif + #if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) if (png_ptr->transformations & PNG_SWAP_BYTES) - png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined."); + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined"); #endif } -#if defined(PNG_READ_INTERLACING_SUPPORTED) - /* if interlaced and we do not need a new row, combine row and return */ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* If interlaced and we do not need a new row, combine row and return. + * Notice that the pixels we have from previous rows have been transformed + * already; we can only combine like with like (transformed or + * untransformed) and, because of the libpng API for interlaced images, this + * means we must transform before de-interlacing. + */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { switch (png_ptr->pass) @@ -619,62 +385,68 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) if (png_ptr->row_number & 0x07) { if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, - png_pass_dsp_mask[png_ptr->pass]); + png_combine_row(png_ptr, dsp_row, 1/*display*/); png_read_finish_row(png_ptr); return; } break; + case 1: if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) { if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, - png_pass_dsp_mask[png_ptr->pass]); + png_combine_row(png_ptr, dsp_row, 1/*display*/); + png_read_finish_row(png_ptr); return; } break; + case 2: if ((png_ptr->row_number & 0x07) != 4) { if (dsp_row != NULL && (png_ptr->row_number & 4)) - png_combine_row(png_ptr, dsp_row, - png_pass_dsp_mask[png_ptr->pass]); + png_combine_row(png_ptr, dsp_row, 1/*display*/); + png_read_finish_row(png_ptr); return; } break; + case 3: if ((png_ptr->row_number & 3) || png_ptr->width < 3) { if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, - png_pass_dsp_mask[png_ptr->pass]); + png_combine_row(png_ptr, dsp_row, 1/*display*/); + png_read_finish_row(png_ptr); return; } break; + case 4: if ((png_ptr->row_number & 3) != 2) { if (dsp_row != NULL && (png_ptr->row_number & 2)) - png_combine_row(png_ptr, dsp_row, - png_pass_dsp_mask[png_ptr->pass]); + png_combine_row(png_ptr, dsp_row, 1/*display*/); + png_read_finish_row(png_ptr); return; } break; + case 5: if ((png_ptr->row_number & 1) || png_ptr->width < 2) { if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, - png_pass_dsp_mask[png_ptr->pass]); + png_combine_row(png_ptr, dsp_row, 1/*display*/); + png_read_finish_row(png_ptr); return; } break; + + default: case 6: if (!(png_ptr->row_number & 1)) { @@ -689,114 +461,85 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) if (!(png_ptr->mode & PNG_HAVE_IDAT)) png_error(png_ptr, "Invalid attempt to read row data"); - png_ptr->zstream.next_out = png_ptr->row_buf; - png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; - do + /* Fill the row with IDAT data: */ + png_read_IDAT_data(png_ptr, png_ptr->row_buf, row_info.rowbytes + 1); + + if (png_ptr->row_buf[0] > PNG_FILTER_VALUE_NONE) { - if (!(png_ptr->zstream.avail_in)) - { - while (!png_ptr->idat_size) - { - png_byte chunk_length[4]; + if (png_ptr->row_buf[0] < PNG_FILTER_VALUE_LAST) + png_read_filter_row(png_ptr, &row_info, png_ptr->row_buf + 1, + png_ptr->prev_row + 1, png_ptr->row_buf[0]); + else + png_error(png_ptr, "bad adaptive filter value"); + } - png_crc_finish(png_ptr, 0); + /* libpng 1.5.6: the following line was copying png_ptr->rowbytes before + * 1.5.6, while the buffer really is this big in current versions of libpng + * it may not be in the future, so this was changed just to copy the + * interlaced count: + */ + memcpy(png_ptr->prev_row, png_ptr->row_buf, row_info.rowbytes + 1); - png_read_data(png_ptr, chunk_length, 4); - png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length); - - png_reset_crc(png_ptr); - png_crc_read(png_ptr, png_ptr->chunk_name, 4); - if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - png_error(png_ptr, "Not enough image data"); - } - png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; - png_ptr->zstream.next_in = png_ptr->zbuf; - if (png_ptr->zbuf_size > png_ptr->idat_size) - png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; - png_crc_read(png_ptr, png_ptr->zbuf, - (png_size_t)png_ptr->zstream.avail_in); - png_ptr->idat_size -= png_ptr->zstream.avail_in; - } - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); - if (ret == Z_STREAM_END) - { - if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || - png_ptr->idat_size) - png_error(png_ptr, "Extra compressed data"); - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; - break; - } - if (ret != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : - "Decompression error"); - - } while (png_ptr->zstream.avail_out); - - png_ptr->row_info.color_type = png_ptr->color_type; - png_ptr->row_info.width = png_ptr->iwidth; - png_ptr->row_info.channels = png_ptr->channels; - png_ptr->row_info.bit_depth = png_ptr->bit_depth; - png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; - png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, - png_ptr->row_info.width); - - if(png_ptr->row_buf[0]) - png_read_filter_row(png_ptr, &(png_ptr->row_info), - png_ptr->row_buf + 1, png_ptr->prev_row + 1, - (int)(png_ptr->row_buf[0])); - - png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, - png_ptr->rowbytes + 1); - -#if defined(PNG_MNG_FEATURES_SUPPORTED) - if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) { /* Intrapixel differencing */ - png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_read_intrapixel(&row_info, png_ptr->row_buf + 1); } #endif - if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) - png_do_read_transformations(png_ptr); +#ifdef PNG_READ_TRANSFORMS_SUPPORTED + if (png_ptr->transformations) + png_do_read_transformations(png_ptr, &row_info); +#endif -#if defined(PNG_READ_INTERLACING_SUPPORTED) - /* blow up interlaced rows to full size */ + /* The transformed pixel depth should match the depth now in row_info. */ + if (png_ptr->transformed_pixel_depth == 0) + { + png_ptr->transformed_pixel_depth = row_info.pixel_depth; + if (row_info.pixel_depth > png_ptr->maximum_pixel_depth) + png_error(png_ptr, "sequential row overflow"); + } + + else if (png_ptr->transformed_pixel_depth != row_info.pixel_depth) + png_error(png_ptr, "internal sequential row size calculation error"); + +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Blow up interlaced rows to full size */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { if (png_ptr->pass < 6) -/* old interface (pre-1.0.9): - png_do_read_interlace(&(png_ptr->row_info), - png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); - */ - png_do_read_interlace(png_ptr); + png_do_read_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass, + png_ptr->transformations); if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, - png_pass_dsp_mask[png_ptr->pass]); + png_combine_row(png_ptr, dsp_row, 1/*display*/); + if (row != NULL) - png_combine_row(png_ptr, row, - png_pass_mask[png_ptr->pass]); + png_combine_row(png_ptr, row, 0/*row*/); } + else #endif { if (row != NULL) - png_combine_row(png_ptr, row, 0xff); + png_combine_row(png_ptr, row, -1/*ignored*/); + if (dsp_row != NULL) - png_combine_row(png_ptr, dsp_row, 0xff); + png_combine_row(png_ptr, dsp_row, -1/*ignored*/); } png_read_finish_row(png_ptr); if (png_ptr->read_row_fn != NULL) (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); -} -#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +} +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read one or more rows of image data. If the image is interlaced, * and png_set_interlace_handling() has been called, the rows need to * contain the contents of the rows from the previous pass. If the @@ -822,15 +565,18 @@ png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) */ void PNGAPI -png_read_rows(png_structp png_ptr, png_bytepp row, - png_bytepp display_row, png_uint_32 num_rows) +png_read_rows(png_structrp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) { png_uint_32 i; png_bytepp rp; png_bytepp dp; - png_debug(1, "in png_read_rows\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_read_rows"); + + if (png_ptr == NULL) + return; + rp = row; dp = display_row; if (rp != NULL && dp != NULL) @@ -841,24 +587,26 @@ png_read_rows(png_structp png_ptr, png_bytepp row, png_read_row(png_ptr, rptr, dptr); } - else if(rp != NULL) + + else if (rp != NULL) for (i = 0; i < num_rows; i++) { png_bytep rptr = *rp; - png_read_row(png_ptr, rptr, png_bytep_NULL); + png_read_row(png_ptr, rptr, NULL); rp++; } - else if(dp != NULL) + + else if (dp != NULL) for (i = 0; i < num_rows; i++) { png_bytep dptr = *dp; - png_read_row(png_ptr, png_bytep_NULL, dptr); + png_read_row(png_ptr, NULL, dptr); dp++; } } -#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the entire image. If the image has an alpha channel or a tRNS * chunk, and you have called png_handle_alpha()[*], you will need to * initialize the image to the current image that PNG will be overlaying. @@ -872,487 +620,363 @@ png_read_rows(png_structp png_ptr, png_bytepp row, * [*] png_handle_alpha() does not exist yet, as of this version of libpng */ void PNGAPI -png_read_image(png_structp png_ptr, png_bytepp image) +png_read_image(png_structrp png_ptr, png_bytepp image) { - png_uint_32 i,image_height; + png_uint_32 i, image_height; int pass, j; png_bytepp rp; - png_debug(1, "in png_read_image\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_read_image"); + + if (png_ptr == NULL) + return; #ifdef PNG_READ_INTERLACING_SUPPORTED - pass = png_set_interlace_handling(png_ptr); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + { + pass = png_set_interlace_handling(png_ptr); + /* And make sure transforms are initialized. */ + png_start_read_image(png_ptr); + } + else + { + if (png_ptr->interlaced && !(png_ptr->transformations & PNG_INTERLACE)) + { + /* Caller called png_start_read_image or png_read_update_info without + * first turning on the PNG_INTERLACE transform. We can fix this here, + * but the caller should do it! + */ + png_warning(png_ptr, "Interlace handling should be turned on when " + "using png_read_image"); + /* Make sure this is set correctly */ + png_ptr->num_rows = png_ptr->height; + } + + /* Obtain the pass number, which also turns on the PNG_INTERLACE flag in + * the above error case. + */ + pass = png_set_interlace_handling(png_ptr); + } #else if (png_ptr->interlaced) png_error(png_ptr, - "Cannot read interlaced image -- interlace handler disabled."); + "Cannot read interlaced image -- interlace handler disabled"); + pass = 1; #endif - image_height=png_ptr->height; - png_ptr->num_rows = image_height; /* Make sure this is set correctly */ for (j = 0; j < pass; j++) { rp = image; for (i = 0; i < image_height; i++) { - png_read_row(png_ptr, *rp, png_bytep_NULL); + png_read_row(png_ptr, *rp, NULL); rp++; } } } -#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED /* Read the end of the PNG file. Will not read past the end of the * file, will verify the end is accurate, and will read any comments * or time information at the end of the file, if info is not NULL. */ void PNGAPI -png_read_end(png_structp png_ptr, png_infop info_ptr) +png_read_end(png_structrp png_ptr, png_inforp info_ptr) { - png_byte chunk_length[4]; - png_uint_32 length; +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + int keep; +#endif - png_debug(1, "in png_read_end\n"); - if(png_ptr == NULL) return; - png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + png_debug(1, "in png_read_end"); + + if (png_ptr == NULL) + return; + + /* If png_read_end is called in the middle of reading the rows there may + * still be pending IDAT data and an owned zstream. Deal with this here. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + if (!png_chunk_unknown_handling(png_ptr, png_IDAT)) +#endif + png_read_finish_IDAT(png_ptr); + +#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Report invalid palette index; added at libng-1.5.10 */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max > png_ptr->num_palette) + png_benign_error(png_ptr, "Read palette index exceeding num_palette"); +#endif do { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST PNG_IHDR; - PNG_CONST PNG_IDAT; - PNG_CONST PNG_IEND; - PNG_CONST PNG_PLTE; -#if defined(PNG_READ_bKGD_SUPPORTED) - PNG_CONST PNG_bKGD; -#endif -#if defined(PNG_READ_cHRM_SUPPORTED) - PNG_CONST PNG_cHRM; -#endif -#if defined(PNG_READ_gAMA_SUPPORTED) - PNG_CONST PNG_gAMA; -#endif -#if defined(PNG_READ_hIST_SUPPORTED) - PNG_CONST PNG_hIST; -#endif -#if defined(PNG_READ_iCCP_SUPPORTED) - PNG_CONST PNG_iCCP; -#endif -#if defined(PNG_READ_iTXt_SUPPORTED) - PNG_CONST PNG_iTXt; -#endif -#if defined(PNG_READ_oFFs_SUPPORTED) - PNG_CONST PNG_oFFs; -#endif -#if defined(PNG_READ_pCAL_SUPPORTED) - PNG_CONST PNG_pCAL; -#endif -#if defined(PNG_READ_pHYs_SUPPORTED) - PNG_CONST PNG_pHYs; -#endif -#if defined(PNG_READ_sBIT_SUPPORTED) - PNG_CONST PNG_sBIT; -#endif -#if defined(PNG_READ_sCAL_SUPPORTED) - PNG_CONST PNG_sCAL; -#endif -#if defined(PNG_READ_sPLT_SUPPORTED) - PNG_CONST PNG_sPLT; -#endif -#if defined(PNG_READ_sRGB_SUPPORTED) - PNG_CONST PNG_sRGB; -#endif -#if defined(PNG_READ_tEXt_SUPPORTED) - PNG_CONST PNG_tEXt; -#endif -#if defined(PNG_READ_tIME_SUPPORTED) - PNG_CONST PNG_tIME; -#endif -#if defined(PNG_READ_tRNS_SUPPORTED) - PNG_CONST PNG_tRNS; -#endif -#if defined(PNG_READ_zTXt_SUPPORTED) - PNG_CONST PNG_zTXt; -#endif -#endif /* PNG_USE_LOCAL_ARRAYS */ + png_uint_32 length = png_read_chunk_header(png_ptr); + png_uint_32 chunk_name = png_ptr->chunk_name; - png_read_data(png_ptr, chunk_length, 4); - length = png_get_uint_31(png_ptr,chunk_length); - - png_reset_crc(png_ptr); - png_crc_read(png_ptr, png_ptr->chunk_name, 4); - - png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name); - - if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + if (chunk_name == png_IHDR) png_handle_IHDR(png_ptr, info_ptr, length); - else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + + else if (chunk_name == png_IEND) png_handle_IEND(png_ptr, info_ptr, length); + #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED - else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + else if ((keep = png_chunk_unknown_handling(png_ptr, chunk_name)) != 0) { - if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if (chunk_name == png_IDAT) { if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) - png_error(png_ptr, "Too many IDAT's found"); + png_benign_error(png_ptr, "Too many IDATs found"); } - png_handle_unknown(png_ptr, info_ptr, length); - if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_unknown(png_ptr, info_ptr, length, keep); + if (chunk_name == png_PLTE) png_ptr->mode |= PNG_HAVE_PLTE; } #endif - else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + + else if (chunk_name == png_IDAT) { /* 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_benign_error(png_ptr, "Too many IDATs found"); + png_crc_finish(png_ptr, length); } - else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + else if (chunk_name == png_PLTE) png_handle_PLTE(png_ptr, info_ptr, length); -#if defined(PNG_READ_bKGD_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + +#ifdef PNG_READ_bKGD_SUPPORTED + else if (chunk_name == png_bKGD) png_handle_bKGD(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_cHRM_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + +#ifdef PNG_READ_cHRM_SUPPORTED + else if (chunk_name == png_cHRM) png_handle_cHRM(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_gAMA_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + +#ifdef PNG_READ_gAMA_SUPPORTED + else if (chunk_name == png_gAMA) png_handle_gAMA(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_hIST_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + +#ifdef PNG_READ_hIST_SUPPORTED + else if (chunk_name == png_hIST) png_handle_hIST(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_oFFs_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + +#ifdef PNG_READ_oFFs_SUPPORTED + else if (chunk_name == png_oFFs) png_handle_oFFs(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_pCAL_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + +#ifdef PNG_READ_pCAL_SUPPORTED + else if (chunk_name == png_pCAL) png_handle_pCAL(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sCAL_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + +#ifdef PNG_READ_sCAL_SUPPORTED + else if (chunk_name == png_sCAL) png_handle_sCAL(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_pHYs_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + +#ifdef PNG_READ_pHYs_SUPPORTED + else if (chunk_name == png_pHYs) png_handle_pHYs(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sBIT_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + +#ifdef PNG_READ_sBIT_SUPPORTED + else if (chunk_name == png_sBIT) png_handle_sBIT(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sRGB_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + +#ifdef PNG_READ_sRGB_SUPPORTED + else if (chunk_name == png_sRGB) png_handle_sRGB(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_iCCP_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + +#ifdef PNG_READ_iCCP_SUPPORTED + else if (chunk_name == png_iCCP) png_handle_iCCP(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_sPLT_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + +#ifdef PNG_READ_sPLT_SUPPORTED + else if (chunk_name == png_sPLT) png_handle_sPLT(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_tEXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + +#ifdef PNG_READ_tEXt_SUPPORTED + else if (chunk_name == png_tEXt) png_handle_tEXt(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_tIME_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + +#ifdef PNG_READ_tIME_SUPPORTED + else if (chunk_name == png_tIME) png_handle_tIME(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_tRNS_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + +#ifdef PNG_READ_tRNS_SUPPORTED + else if (chunk_name == png_tRNS) png_handle_tRNS(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_zTXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + +#ifdef PNG_READ_zTXt_SUPPORTED + else if (chunk_name == png_zTXt) png_handle_zTXt(png_ptr, info_ptr, length); #endif -#if defined(PNG_READ_iTXt_SUPPORTED) - else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + +#ifdef PNG_READ_iTXt_SUPPORTED + else if (chunk_name == png_iTXt) png_handle_iTXt(png_ptr, info_ptr, length); #endif + else - png_handle_unknown(png_ptr, info_ptr, length); + png_handle_unknown(png_ptr, info_ptr, length, + PNG_HANDLE_CHUNK_AS_DEFAULT); } while (!(png_ptr->mode & PNG_HAVE_IEND)); } -#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ -/* free all memory used by the read */ -void PNGAPI -png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, - png_infopp end_info_ptr_ptr) +/* Free all memory used in the read struct */ +static void +png_read_destroy(png_structrp png_ptr) { - png_structp png_ptr = NULL; - png_infop info_ptr = NULL, end_info_ptr = NULL; -#ifdef PNG_USER_MEM_SUPPORTED - png_free_ptr free_fn; - png_voidp mem_ptr; + png_debug(1, "in png_read_destroy"); + +#ifdef PNG_READ_GAMMA_SUPPORTED + png_destroy_gamma_table(png_ptr); #endif - png_debug(1, "in png_destroy_read_struct\n"); - if (png_ptr_ptr != NULL) - png_ptr = *png_ptr_ptr; - - if (info_ptr_ptr != NULL) - info_ptr = *info_ptr_ptr; - - if (end_info_ptr_ptr != NULL) - end_info_ptr = *end_info_ptr_ptr; - -#ifdef PNG_USER_MEM_SUPPORTED - free_fn = png_ptr->free_fn; - mem_ptr = png_ptr->mem_ptr; -#endif - - png_read_destroy(png_ptr, info_ptr, end_info_ptr); - - if (info_ptr != NULL) - { -#if defined(PNG_TEXT_SUPPORTED) - png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); -#endif - -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, - (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)info_ptr); -#endif - *info_ptr_ptr = NULL; - } - - if (end_info_ptr != NULL) - { -#if defined(PNG_READ_TEXT_SUPPORTED) - png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); -#endif -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, - (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)end_info_ptr); -#endif - *end_info_ptr_ptr = NULL; - } - - if (png_ptr != NULL) - { -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, - (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)png_ptr); -#endif - *png_ptr_ptr = NULL; - } -} - -/* free all memory used by the read (old method) */ -void /* PRIVATE */ -png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr) -{ -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf tmp_jmp; -#endif - png_error_ptr error_fn; - png_error_ptr warning_fn; - png_voidp error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - png_free_ptr free_fn; -#endif - - png_debug(1, "in png_read_destroy\n"); - if (info_ptr != NULL) - png_info_destroy(png_ptr, info_ptr); - - if (end_info_ptr != NULL) - png_info_destroy(png_ptr, end_info_ptr); - - png_free(png_ptr, png_ptr->zbuf); png_free(png_ptr, png_ptr->big_row_buf); - png_free(png_ptr, png_ptr->prev_row); -#if defined(PNG_READ_DITHER_SUPPORTED) + png_free(png_ptr, png_ptr->big_prev_row); + png_free(png_ptr, png_ptr->read_buffer); + +#ifdef PNG_READ_QUANTIZE_SUPPORTED png_free(png_ptr, png_ptr->palette_lookup); - png_free(png_ptr, png_ptr->dither_index); + png_free(png_ptr, png_ptr->quantize_index); #endif -#if defined(PNG_READ_GAMMA_SUPPORTED) - png_free(png_ptr, png_ptr->gamma_table); -#endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - png_free(png_ptr, png_ptr->gamma_from_1); - png_free(png_ptr, png_ptr->gamma_to_1); -#endif -#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_PLTE) png_zfree(png_ptr, png_ptr->palette); png_ptr->free_me &= ~PNG_FREE_PLTE; -#else - if (png_ptr->flags & PNG_FLAG_FREE_PLTE) - png_zfree(png_ptr, png_ptr->palette); - png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; -#endif + #if defined(PNG_tRNS_SUPPORTED) || \ defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) -#ifdef PNG_FREE_ME_SUPPORTED if (png_ptr->free_me & PNG_FREE_TRNS) - png_free(png_ptr, png_ptr->trans); + png_free(png_ptr, png_ptr->trans_alpha); png_ptr->free_me &= ~PNG_FREE_TRNS; -#else - if (png_ptr->flags & PNG_FLAG_FREE_TRNS) - png_free(png_ptr, png_ptr->trans); - png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; -#endif -#endif -#if defined(PNG_READ_hIST_SUPPORTED) -#ifdef PNG_FREE_ME_SUPPORTED - if (png_ptr->free_me & PNG_FREE_HIST) - png_free(png_ptr, png_ptr->hist); - png_ptr->free_me &= ~PNG_FREE_HIST; -#else - if (png_ptr->flags & PNG_FLAG_FREE_HIST) - png_free(png_ptr, png_ptr->hist); - png_ptr->flags &= ~PNG_FLAG_FREE_HIST; -#endif -#endif -#if defined(PNG_READ_GAMMA_SUPPORTED) - if (png_ptr->gamma_16_table != NULL) - { - int i; - int istop = (1 << (8 - png_ptr->gamma_shift)); - for (i = 0; i < istop; i++) - { - png_free(png_ptr, png_ptr->gamma_16_table[i]); - } - png_free(png_ptr, png_ptr->gamma_16_table); - } -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_16_from_1 != NULL) - { - int i; - int istop = (1 << (8 - png_ptr->gamma_shift)); - for (i = 0; i < istop; i++) - { - png_free(png_ptr, png_ptr->gamma_16_from_1[i]); - } - png_free(png_ptr, png_ptr->gamma_16_from_1); - } - if (png_ptr->gamma_16_to_1 != NULL) - { - int i; - int istop = (1 << (8 - png_ptr->gamma_shift)); - for (i = 0; i < istop; i++) - { - png_free(png_ptr, png_ptr->gamma_16_to_1[i]); - } - png_free(png_ptr, png_ptr->gamma_16_to_1); - } -#endif -#endif -#if defined(PNG_TIME_RFC1123_SUPPORTED) - png_free(png_ptr, png_ptr->time_buffer); #endif inflateEnd(&png_ptr->zstream); + #ifdef PNG_PROGRESSIVE_READ_SUPPORTED png_free(png_ptr, png_ptr->save_buffer); #endif -#ifdef PNG_PROGRESSIVE_READ_SUPPORTED -#ifdef PNG_TEXT_SUPPORTED - png_free(png_ptr, png_ptr->current_text); -#endif /* PNG_TEXT_SUPPORTED */ -#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ +#if defined(PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED) &&\ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + png_free(png_ptr, png_ptr->unknown_chunk.data); +#endif - /* Save the important info out of the png_struct, in case it is - * being used again. +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + png_free(png_ptr, png_ptr->chunk_list); +#endif + + /* NOTE: the 'setjmp' buffer may still be allocated and the memory and error + * callbacks are still set at this point. They are required to complete the + * destruction of the png_struct itself. */ -#ifdef PNG_SETJMP_SUPPORTED - png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); -#endif +} - error_fn = png_ptr->error_fn; - warning_fn = png_ptr->warning_fn; - error_ptr = png_ptr->error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - free_fn = png_ptr->free_fn; -#endif +/* Free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structrp png_ptr = NULL; - png_memset(png_ptr, 0, png_sizeof (png_struct)); + png_debug(1, "in png_destroy_read_struct"); - png_ptr->error_fn = error_fn; - png_ptr->warning_fn = warning_fn; - png_ptr->error_ptr = error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - png_ptr->free_fn = free_fn; -#endif + if (png_ptr_ptr != NULL) + png_ptr = *png_ptr_ptr; -#ifdef PNG_SETJMP_SUPPORTED - png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); -#endif + if (png_ptr == NULL) + return; + /* libpng 1.6.0: use the API to destroy info structs to ensure consistent + * behavior. Prior to 1.6.0 libpng did extra 'info' destruction in this API. + * The extra was, apparently, unnecessary yet this hides memory leak bugs. + */ + png_destroy_info_struct(png_ptr, end_info_ptr_ptr); + png_destroy_info_struct(png_ptr, info_ptr_ptr); + + *png_ptr_ptr = NULL; + png_read_destroy(png_ptr); + png_destroy_png_struct(png_ptr); } void PNGAPI -png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +png_set_read_status_fn(png_structrp png_ptr, png_read_status_ptr read_row_fn) { - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + png_ptr->read_row_fn = read_row_fn; } -#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED -#if defined(PNG_INFO_IMAGE_SUPPORTED) +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +#ifdef PNG_INFO_IMAGE_SUPPORTED void PNGAPI -png_read_png(png_structp png_ptr, png_infop info_ptr, +png_read_png(png_structrp png_ptr, png_inforp info_ptr, int transforms, voidp params) { int row; - if(png_ptr == NULL) return; -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) - /* invert the alpha channel from opacity to transparency - */ - if (transforms & PNG_TRANSFORM_INVERT_ALPHA) - png_set_invert_alpha(png_ptr); -#endif + if (png_ptr == NULL || info_ptr == NULL) + return; /* png_read_info() gives us all of the information from the * PNG file before the first IDAT (image data chunk). */ png_read_info(png_ptr, info_ptr); - if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) - png_error(png_ptr,"Image is too high to process with png_read_png()"); + if (info_ptr->height > PNG_UINT_32_MAX/(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 +#ifdef PNG_READ_SCALE_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); + if (transforms & PNG_TRANSFORM_SCALE_16) + { + /* Added at libpng-1.5.4. "strip_16" produces the same result that it + * did in earlier versions, while "scale_16" is now more accurate. + */ + png_set_scale_16(png_ptr); + } #endif -#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* If both SCALE and STRIP are required pngrtran will effectively cancel the + * latter by doing SCALE first. This is ok and allows apps not to check for + * which is supported to get the right answer. + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED /* Strip alpha bytes from the input data without combining with * the background (not recommended). */ if (transforms & PNG_TRANSFORM_STRIP_ALPHA) - png_set_strip_alpha(png_ptr); + png_set_strip_alpha(png_ptr); #endif #if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) @@ -1360,41 +984,41 @@ png_read_png(png_structp png_ptr, png_infop info_ptr, * byte into separate bytes (useful for paletted and grayscale images). */ if (transforms & PNG_TRANSFORM_PACKING) - png_set_packing(png_ptr); + png_set_packing(png_ptr); #endif -#if defined(PNG_READ_PACKSWAP_SUPPORTED) +#ifdef PNG_READ_PACKSWAP_SUPPORTED /* Change the order of packed pixels to least significant bit first * (not useful if you are using png_set_packing). */ if (transforms & PNG_TRANSFORM_PACKSWAP) - png_set_packswap(png_ptr); + png_set_packswap(png_ptr); #endif -#if defined(PNG_READ_EXPAND_SUPPORTED) +#ifdef PNG_READ_EXPAND_SUPPORTED /* Expand paletted colors into true RGB triplets * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel * Expand paletted or RGB images with transparency to full alpha * channels so the data will be available as RGBA quartets. */ if (transforms & PNG_TRANSFORM_EXPAND) - if ((png_ptr->bit_depth < 8) || - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || - (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) png_set_expand(png_ptr); #endif - /* We don't handle background color or gamma transformation or dithering. + /* We don't handle background color or gamma transformation or quantizing. */ -#if defined(PNG_READ_INVERT_SUPPORTED) - /* invert monochrome files to have 0 as white and 1 as black +#ifdef PNG_READ_INVERT_SUPPORTED + /* Invert monochrome files to have 0 as white and 1 as black */ if (transforms & PNG_TRANSFORM_INVERT_MONO) - png_set_invert_mono(png_ptr); + png_set_invert_mono(png_ptr); #endif -#if defined(PNG_READ_SHIFT_SUPPORTED) +#ifdef PNG_READ_SHIFT_SUPPORTED /* If you want to shift the pixel values from the range [0,255] or * [0,65535] to the original [0,7] or [0,31], or whatever range the * colors were originally in: @@ -1409,29 +1033,51 @@ 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) - */ +#ifdef PNG_READ_BGR_SUPPORTED + /* Flip the RGB pixels to BGR (or RGBA to BGRA) */ if (transforms & PNG_TRANSFORM_BGR) - png_set_bgr(png_ptr); + 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) - */ +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED + /* Swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) */ if (transforms & PNG_TRANSFORM_SWAP_ALPHA) - png_set_swap_alpha(png_ptr); + png_set_swap_alpha(png_ptr); #endif -#if defined(PNG_READ_SWAP_SUPPORTED) - /* swap bytes of 16 bit files to least significant byte first - */ +#ifdef PNG_READ_SWAP_SUPPORTED + /* Swap bytes of 16-bit files to least significant byte first */ if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) - png_set_swap(png_ptr); + png_set_swap(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + +/* Added at libpng-1.2.41 */ +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* Expand grayscale image to RGB */ + if (transforms & PNG_TRANSFORM_GRAY_TO_RGB) + png_set_gray_to_rgb(png_ptr); +#endif + +/* Added at libpng-1.5.4 */ +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if (transforms & PNG_TRANSFORM_EXPAND_16) + png_set_expand_16(png_ptr); #endif /* We don't handle adding filler bytes */ + /* We use png_read_image and rely on that for interlace handling, but we also + * call png_read_update_info therefore must turn on interlace handling now: + */ + (void)png_set_interlace_handling(png_ptr); + /* 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). @@ -1440,33 +1086,2915 @@ png_read_png(png_structp png_ptr, png_infop info_ptr, /* -------------- image transformations end here ------------------- */ -#ifdef PNG_FREE_ME_SUPPORTED png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); -#endif - if(info_ptr->row_pointers == NULL) + if (info_ptr->row_pointers == NULL) { + png_uint_32 iptr; + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, - info_ptr->height * png_sizeof(png_bytep)); -#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->height * (sizeof (png_bytep))); + for (iptr=0; iptrheight; iptr++) + info_ptr->row_pointers[iptr] = NULL; + info_ptr->free_me |= PNG_FREE_ROWS; -#endif + for (row = 0; row < (int)info_ptr->height; row++) - { info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); - } } png_read_image(png_ptr, info_ptr->row_pointers); info_ptr->valid |= PNG_INFO_IDAT; - /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + /* Read rest of file, and get additional chunks in info_ptr - REQUIRED */ png_read_end(png_ptr, info_ptr); - transforms = transforms; /* quiet compiler warnings */ - params = params; + PNG_UNUSED(transforms) /* Quiet compiler warnings */ + PNG_UNUSED(params) } #endif /* PNG_INFO_IMAGE_SUPPORTED */ -#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ + +#ifdef PNG_SIMPLIFIED_READ_SUPPORTED +/* SIMPLIFIED READ + * + * This code currently relies on the sequential reader, though it could easily + * be made to work with the progressive one. + */ +/* Arguments to png_image_finish_read: */ + +/* Encoding of PNG data (used by the color-map code) */ +/* TODO: change these, dang, ANSI-C reserves the 'E' namespace. */ +# define E_NOTSET 0 /* File encoding not yet known */ +# define E_sRGB 1 /* 8-bit encoded to sRGB gamma */ +# define E_LINEAR 2 /* 16-bit linear: not encoded, NOT pre-multiplied! */ +# define E_FILE 3 /* 8-bit encoded to file gamma, not sRGB or linear */ +# define E_LINEAR8 4 /* 8-bit linear: only from a file value */ + +/* Color-map processing: after libpng has run on the PNG image further + * processing may be needed to conver the data to color-map indicies. + */ +#define PNG_CMAP_NONE 0 +#define PNG_CMAP_GA 1 /* Process GA data to a color-map with alpha */ +#define PNG_CMAP_TRANS 2 /* Process GA data to a background index */ +#define PNG_CMAP_RGB 3 /* Process RGB data */ +#define PNG_CMAP_RGB_ALPHA 4 /* Process RGBA data */ + +/* The following document where the background is for each processing case. */ +#define PNG_CMAP_NONE_BACKGROUND 256 +#define PNG_CMAP_GA_BACKGROUND 231 +#define PNG_CMAP_TRANS_BACKGROUND 254 +#define PNG_CMAP_RGB_BACKGROUND 256 +#define PNG_CMAP_RGB_ALPHA_BACKGROUND 216 + +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_voidp buffer; + png_int_32 row_stride; + png_voidp colormap; + png_const_colorp background; + /* Local variables: */ + png_voidp local_row; + png_voidp first_row; + ptrdiff_t row_bytes; /* step between rows */ + int file_encoding; /* E_ values above */ + png_fixed_point gamma_to_linear; /* For E_FILE, reciprocal of gamma */ + int colormap_processing; /* PNG_CMAP_ values above */ +} png_image_read_control; + +/* Do all the *safe* initialization - 'safe' means that png_error won't be + * called, so setting up the jmp_buf is not required. This means that anything + * called from here must *not* call png_malloc - it has to call png_malloc_warn + * instead so that control is returned safely back to this routine. + */ +static int +png_image_read_init(png_imagep image) +{ + if (image->opaque == NULL) + { + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + /* And set the rest of the structure to NULL to ensure that the various + * fields are consistent. + */ + memset(image, 0, (sizeof *image)); + image->version = PNG_IMAGE_VERSION; + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr != NULL) + { + png_controlp control = png_voidcast(png_controlp, + png_malloc_warn(png_ptr, (sizeof *control))); + + if (control != NULL) + { + memset(control, 0, (sizeof *control)); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 0; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_read_struct(&png_ptr, NULL, NULL); + } + + return png_image_error(image, "png_image_read: out of memory"); + } + + return png_image_error(image, "png_image_read: opaque pointer not NULL"); +} + +/* Utility to find the base format of a PNG file from a png_struct. */ +static png_uint_32 +png_image_format(png_structrp png_ptr) +{ + png_uint_32 format = 0; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + format |= PNG_FORMAT_FLAG_COLOR; + + if (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) + format |= PNG_FORMAT_FLAG_ALPHA; + + /* Use png_ptr here, not info_ptr, because by examination png_handle_tRNS + * sets the png_struct fields; that's all we are interested in here. The + * precise interaction with an app call to png_set_tRNS and PNG file reading + * is unclear. + */ + else if (png_ptr->num_trans > 0) + format |= PNG_FORMAT_FLAG_ALPHA; + + if (png_ptr->bit_depth == 16) + format |= PNG_FORMAT_FLAG_LINEAR; + + if (png_ptr->color_type & PNG_COLOR_MASK_PALETTE) + format |= PNG_FORMAT_FLAG_COLORMAP; + + return format; +} + +/* Is the given gamma significantly different from sRGB? The test is the same + * one used in pngrtran.c when deciding whether to do gamma correction. The + * arithmetic optimizes the division by using the fact that the inverse of the + * file sRGB gamma is 2.2 + */ +static int +png_gamma_not_sRGB(png_fixed_point g) +{ + if (g < PNG_FP_1) + { + /* An uninitialized gamma is assumed to be sRGB for the simplified API. */ + if (g == 0) + return 0; + + return png_gamma_significant((g * 11 + 2)/5 /* i.e. *2.2, rounded */); + } + + return 1; +} + +/* Do the main body of a 'png_image_begin_read' function; read the PNG file + * header and fill in all the information. This is executed in a safe context, + * unlike the init routine above. + */ +static int +png_image_read_header(png_voidp argument) +{ + png_imagep image = png_voidcast(png_imagep, argument); + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + + png_set_benign_errors(png_ptr, 1/*warn*/); + png_read_info(png_ptr, info_ptr); + + /* Do this the fast way; just read directly out of png_struct. */ + image->width = png_ptr->width; + image->height = png_ptr->height; + + { + png_uint_32 format = png_image_format(png_ptr); + + image->format = format; + +#ifdef PNG_COLORSPACE_SUPPORTED + /* Does the colorspace match sRGB? If there is no color endpoint + * (colorant) information assume yes, otherwise require the + * 'ENDPOINTS_MATCHE_sRGB' colorspace flag to have been set. If the + * colorspace has been determined to be invalid ignore it. + */ + if ((format & PNG_FORMAT_FLAG_COLOR) != 0 && ((png_ptr->colorspace.flags + & (PNG_COLORSPACE_HAVE_ENDPOINTS|PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB| + PNG_COLORSPACE_INVALID)) == PNG_COLORSPACE_HAVE_ENDPOINTS)) + image->flags |= PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB; +#endif + } + + /* We need the maximum number of entries regardless of the format the + * application sets here. + */ + { + png_uint_32 cmap_entries; + + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + cmap_entries = 1U << png_ptr->bit_depth; + break; + + case PNG_COLOR_TYPE_PALETTE: + cmap_entries = png_ptr->num_palette; + break; + + default: + cmap_entries = 256; + break; + } + + if (cmap_entries > 256) + cmap_entries = 256; + + image->colormap_entries = cmap_entries; + } + + return 1; +} + +#ifdef PNG_STDIO_SUPPORTED +int PNGAPI +png_image_begin_read_from_stdio(png_imagep image, FILE* file) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file != NULL) + { + if (png_image_read_init(image)) + { + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_stdio: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_stdio: incorrect PNG_IMAGE_VERSION"); + + return 0; +} + +int PNGAPI +png_image_begin_read_from_file(png_imagep image, const char *file_name) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file_name != NULL) + { + FILE *fp = fopen(file_name, "rb"); + + if (fp != NULL) + { + if (png_image_read_init(image)) + { + image->opaque->png_ptr->io_ptr = fp; + image->opaque->owned_file = 1; + return png_safe_execute(image, png_image_read_header, image); + } + + /* Clean up: just the opened file. */ + (void)fclose(fp); + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_begin_read_from_file: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_file: incorrect PNG_IMAGE_VERSION"); + + return 0; +} +#endif /* PNG_STDIO_SUPPORTED */ + +static void PNGCBAPI +png_image_memory_read(png_structp png_ptr, png_bytep out, png_size_t need) +{ + if (png_ptr != NULL) + { + png_imagep image = png_voidcast(png_imagep, png_ptr->io_ptr); + if (image != NULL) + { + png_controlp cp = image->opaque; + if (cp != NULL) + { + png_const_bytep memory = cp->memory; + png_size_t size = cp->size; + + if (memory != NULL && size >= need) + { + memcpy(out, memory, need); + cp->memory = memory + need; + cp->size = size - need; + return; + } + + png_error(png_ptr, "read beyond end of data"); + } + } + + png_error(png_ptr, "invalid memory read"); + } +} + +int PNGAPI png_image_begin_read_from_memory(png_imagep image, + png_const_voidp memory, png_size_t size) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (memory != NULL && size > 0) + { + if (png_image_read_init(image)) + { + /* Now set the IO functions to read from the memory buffer and + * store it into io_ptr. Again do this in-place to avoid calling a + * libpng function that requires error handling. + */ + image->opaque->memory = png_voidcast(png_const_bytep, memory); + image->opaque->size = size; + image->opaque->png_ptr->io_ptr = image; + image->opaque->png_ptr->read_data_fn = png_image_memory_read; + + return png_safe_execute(image, png_image_read_header, image); + } + } + + else + return png_image_error(image, + "png_image_begin_read_from_memory: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_begin_read_from_memory: incorrect PNG_IMAGE_VERSION"); + + return 0; +} + +/* Utility function to skip chunks that are not used by the simplified image + * read functions and an appropriate macro to call it. + */ +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +static void +png_image_skip_unused_chunks(png_structrp png_ptr) +{ + /* Prepare the reader to ignore all recognized chunks whose data will not + * be used, i.e., all chunks recognized by libpng except for those + * involved in basic image reading: + * + * IHDR, PLTE, IDAT, IEND + * + * Or image data handling: + * + * tRNS, bKGD, gAMA, cHRM, sRGB, iCCP and sBIT. + * + * This provides a small performance improvement and eliminates any + * potential vulnerability to security problems in the unused chunks. + */ + { + static PNG_CONST png_byte chunks_to_process[] = { + 98, 75, 71, 68, '\0', /* bKGD */ + 99, 72, 82, 77, '\0', /* cHRM */ + 103, 65, 77, 65, '\0', /* gAMA */ + 105, 67, 67, 80, '\0', /* iCCP */ + 115, 66, 73, 84, '\0', /* sBIT */ + 115, 82, 71, 66, '\0', /* sRGB */ + }; + + /* Ignore unknown chunks and all other chunks except for the + * IHDR, PLTE, tRNS, IDAT, and IEND chunks. + */ + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_NEVER, + NULL, -1); + + /* But do not ignore image data handling chunks */ + png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_AS_DEFAULT, + chunks_to_process, (sizeof chunks_to_process)/5); + } +} + +# define PNG_SKIP_CHUNKS(p) png_image_skip_unused_chunks(p) +#else +# define PNG_SKIP_CHUNKS(p) ((void)0) +#endif /* PNG_HANDLE_AS_UNKNOWN_SUPPORTED */ + +/* The following macro gives the exact rounded answer for all values in the + * range 0..255 (it actually divides by 51.2, but the rounding still generates + * the correct numbers 0..5 + */ +#define PNG_DIV51(v8) (((v8) * 5 + 130) >> 8) + +/* Utility functions to make particular color-maps */ +static void +set_file_encoding(png_image_read_control *display) +{ + png_fixed_point g = display->image->opaque->png_ptr->colorspace.gamma; + if (png_gamma_significant(g)) + { + if (png_gamma_not_sRGB(g)) + { + display->file_encoding = E_FILE; + display->gamma_to_linear = png_reciprocal(g); + } + + else + display->file_encoding = E_sRGB; + } + + else + display->file_encoding = E_LINEAR8; +} + +static unsigned int +decode_gamma(png_image_read_control *display, png_uint_32 value, int encoding) +{ + if (encoding == E_FILE) /* double check */ + encoding = display->file_encoding; + + if (encoding == E_NOTSET) /* must be the file encoding */ + { + set_file_encoding(display); + encoding = display->file_encoding; + } + + switch (encoding) + { + case E_FILE: + value = png_gamma_16bit_correct(value*257, display->gamma_to_linear); + break; + + case E_sRGB: + value = png_sRGB_table[value]; + break; + + case E_LINEAR: + break; + + case E_LINEAR8: + value *= 257; + break; + + default: + png_error(display->image->opaque->png_ptr, + "unexpected encoding (internal error)"); + break; + } + + return value; +} + +static png_uint_32 +png_colormap_compose(png_image_read_control *display, + png_uint_32 foreground, int foreground_encoding, png_uint_32 alpha, + png_uint_32 background, int encoding) +{ + /* The file value is composed on the background, the background has the given + * encoding and so does the result, the file is encoded with E_FILE and the + * file and alpha are 8-bit values. The (output) encoding will always be + * E_LINEAR or E_sRGB. + */ + png_uint_32 f = decode_gamma(display, foreground, foreground_encoding); + png_uint_32 b = decode_gamma(display, background, encoding); + + /* The alpha is always an 8-bit value (it comes from the palette), the value + * scaled by 255 is what PNG_sRGB_FROM_LINEAR requires. + */ + f = f * alpha + b * (255-alpha); + + if (encoding == E_LINEAR) + { + /* Scale to 65535; divide by 255, approximately (in fact this is extremely + * accurate, it divides by 255.00000005937181414556, with no overflow.) + */ + f *= 257; /* Now scaled by 65535 */ + f += f >> 16; + f = (f+32768) >> 16; + } + + else /* E_sRGB */ + f = PNG_sRGB_FROM_LINEAR(f); + + return f; +} + +/* NOTE: E_LINEAR values to this routine must be 16-bit, but E_FILE values must + * be 8-bit. + */ +static void +png_create_colormap_entry(png_image_read_control *display, + png_uint_32 ip, png_uint_32 red, png_uint_32 green, png_uint_32 blue, + png_uint_32 alpha, int encoding) +{ + png_imagep image = display->image; + const int output_encoding = (image->format & PNG_FORMAT_FLAG_LINEAR) ? + E_LINEAR : E_sRGB; + const int convert_to_Y = (image->format & PNG_FORMAT_FLAG_COLOR) == 0 && + (red != green || green != blue); + + if (ip > 255) + png_error(image->opaque->png_ptr, "color-map index out of range"); + + /* Update the cache with whether the file gamma is significantly different + * from sRGB. + */ + if (encoding == E_FILE) + { + if (display->file_encoding == E_NOTSET) + set_file_encoding(display); + + /* Note that the cached value may be E_FILE too, but if it is then the + * gamma_to_linear member has been set. + */ + encoding = display->file_encoding; + } + + if (encoding == E_FILE) + { + png_fixed_point g = display->gamma_to_linear; + + red = png_gamma_16bit_correct(red*257, g); + green = png_gamma_16bit_correct(green*257, g); + blue = png_gamma_16bit_correct(blue*257, g); + + if (convert_to_Y || output_encoding == E_LINEAR) + { + alpha *= 257; + encoding = E_LINEAR; + } + + else + { + red = PNG_sRGB_FROM_LINEAR(red * 255); + green = PNG_sRGB_FROM_LINEAR(green * 255); + blue = PNG_sRGB_FROM_LINEAR(blue * 255); + encoding = E_sRGB; + } + } + + else if (encoding == E_LINEAR8) + { + /* This encoding occurs quite frequently in test cases because PngSuite + * includes a gAMA 1.0 chunk with most images. + */ + red *= 257; + green *= 257; + blue *= 257; + alpha *= 257; + encoding = E_LINEAR; + } + + else if (encoding == E_sRGB && (convert_to_Y || output_encoding == E_LINEAR)) + { + /* The values are 8-bit sRGB values, but must be converted to 16-bit + * linear. + */ + red = png_sRGB_table[red]; + green = png_sRGB_table[green]; + blue = png_sRGB_table[blue]; + alpha *= 257; + encoding = E_LINEAR; + } + + /* This is set if the color isn't gray but the output is. */ + if (encoding == E_LINEAR) + { + if (convert_to_Y) + { + /* NOTE: these values are copied from png_do_rgb_to_gray */ + png_uint_32 y = (png_uint_32)6968 * red + (png_uint_32)23434 * green + + (png_uint_32)2366 * blue; + + if (output_encoding == E_LINEAR) + y = (y + 16384) >> 15; + + else + { + /* y is scaled by 32768, we need it scaled by 255: */ + y = (y + 128) >> 8; + y *= 255; + y = PNG_sRGB_FROM_LINEAR((y + 64) >> 7); + encoding = E_sRGB; + } + + blue = red = green = y; + } + + else if (output_encoding == E_sRGB) + { + red = PNG_sRGB_FROM_LINEAR(red * 255); + green = PNG_sRGB_FROM_LINEAR(green * 255); + blue = PNG_sRGB_FROM_LINEAR(blue * 255); + alpha = PNG_DIV257(alpha); + encoding = E_sRGB; + } + } + + if (encoding != output_encoding) + png_error(image->opaque->png_ptr, "bad encoding (internal error)"); + + /* Store the value. */ + { +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int afirst = (image->format & PNG_FORMAT_FLAG_AFIRST) != 0 && + (image->format & PNG_FORMAT_FLAG_ALPHA) != 0; +# else +# define afirst 0 +# endif +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int bgr = (image->format & PNG_FORMAT_FLAG_BGR) ? 2 : 0; +# else +# define bgr 0 +# endif + + if (output_encoding == E_LINEAR) + { + png_uint_16p entry = png_voidcast(png_uint_16p, display->colormap); + + entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format); + + /* The linear 16-bit values must be pre-multiplied by the alpha channel + * value, if less than 65535 (this is, effectively, composite on black + * if the alpha channel is removed.) + */ + switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) + { + case 4: + entry[afirst ? 0 : 3] = (png_uint_16)alpha; + /* FALL THROUGH */ + + case 3: + if (alpha < 65535) + { + if (alpha > 0) + { + blue = (blue * alpha + 32767U)/65535U; + green = (green * alpha + 32767U)/65535U; + red = (red * alpha + 32767U)/65535U; + } + + else + red = green = blue = 0; + } + entry[afirst + (2 ^ bgr)] = (png_uint_16)blue; + entry[afirst + 1] = (png_uint_16)green; + entry[afirst + bgr] = (png_uint_16)red; + break; + + case 2: + entry[1 ^ afirst] = (png_uint_16)alpha; + /* FALL THROUGH */ + + case 1: + if (alpha < 65535) + { + if (alpha > 0) + green = (green * alpha + 32767U)/65535U; + + else + green = 0; + } + entry[afirst] = (png_uint_16)green; + break; + + default: + break; + } + } + + else /* output encoding is E_sRGB */ + { + png_bytep entry = png_voidcast(png_bytep, display->colormap); + + entry += ip * PNG_IMAGE_SAMPLE_CHANNELS(image->format); + + switch (PNG_IMAGE_SAMPLE_CHANNELS(image->format)) + { + case 4: + entry[afirst ? 0 : 3] = (png_byte)alpha; + case 3: + entry[afirst + (2 ^ bgr)] = (png_byte)blue; + entry[afirst + 1] = (png_byte)green; + entry[afirst + bgr] = (png_byte)red; + break; + + case 2: + entry[1 ^ afirst] = (png_byte)alpha; + case 1: + entry[afirst] = (png_byte)green; + break; + + default: + break; + } + } + +# ifdef afirst +# undef afirst +# endif +# ifdef bgr +# undef bgr +# endif + } +} + +static int +make_gray_file_colormap(png_image_read_control *display) +{ + unsigned int i; + + for (i=0; i<256; ++i) + png_create_colormap_entry(display, i, i, i, i, 255, E_FILE); + + return i; +} + +static int +make_gray_colormap(png_image_read_control *display) +{ + unsigned int i; + + for (i=0; i<256; ++i) + png_create_colormap_entry(display, i, i, i, i, 255, E_sRGB); + + return i; +} +#define PNG_GRAY_COLORMAP_ENTRIES 256 + +static int +make_ga_colormap(png_image_read_control *display) +{ + unsigned int i, a; + + /* Alpha is retained, the output will be a color-map with entries + * selected by six levels of alpha. One transparent entry, 6 gray + * levels for all the intermediate alpha values, leaving 230 entries + * for the opaque grays. The color-map entries are the six values + * [0..5]*51, the GA processing uses PNG_DIV51(value) to find the + * relevant entry. + * + * if (alpha > 229) // opaque + * { + * // The 231 entries are selected to make the math below work: + * base = 0; + * entry = (231 * gray + 128) >> 8; + * } + * else if (alpha < 26) // transparent + * { + * base = 231; + * entry = 0; + * } + * else // partially opaque + * { + * base = 226 + 6 * PNG_DIV51(alpha); + * entry = PNG_DIV51(gray); + * } + */ + i = 0; + while (i < 231) + { + unsigned int gray = (i * 256 + 115) / 231; + png_create_colormap_entry(display, i++, gray, gray, gray, 255, E_sRGB); + } + + /* 255 is used here for the component values for consistency with the code + * that undoes premultiplication in pngwrite.c. + */ + png_create_colormap_entry(display, i++, 255, 255, 255, 0, E_sRGB); + + for (a=1; a<5; ++a) + { + unsigned int g; + + for (g=0; g<6; ++g) + png_create_colormap_entry(display, i++, g*51, g*51, g*51, a*51, + E_sRGB); + } + + return i; +} + +#define PNG_GA_COLORMAP_ENTRIES 256 + +static int +make_rgb_colormap(png_image_read_control *display) +{ + unsigned int i, r; + + /* Build a 6x6x6 opaque RGB cube */ + for (i=r=0; r<6; ++r) + { + unsigned int g; + + for (g=0; g<6; ++g) + { + unsigned int b; + + for (b=0; b<6; ++b) + png_create_colormap_entry(display, i++, r*51, g*51, b*51, 255, + E_sRGB); + } + } + + return i; +} + +#define PNG_RGB_COLORMAP_ENTRIES 216 + +/* Return a palette index to the above palette given three 8-bit sRGB values. */ +#define PNG_RGB_INDEX(r,g,b) \ + ((png_byte)(6 * (6 * PNG_DIV51(r) + PNG_DIV51(g)) + PNG_DIV51(b))) + +static int +png_image_read_colormap(png_voidp argument) +{ + png_image_read_control *display = + png_voidcast(png_image_read_control*, argument); + const png_imagep image = display->image; + + const png_structrp png_ptr = image->opaque->png_ptr; + const png_uint_32 output_format = image->format; + const int output_encoding = (output_format & PNG_FORMAT_FLAG_LINEAR) ? + E_LINEAR : E_sRGB; + + unsigned int cmap_entries; + unsigned int output_processing; /* Output processing option */ + unsigned int data_encoding = E_NOTSET; /* Encoding libpng must produce */ + + /* Background information; the background color and the index of this color + * in the color-map if it exists (else 256). + */ + unsigned int background_index = 256; + png_uint_32 back_r, back_g, back_b; + + /* Flags to accumulate things that need to be done to the input. */ + int expand_tRNS = 0; + + /* Exclude the NYI feature of compositing onto a color-mapped buffer; it is + * very difficult to do, the results look awful, and it is difficult to see + * what possible use it is because the application can't control the + * color-map. + */ + if (((png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0 || + png_ptr->num_trans > 0) /* alpha in input */ && + ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) /* no alpha in output */) + { + if (output_encoding == E_LINEAR) /* compose on black */ + back_b = back_g = back_r = 0; + + else if (display->background == NULL /* no way to remove it */) + png_error(png_ptr, + "a background color must be supplied to remove alpha/transparency"); + + /* Get a copy of the background color (this avoids repeating the checks + * below.) The encoding is 8-bit sRGB or 16-bit linear, depending on the + * output format. + */ + else + { + back_g = display->background->green; + if (output_format & PNG_FORMAT_FLAG_COLOR) + { + back_r = display->background->red; + back_b = display->background->blue; + } + else + back_b = back_r = back_g; + } + } + + else if (output_encoding == E_LINEAR) + back_b = back_r = back_g = 65535; + + else + back_b = back_r = back_g = 255; + + /* Default the input file gamma if required - this is necessary because + * libpng assumes that if no gamma information is present the data is in the + * output format, but the simplified API deduces the gamma from the input + * format. + */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_GAMMA) == 0) + { + /* Do this directly, not using the png_colorspace functions, to ensure + * that it happens even if the colorspace is invalid (though probably if + * it is the setting will be ignored) Note that the same thing can be + * achieved at the application interface with png_set_gAMA. + */ + if (png_ptr->bit_depth == 16 && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + png_ptr->colorspace.gamma = PNG_GAMMA_LINEAR; + + else + png_ptr->colorspace.gamma = PNG_GAMMA_sRGB_INVERSE; + + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* Decide what to do based on the PNG color type of the input data. The + * utility function png_create_colormap_entry deals with most aspects of the + * output transformations; this code works out how to produce bytes of + * color-map entries from the original format. + */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + if (png_ptr->bit_depth <= 8) + { + /* There at most 256 colors in the output, regardless of + * transparency. + */ + unsigned int step, i, val, trans = 256/*ignore*/, back_alpha = 0; + + cmap_entries = 1U << png_ptr->bit_depth; + if (cmap_entries > image->colormap_entries) + png_error(png_ptr, "gray[8] color-map: too few entries"); + + step = 255 / (cmap_entries - 1); + output_processing = PNG_CMAP_NONE; + + /* If there is a tRNS chunk then this either selects a transparent + * value or, if the output has no alpha, the background color. + */ + if (png_ptr->num_trans > 0) + { + trans = png_ptr->trans_color.gray; + + if ((output_format & PNG_FORMAT_FLAG_ALPHA) == 0) + back_alpha = output_encoding == E_LINEAR ? 65535 : 255; + } + + /* png_create_colormap_entry just takes an RGBA and writes the + * corresponding color-map entry using the format from 'image', + * including the required conversion to sRGB or linear as + * appropriate. The input values are always either sRGB (if the + * gamma correction flag is 0) or 0..255 scaled file encoded values + * (if the function must gamma correct them). + */ + for (i=val=0; ibit_depth < 8) + png_set_packing(png_ptr); + } + + else /* bit depth is 16 */ + { + /* The 16-bit input values can be converted directly to 8-bit gamma + * encoded values; however, if a tRNS chunk is present 257 color-map + * entries are required. This means that the extra entry requires + * special processing; add an alpha channel, sacrifice gray level + * 254 and convert transparent (alpha==0) entries to that. + * + * Use libpng to chop the data to 8 bits. Convert it to sRGB at the + * same time to minimize quality loss. If a tRNS chunk is present + * this means libpng must handle it too; otherwise it is impossible + * to do the exact match on the 16-bit value. + * + * If the output has no alpha channel *and* the background color is + * gray then it is possible to let libpng handle the substitution by + * ensuring that the corresponding gray level matches the background + * color exactly. + */ + data_encoding = E_sRGB; + + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray[16] color-map: too few entries"); + + cmap_entries = make_gray_colormap(display); + + if (png_ptr->num_trans > 0) + { + unsigned int back_alpha; + + if (output_format & PNG_FORMAT_FLAG_ALPHA) + back_alpha = 0; + + else + { + if (back_r == back_g && back_g == back_b) + { + /* Background is gray; no special processing will be + * required. + */ + png_color_16 c; + png_uint_32 gray = back_g; + + if (output_encoding == E_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry + * matches. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 65535, E_LINEAR); + } + + /* The background passed to libpng, however, must be the + * sRGB value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + /* NOTE: does this work without expanding tRNS to alpha? + * It should be the color->gray case below apparently + * doesn't. + */ + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_NONE; + break; + } + + back_alpha = output_encoding == E_LINEAR ? 65535 : 255; + } + + /* output_processing means that the libpng-processed row will be + * 8-bit GA and it has to be processing to single byte color-map + * values. Entry 254 is replaced by either a completely + * transparent entry or by the background color at full + * precision (and the background color is not a simple gray leve + * in this case.) + */ + expand_tRNS = 1; + output_processing = PNG_CMAP_TRANS; + background_index = 254; + + /* And set (overwrite) color-map entry 254 to the actual + * background color at full precision. + */ + png_create_colormap_entry(display, 254, back_r, back_g, back_b, + back_alpha, output_encoding); + } + + else + output_processing = PNG_CMAP_NONE; + } + break; + + case PNG_COLOR_TYPE_GRAY_ALPHA: + /* 8-bit or 16-bit PNG with two channels - gray and alpha. A minimum + * of 65536 combinations. If, however, the alpha channel is to be + * removed there are only 256 possibilities if the background is gray. + * (Otherwise there is a subset of the 65536 possibilities defined by + * the triangle between black, white and the background color.) + * + * Reduce 16-bit files to 8-bit and sRGB encode the result. No need to + * worry about tRNS matching - tRNS is ignored if there is an alpha + * channel. + */ + data_encoding = E_sRGB; + + if (output_format & PNG_FORMAT_FLAG_ALPHA) + { + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray+alpha color-map: too few entries"); + + cmap_entries = make_ga_colormap(display); + + background_index = PNG_CMAP_GA_BACKGROUND; + output_processing = PNG_CMAP_GA; + } + + else /* alpha is removed */ + { + /* Alpha must be removed as the PNG data is processed when the + * background is a color because the G and A channels are + * independent and the vector addition (non-parallel vectors) is a + * 2-D problem. + * + * This can be reduced to the same algorithm as above by making a + * colormap containing gray levels (for the opaque grays), a + * background entry (for a transparent pixel) and a set of four six + * level color values, one set for each intermediate alpha value. + * See the comments in make_ga_colormap for how this works in the + * per-pixel processing. + * + * If the background is gray, however, we only need a 256 entry gray + * level color map. It is sufficient to make the entry generated + * for the background color be exactly the color specified. + */ + if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0 || + (back_r == back_g && back_g == back_b)) + { + /* Background is gray; no special processing will be required. */ + png_color_16 c; + png_uint_32 gray = back_g; + + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "gray-alpha color-map: too few entries"); + + cmap_entries = make_gray_colormap(display); + + if (output_encoding == E_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry matches. */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 65535, E_LINEAR); + } + + /* The background passed to libpng, however, must be the sRGB + * value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_NONE; + } + + else + { + png_uint_32 i, a; + + /* This is the same as png_make_ga_colormap, above, except that + * the entries are all opaque. + */ + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "ga-alpha color-map: too few entries"); + + i = 0; + while (i < 231) + { + png_uint_32 gray = (i * 256 + 115) / 231; + png_create_colormap_entry(display, i++, gray, gray, gray, + 255, E_sRGB); + } + + /* NOTE: this preserves the full precision of the application + * background color. + */ + background_index = i; + png_create_colormap_entry(display, i++, back_r, back_g, back_b, + output_encoding == E_LINEAR ? 65535U : 255U, output_encoding); + + /* For non-opaque input composite on the sRGB background - this + * requires inverting the encoding for each component. The input + * is still converted to the sRGB encoding because this is a + * reasonable approximate to the logarithmic curve of human + * visual sensitivity, at least over the narrow range which PNG + * represents. Consequently 'G' is always sRGB encoded, while + * 'A' is linear. We need the linear background colors. + */ + if (output_encoding == E_sRGB) /* else already linear */ + { + /* This may produce a value not exactly matching the + * background, but that's ok because these numbers are only + * used when alpha != 0 + */ + back_r = png_sRGB_table[back_r]; + back_g = png_sRGB_table[back_g]; + back_b = png_sRGB_table[back_b]; + } + + for (a=1; a<5; ++a) + { + unsigned int g; + + /* PNG_sRGB_FROM_LINEAR expects a 16-bit linear value scaled + * by an 8-bit alpha value (0..255). + */ + png_uint_32 alpha = 51 * a; + png_uint_32 back_rx = (255-alpha) * back_r; + png_uint_32 back_gx = (255-alpha) * back_g; + png_uint_32 back_bx = (255-alpha) * back_b; + + for (g=0; g<6; ++g) + { + png_uint_32 gray = png_sRGB_table[g*51] * alpha; + + png_create_colormap_entry(display, i++, + PNG_sRGB_FROM_LINEAR(gray + back_rx), + PNG_sRGB_FROM_LINEAR(gray + back_gx), + PNG_sRGB_FROM_LINEAR(gray + back_bx), 255, E_sRGB); + } + } + + cmap_entries = i; + output_processing = PNG_CMAP_GA; + } + } + break; + + case PNG_COLOR_TYPE_RGB: + case PNG_COLOR_TYPE_RGB_ALPHA: + /* Exclude the case where the output is gray; we can always handle this + * with the cases above. + */ + if ((output_format & PNG_FORMAT_FLAG_COLOR) == 0) + { + /* The color-map will be grayscale, so we may as well convert the + * input RGB values to a simple grayscale and use the grayscale + * code above. + * + * NOTE: calling this apparently damages the recognition of the + * transparent color in background color handling; call + * png_set_tRNS_to_alpha before png_set_background_fixed. + */ + png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, -1, + -1); + data_encoding = E_sRGB; + + /* The output will now be one or two 8-bit gray or gray+alpha + * channels. The more complex case arises when the input has alpha. + */ + if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) && + (output_format & PNG_FORMAT_FLAG_ALPHA) != 0) + { + /* Both input and output have an alpha channel, so no background + * processing is required; just map the GA bytes to the right + * color-map entry. + */ + expand_tRNS = 1; + + if (PNG_GA_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb[ga] color-map: too few entries"); + + cmap_entries = make_ga_colormap(display); + background_index = PNG_CMAP_GA_BACKGROUND; + output_processing = PNG_CMAP_GA; + } + + else + { + /* Either the input or the output has no alpha channel, so there + * will be no non-opaque pixels in the color-map; it will just be + * grayscale. + */ + if (PNG_GRAY_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb[gray] color-map: too few entries"); + + /* Ideally this code would use libpng to do the gamma correction, + * but if an input alpha channel is to be removed we will hit the + * libpng bug in gamma+compose+rgb-to-gray (the double gamma + * correction bug). Fix this by dropping the gamma correction in + * this case and doing it in the palette; this will result in + * duplicate palette entries, but that's better than the + * alternative of double gamma correction. + */ + if ((png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) && + png_gamma_not_sRGB(png_ptr->colorspace.gamma)) + { + cmap_entries = make_gray_file_colormap(display); + data_encoding = E_FILE; + } + + else + cmap_entries = make_gray_colormap(display); + + /* But if the input has alpha or transparency it must be removed + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) + { + png_color_16 c; + png_uint_32 gray = back_g; + + /* We need to ensure that the application background exists in + * the colormap and that completely transparent pixels map to + * it. Achieve this simply by ensuring that the entry + * selected for the background really is the background color. + */ + if (data_encoding == E_FILE) /* from the fixup above */ + { + /* The app supplied a gray which is in output_encoding, we + * need to convert it to a value of the input (E_FILE) + * encoding then set this palette entry to the required + * output encoding. + */ + if (output_encoding == E_sRGB) + gray = png_sRGB_table[gray]; /* now E_LINEAR */ + + gray = PNG_DIV257(png_gamma_16bit_correct(gray, + png_ptr->colorspace.gamma)); /* now E_FILE */ + + /* And make sure the corresponding palette entry contains + * exactly the required sRGB value. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 0/*unused*/, output_encoding); + } + + else if (output_encoding == E_LINEAR) + { + gray = PNG_sRGB_FROM_LINEAR(gray * 255); + + /* And make sure the corresponding palette entry matches. + */ + png_create_colormap_entry(display, gray, back_g, back_g, + back_g, 0/*unused*/, E_LINEAR); + } + + /* The background passed to libpng, however, must be the + * output (normally sRGB) value. + */ + c.index = 0; /*unused*/ + c.gray = c.red = c.green = c.blue = (png_uint_16)gray; + + /* NOTE: the following is apparently a bug in libpng. Without + * it the transparent color recognition in + * png_set_background_fixed seems to go wrong. + */ + expand_tRNS = 1; + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + } + + output_processing = PNG_CMAP_NONE; + } + } + + else /* output is color */ + { + /* We could use png_quantize here so long as there is no transparent + * color or alpha; png_quantize ignores alpha. Easier overall just + * to do it once and using PNG_DIV51 on the 6x6x6 reduced RGB cube. + * Consequently we always want libpng to produce sRGB data. + */ + data_encoding = E_sRGB; + + /* Is there any transparency or alpha? */ + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + png_ptr->num_trans > 0) + { + /* Is there alpha in the output too? If so all four channels are + * processed into a special RGB cube with alpha support. + */ + if (output_format & PNG_FORMAT_FLAG_ALPHA) + { + png_uint_32 r; + + if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries) + png_error(png_ptr, "rgb+alpha color-map: too few entries"); + + cmap_entries = make_rgb_colormap(display); + + /* Add a transparent entry. */ + png_create_colormap_entry(display, cmap_entries, 255, 255, + 255, 0, E_sRGB); + + /* This is stored as the background index for the processing + * algorithm. + */ + background_index = cmap_entries++; + + /* Add 27 r,g,b entries each with alpha 0.5. */ + for (r=0; r<256; r = (r << 1) | 0x7f) + { + png_uint_32 g; + + for (g=0; g<256; g = (g << 1) | 0x7f) + { + png_uint_32 b; + + /* This generates components with the values 0, 127 and + * 255 + */ + for (b=0; b<256; b = (b << 1) | 0x7f) + png_create_colormap_entry(display, cmap_entries++, + r, g, b, 128, E_sRGB); + } + } + + expand_tRNS = 1; + output_processing = PNG_CMAP_RGB_ALPHA; + } + + else + { + /* Alpha/transparency must be removed. The background must + * exist in the color map (achieved by setting adding it after + * the 666 color-map). If the standard processing code will + * pick up this entry automatically that's all that is + * required; libpng can be called to do the background + * processing. + */ + unsigned int sample_size = + PNG_IMAGE_SAMPLE_SIZE(output_format); + png_uint_32 r, g, b; /* sRGB background */ + + if (PNG_RGB_COLORMAP_ENTRIES+1+27 > image->colormap_entries) + png_error(png_ptr, "rgb-alpha color-map: too few entries"); + + cmap_entries = make_rgb_colormap(display); + + png_create_colormap_entry(display, cmap_entries, back_r, + back_g, back_b, 0/*unused*/, output_encoding); + + if (output_encoding == E_LINEAR) + { + r = PNG_sRGB_FROM_LINEAR(back_r * 255); + g = PNG_sRGB_FROM_LINEAR(back_g * 255); + b = PNG_sRGB_FROM_LINEAR(back_b * 255); + } + + else + { + r = back_r; + g = back_g; + b = back_g; + } + + /* Compare the newly-created color-map entry with the one the + * PNG_CMAP_RGB algorithm will use. If the two entries don't + * match, add the new one and set this as the background + * index. + */ + if (memcmp((png_const_bytep)display->colormap + + sample_size * cmap_entries, + (png_const_bytep)display->colormap + + sample_size * PNG_RGB_INDEX(r,g,b), + sample_size) != 0) + { + /* The background color must be added. */ + background_index = cmap_entries++; + + /* Add 27 r,g,b entries each with created by composing with + * the background at alpha 0.5. + */ + for (r=0; r<256; r = (r << 1) | 0x7f) + { + for (g=0; g<256; g = (g << 1) | 0x7f) + { + /* This generates components with the values 0, 127 + * and 255 + */ + for (b=0; b<256; b = (b << 1) | 0x7f) + png_create_colormap_entry(display, cmap_entries++, + png_colormap_compose(display, r, E_sRGB, 128, + back_r, output_encoding), + png_colormap_compose(display, g, E_sRGB, 128, + back_g, output_encoding), + png_colormap_compose(display, b, E_sRGB, 128, + back_b, output_encoding), + 0/*unused*/, output_encoding); + } + } + + expand_tRNS = 1; + output_processing = PNG_CMAP_RGB_ALPHA; + } + + else /* background color is in the standard color-map */ + { + png_color_16 c; + + c.index = 0; /*unused*/ + c.red = (png_uint_16)back_r; + c.gray = c.green = (png_uint_16)back_g; + c.blue = (png_uint_16)back_b; + + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + + output_processing = PNG_CMAP_RGB; + } + } + } + + else /* no alpha or transparency in the input */ + { + /* Alpha in the output is irrelevant, simply map the opaque input + * pixels to the 6x6x6 color-map. + */ + if (PNG_RGB_COLORMAP_ENTRIES > image->colormap_entries) + png_error(png_ptr, "rgb color-map: too few entries"); + + cmap_entries = make_rgb_colormap(display); + output_processing = PNG_CMAP_RGB; + } + } + break; + + case PNG_COLOR_TYPE_PALETTE: + /* It's already got a color-map. It may be necessary to eliminate the + * tRNS entries though. + */ + { + unsigned int num_trans = png_ptr->num_trans; + png_const_bytep trans = num_trans > 0 ? png_ptr->trans_alpha : NULL; + png_const_colorp colormap = png_ptr->palette; + const int do_background = trans != NULL && + (output_format & PNG_FORMAT_FLAG_ALPHA) == 0; + unsigned int i; + + /* Just in case: */ + if (trans == NULL) + num_trans = 0; + + output_processing = PNG_CMAP_NONE; + data_encoding = E_FILE; /* Don't change from color-map indicies */ + cmap_entries = png_ptr->num_palette; + if (cmap_entries > 256) + cmap_entries = 256; + + if (cmap_entries > image->colormap_entries) + png_error(png_ptr, "palette color-map: too few entries"); + + for (i=0; i < cmap_entries; ++i) + { + if (do_background && i < num_trans && trans[i] < 255) + { + if (trans[i] == 0) + png_create_colormap_entry(display, i, back_r, back_g, + back_b, 0, output_encoding); + + else + { + /* Must compose the PNG file color in the color-map entry + * on the sRGB color in 'back'. + */ + png_create_colormap_entry(display, i, + png_colormap_compose(display, colormap[i].red, E_FILE, + trans[i], back_r, output_encoding), + png_colormap_compose(display, colormap[i].green, E_FILE, + trans[i], back_g, output_encoding), + png_colormap_compose(display, colormap[i].blue, E_FILE, + trans[i], back_b, output_encoding), + output_encoding == E_LINEAR ? trans[i] * 257U : + trans[i], + output_encoding); + } + } + + else + png_create_colormap_entry(display, i, colormap[i].red, + colormap[i].green, colormap[i].blue, + i < num_trans ? trans[i] : 255U, E_FILE/*8-bit*/); + } + + /* The PNG data may have indicies packed in fewer than 8 bits, it + * must be expanded if so. + */ + if (png_ptr->bit_depth < 8) + png_set_packing(png_ptr); + } + break; + + default: + png_error(png_ptr, "invalid PNG color type"); + /*NOT REACHED*/ + break; + } + + /* Now deal with the output processing */ + if (expand_tRNS && png_ptr->num_trans > 0 && + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) == 0) + png_set_tRNS_to_alpha(png_ptr); + + switch (data_encoding) + { + default: + png_error(png_ptr, "bad data option (internal error)"); + break; + + case E_sRGB: + /* Change to 8-bit sRGB */ + png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, PNG_GAMMA_sRGB); + /* FALL THROUGH */ + + case E_FILE: + if (png_ptr->bit_depth > 8) + png_set_scale_16(png_ptr); + break; + } + + if (cmap_entries > 256 || cmap_entries > image->colormap_entries) + png_error(png_ptr, "color map overflow (BAD internal error)"); + + image->colormap_entries = cmap_entries; + + /* Double check using the recorded background index */ + switch (output_processing) + { + case PNG_CMAP_NONE: + if (background_index != PNG_CMAP_NONE_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_GA: + if (background_index != PNG_CMAP_GA_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_TRANS: + if (background_index >= cmap_entries || + background_index != PNG_CMAP_TRANS_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_RGB: + if (background_index != PNG_CMAP_RGB_BACKGROUND) + goto bad_background; + break; + + case PNG_CMAP_RGB_ALPHA: + if (background_index != PNG_CMAP_RGB_ALPHA_BACKGROUND) + goto bad_background; + break; + + default: + png_error(png_ptr, "bad processing option (internal error)"); + + bad_background: + png_error(png_ptr, "bad background index (internal error)"); + } + + display->colormap_processing = output_processing; + + return 1/*ok*/; +} + +/* The final part of the color-map read called from png_image_finish_read. */ +static int +png_image_read_and_map(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + int passes; + + /* Called when the libpng data must be transformed into the color-mapped + * form. There is a local row buffer in display->local and this routine must + * do the interlace handling. + */ + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + passes = 0; + png_error(png_ptr, "unknown interlace type"); + } + + { + png_uint_32 height = image->height; + png_uint_32 width = image->width; + int proc = display->colormap_processing; + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t step_row = display->row_bytes; + int pass; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass); + stepx = PNG_PASS_COL_OFFSET(pass); + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = stepy = 1; + } + + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read read the libpng data into the temporary buffer. */ + png_read_row(png_ptr, inrow, NULL); + + /* Now process the row according to the processing option, note + * that the caller verifies that the format of the libpng output + * data is as required. + */ + outrow += startx; + switch (proc) + { + case PNG_CMAP_GA: + for (; outrow < end_row; outrow += stepx) + { + /* The data is always in the PNG order */ + unsigned int gray = *inrow++; + unsigned int alpha = *inrow++; + unsigned int entry; + + /* NOTE: this code is copied as a comment in + * make_ga_colormap above. Please update the + * comment if you change this code! + */ + if (alpha > 229) /* opaque */ + { + entry = (231 * gray + 128) >> 8; + } + else if (alpha < 26) /* transparent */ + { + entry = 231; + } + else /* partially opaque */ + { + entry = 226 + 6 * PNG_DIV51(alpha) + PNG_DIV51(gray); + } + + *outrow = (png_byte)entry; + } + break; + + case PNG_CMAP_TRANS: + for (; outrow < end_row; outrow += stepx) + { + png_byte gray = *inrow++; + png_byte alpha = *inrow++; + + if (alpha == 0) + *outrow = PNG_CMAP_TRANS_BACKGROUND; + + else if (gray != PNG_CMAP_TRANS_BACKGROUND) + *outrow = gray; + + else + *outrow = (png_byte)(PNG_CMAP_TRANS_BACKGROUND+1); + } + break; + + case PNG_CMAP_RGB: + for (; outrow < end_row; outrow += stepx) + { + *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], inrow[2]); + inrow += 3; + } + break; + + case PNG_CMAP_RGB_ALPHA: + for (; outrow < end_row; outrow += stepx) + { + unsigned int alpha = inrow[3]; + + /* Because the alpha entries only hold alpha==0.5 values + * split the processing at alpha==0.25 (64) and 0.75 + * (196). + */ + + if (alpha >= 196) + *outrow = PNG_RGB_INDEX(inrow[0], inrow[1], + inrow[2]); + + else if (alpha < 64) + *outrow = PNG_CMAP_RGB_ALPHA_BACKGROUND; + + else + { + /* Likewise there are three entries for each of r, g + * and b. We could select the entry by popcount on + * the top two bits on those architectures that + * support it, this is what the code below does, + * crudely. + */ + unsigned int back_i = PNG_CMAP_RGB_ALPHA_BACKGROUND+1; + + /* Here are how the values map: + * + * 0x00 .. 0x3f -> 0 + * 0x40 .. 0xbf -> 1 + * 0xc0 .. 0xff -> 2 + * + * So, as above with the explicit alpha checks, the + * breakpoints are at 64 and 196. + */ + if (inrow[0] & 0x80) back_i += 9; /* red */ + if (inrow[0] & 0x40) back_i += 9; + if (inrow[0] & 0x80) back_i += 3; /* green */ + if (inrow[0] & 0x40) back_i += 3; + if (inrow[0] & 0x80) back_i += 1; /* blue */ + if (inrow[0] & 0x40) back_i += 1; + + *outrow = (png_byte)back_i; + } + + inrow += 4; + } + break; + + default: + break; + } + } + } + } + + return 1; +} + +static int +png_image_read_colormapped(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_controlp control = image->opaque; + png_structrp png_ptr = control->png_ptr; + png_inforp info_ptr = control->info_ptr; + + int passes = 0; /* As a flag */ + + PNG_SKIP_CHUNKS(png_ptr); + + /* Update the 'info' structure and make sure the result is as required; first + * make sure to turn on the interlace handling if it will be required + * (because it can't be turned on *after* the call to png_read_update_info!) + */ + if (display->colormap_processing == PNG_CMAP_NONE) + passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + /* The expected output can be deduced from the colormap_processing option. */ + switch (display->colormap_processing) + { + case PNG_CMAP_NONE: + /* Output must be one channel and one byte per pixel, the output + * encoding can be anything. + */ + if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + info_ptr->color_type == PNG_COLOR_TYPE_GRAY) && + info_ptr->bit_depth == 8) + break; + + goto bad_output; + + case PNG_CMAP_TRANS: + case PNG_CMAP_GA: + /* Output must be two channels and the 'G' one must be sRGB, the latter + * can be checked with an exact number because it should have been set + * to this number above! + */ + if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 256) + break; + + goto bad_output; + + case PNG_CMAP_RGB: + /* Output must be 8-bit sRGB encoded RGB */ + if (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 216) + break; + + goto bad_output; + + case PNG_CMAP_RGB_ALPHA: + /* Output must be 8-bit sRGB encoded RGBA */ + if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + info_ptr->bit_depth == 8 && + png_ptr->screen_gamma == PNG_GAMMA_sRGB && + image->colormap_entries == 244 /* 216 + 1 + 27 */) + break; + + /* goto bad_output; */ + /* FALL THROUGH */ + + default: + bad_output: + png_error(png_ptr, "bad color-map processing (internal error)"); + } + + /* Now read the rows. Do this here if it is possible to read directly into + * the output buffer, otherwise allocate a local row buffer of the maximum + * size libpng requires and call the relevant processing routine safely. + */ + { + png_voidp first_row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + /* The following expression is designed to work correctly whether it gives + * a signed or an unsigned result. + */ + if (row_bytes < 0) + { + char *ptr = png_voidcast(char*, first_row); + ptr += (image->height-1) * (-row_bytes); + first_row = png_voidcast(png_voidp, ptr); + } + + display->first_row = first_row; + display->row_bytes = row_bytes; + } + + if (passes == 0) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_and_map, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else + { + png_alloc_size_t row_bytes = display->row_bytes; + + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep row = png_voidcast(png_bytep, display->first_row); + + while (y-- > 0) + { + png_read_row(png_ptr, row, NULL); + row += row_bytes; + } + } + + return 1; + } +} + +/* Just the row reading part of png_image_read. */ +static int +png_image_read_composite(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + int passes; + + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + passes = 0; + png_error(png_ptr, "unknown interlace type"); + } + + { + png_uint_32 height = image->height; + png_uint_32 width = image->width; + ptrdiff_t step_row = display->row_bytes; + unsigned int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + int pass; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass) * channels; + stepx = PNG_PASS_COL_OFFSET(pass) * channels; + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = channels; + stepy = 1; + } + + for (; ylocal_row); + png_bytep outrow; + png_const_bytep end_row; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + outrow = png_voidcast(png_bytep, display->first_row); + outrow += y * step_row; + end_row = outrow + width * channels; + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[channels]; + + if (alpha > 0) /* else no change to the output */ + { + unsigned int c; + + for (c=0; cimage; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + png_uint_32 height = image->height; + png_uint_32 width = image->width; + int pass, passes; + + /* Double check the convoluted logic below. We expect to get here with + * libpng doing rgb to gray and gamma correction but background processing + * left to the png_image_read_background function. The rows libpng produce + * might be 8 or 16-bit but should always have two channels; gray plus alpha. + */ + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) + png_error(png_ptr, "lost rgb to gray"); + + if ((png_ptr->transformations & PNG_COMPOSE) != 0) + png_error(png_ptr, "unexpected compose"); + + if (png_get_channels(png_ptr, info_ptr) != 2) + png_error(png_ptr, "lost/gained channels"); + + /* Expect the 8-bit case to always remove the alpha channel */ + if ((image->format & PNG_FORMAT_FLAG_LINEAR) == 0 && + (image->format & PNG_FORMAT_FLAG_ALPHA) != 0) + png_error(png_ptr, "unexpected 8-bit transformation"); + + switch (png_ptr->interlaced) + { + case PNG_INTERLACE_NONE: + passes = 1; + break; + + case PNG_INTERLACE_ADAM7: + passes = PNG_INTERLACE_ADAM7_PASSES; + break; + + default: + passes = 0; + png_error(png_ptr, "unknown interlace type"); + } + + switch (png_get_bit_depth(png_ptr, info_ptr)) + { + default: + png_error(png_ptr, "unexpected bit depth"); + break; + + case 8: + /* 8-bit sRGB gray values with an alpha channel; the alpha channel is + * to be removed by composing on a backgroundi: either the row if + * display->background is NULL or display->background->green if not. + * Unlike the code above ALPHA_OPTIMIZED has *not* been done. + */ + { + png_bytep first_row = png_voidcast(png_bytep, display->first_row); + ptrdiff_t step_row = display->row_bytes; + + for (pass = 0; pass < passes; ++pass) + { + png_bytep row = png_voidcast(png_bytep, + display->first_row); + unsigned int startx, stepx, stepy; + png_uint_32 y; + + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass); + stepx = PNG_PASS_COL_OFFSET(pass); + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = stepy = 1; + } + + if (display->background == NULL) + { + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[1]; + + if (alpha > 0) /* else no change to the output */ + { + png_uint_32 component = inrow[0]; + + if (alpha < 255) /* else just use component */ + { + /* Since PNG_OPTIMIZED_ALPHA was not set it is + * necessary to invert the sRGB transfer + * function and multiply the alpha out. + */ + component = png_sRGB_table[component] * alpha; + component += png_sRGB_table[outrow[0]] * + (255-alpha); + component = PNG_sRGB_FROM_LINEAR(component); + } + + outrow[0] = (png_byte)component; + } + + inrow += 2; /* gray and alpha channel */ + } + } + } + + else /* constant background value */ + { + png_byte background8 = display->background->green; + png_uint_16 background = png_sRGB_table[background8]; + + for (; ylocal_row); + png_bytep outrow = first_row + y * step_row; + png_const_bytep end_row = outrow + width; + + /* Read the row, which is packed: */ + png_read_row(png_ptr, inrow, NULL); + + /* Now do the composition on each pixel in this row. */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_byte alpha = inrow[1]; + + if (alpha > 0) /* else use background */ + { + png_uint_32 component = inrow[0]; + + if (alpha < 255) /* else just use component */ + { + component = png_sRGB_table[component] * alpha; + component += background * (255-alpha); + component = PNG_sRGB_FROM_LINEAR(component); + } + + outrow[0] = (png_byte)component; + } + + else + outrow[0] = background8; + + inrow += 2; /* gray and alpha channel */ + } + + row += display->row_bytes; + } + } + } + } + break; + + case 16: + /* 16-bit linear with pre-multiplied alpha; the pre-multiplication must + * still be done and, maybe, the alpha channel removed. This code also + * handles the alpha-first option. + */ + { + png_uint_16p first_row = png_voidcast(png_uint_16p, + display->first_row); + /* The division by two is safe because the caller passed in a + * stride which was multiplied by 2 (below) to get row_bytes. + */ + ptrdiff_t step_row = display->row_bytes / 2; + int preserve_alpha = (image->format & PNG_FORMAT_FLAG_ALPHA) != 0; + unsigned int outchannels = 1+preserve_alpha; + int swap_alpha = 0; + + if (preserve_alpha && (image->format & PNG_FORMAT_FLAG_AFIRST)) + swap_alpha = 1; + + for (pass = 0; pass < passes; ++pass) + { + unsigned int startx, stepx, stepy; + png_uint_32 y; + + /* The 'x' start and step are adjusted to output components here. + */ + if (png_ptr->interlaced == PNG_INTERLACE_ADAM7) + { + /* The row may be empty for a short image: */ + if (PNG_PASS_COLS(width, pass) == 0) + continue; + + startx = PNG_PASS_START_COL(pass) * outchannels; + stepx = PNG_PASS_COL_OFFSET(pass) * outchannels; + y = PNG_PASS_START_ROW(pass); + stepy = PNG_PASS_ROW_OFFSET(pass); + } + + else + { + y = 0; + startx = 0; + stepx = outchannels; + stepy = 1; + } + + for (; ylocal_row), NULL); + inrow = png_voidcast(png_const_uint_16p, display->local_row); + + /* Now do the pre-multiplication on each pixel in this row. + */ + outrow += startx; + for (; outrow < end_row; outrow += stepx) + { + png_uint_32 component = inrow[0]; + png_uint_16 alpha = inrow[1]; + + if (alpha > 0) /* else 0 */ + { + if (alpha < 65535) /* else just use component */ + { + component *= alpha; + component += 32767; + component /= 65535; + } + } + + else + component = 0; + + outrow[swap_alpha] = (png_uint_16)component; + if (preserve_alpha) + outrow[1 ^ swap_alpha] = alpha; + + inrow += 2; /* components and alpha channel */ + } + } + } + } + break; + } + + return 1; +} + +/* The guts of png_image_finish_read as a png_safe_execute callback. */ +static int +png_image_read_direct(png_voidp argument) +{ + png_image_read_control *display = png_voidcast(png_image_read_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + + png_uint_32 format = image->format; + int linear = (format & PNG_FORMAT_FLAG_LINEAR) != 0; + int do_local_compose = 0; + int do_local_background = 0; /* to avoid double gamma correction bug */ + int passes = 0; + + /* Add transforms to ensure the correct output format is produced then check + * that the required implementation support is there. Always expand; always + * need 8 bits minimum, no palette and expanded tRNS. + */ + png_set_expand(png_ptr); + + /* Now check the format to see if it was modified. */ + { + png_uint_32 base_format = png_image_format(png_ptr) & + ~PNG_FORMAT_FLAG_COLORMAP /* removed by png_set_expand */; + png_uint_32 change = format ^ base_format; + png_fixed_point output_gamma; + int mode; /* alpha mode */ + + /* Do this first so that we have a record if rgb to gray is happening. */ + if (change & PNG_FORMAT_FLAG_COLOR) + { + /* gray<->color transformation required. */ + if (format & PNG_FORMAT_FLAG_COLOR) + png_set_gray_to_rgb(png_ptr); + + else + { + /* libpng can't do both rgb to gray and + * background/pre-multiplication if there is also significant gamma + * correction, because both operations require linear colors and + * the code only supports one transform doing the gamma correction. + * Handle this by doing the pre-multiplication or background + * operation in this code, if necessary. + * + * TODO: fix this by rewriting pngrtran.c (!) + * + * For the moment (given that fixing this in pngrtran.c is an + * enormous change) 'do_local_background' is used to indicate that + * the problem exists. + */ + if (base_format & PNG_FORMAT_FLAG_ALPHA) + do_local_background = 1/*maybe*/; + + png_set_rgb_to_gray_fixed(png_ptr, PNG_ERROR_ACTION_NONE, + PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); + } + + change &= ~PNG_FORMAT_FLAG_COLOR; + } + + /* Set the gamma appropriately, linear for 16-bit input, sRGB otherwise. + */ + { + png_fixed_point input_gamma_default; + + if ((base_format & PNG_FORMAT_FLAG_LINEAR) && + (image->flags & PNG_IMAGE_FLAG_16BIT_sRGB) == 0) + input_gamma_default = PNG_GAMMA_LINEAR; + else + input_gamma_default = PNG_DEFAULT_sRGB; + + /* Call png_set_alpha_mode to set the default for the input gamma; the + * output gamma is set by a second call below. + */ + png_set_alpha_mode_fixed(png_ptr, PNG_ALPHA_PNG, input_gamma_default); + } + + if (linear) + { + /* If there *is* an alpha channel in the input it must be multiplied + * out; use PNG_ALPHA_STANDARD, otherwise just use PNG_ALPHA_PNG. + */ + if (base_format & PNG_FORMAT_FLAG_ALPHA) + mode = PNG_ALPHA_STANDARD; /* associated alpha */ + + else + mode = PNG_ALPHA_PNG; + + output_gamma = PNG_GAMMA_LINEAR; + } + + else + { + mode = PNG_ALPHA_PNG; + output_gamma = PNG_DEFAULT_sRGB; + } + + /* If 'do_local_background' is set check for the presence of gamma + * correction; this is part of the work-round for the libpng bug + * described above. + * + * TODO: fix libpng and remove this. + */ + if (do_local_background) + { + png_fixed_point gtest; + + /* This is 'png_gamma_threshold' from pngrtran.c; the test used for + * gamma correction, the screen gamma hasn't been set on png_struct + * yet; it's set below. png_struct::gamma, however, is set to the + * final value. + */ + if (png_muldiv(>est, output_gamma, png_ptr->colorspace.gamma, + PNG_FP_1) && !png_gamma_significant(gtest)) + do_local_background = 0; + + else if (mode == PNG_ALPHA_STANDARD) + { + do_local_background = 2/*required*/; + mode = PNG_ALPHA_PNG; /* prevent libpng doing it */ + } + + /* else leave as 1 for the checks below */ + } + + /* If the bit-depth changes then handle that here. */ + if (change & PNG_FORMAT_FLAG_LINEAR) + { + if (linear /*16-bit output*/) + png_set_expand_16(png_ptr); + + else /* 8-bit output */ + png_set_scale_16(png_ptr); + + change &= ~PNG_FORMAT_FLAG_LINEAR; + } + + /* Now the background/alpha channel changes. */ + if (change & PNG_FORMAT_FLAG_ALPHA) + { + /* Removing an alpha channel requires composition for the 8-bit + * formats; for the 16-bit it is already done, above, by the + * pre-multiplication and the channel just needs to be stripped. + */ + if (base_format & PNG_FORMAT_FLAG_ALPHA) + { + /* If RGB->gray is happening the alpha channel must be left and the + * operation completed locally. + * + * TODO: fix libpng and remove this. + */ + if (do_local_background) + do_local_background = 2/*required*/; + + /* 16-bit output: just remove the channel */ + else if (linear) /* compose on black (well, pre-multiply) */ + png_set_strip_alpha(png_ptr); + + /* 8-bit output: do an appropriate compose */ + else if (display->background != NULL) + { + png_color_16 c; + + c.index = 0; /*unused*/ + c.red = display->background->red; + c.green = display->background->green; + c.blue = display->background->blue; + c.gray = display->background->green; + + /* This is always an 8-bit sRGB value, using the 'green' channel + * for gray is much better than calculating the luminance here; + * we can get off-by-one errors in that calculation relative to + * the app expectations and that will show up in transparent + * pixels. + */ + png_set_background_fixed(png_ptr, &c, + PNG_BACKGROUND_GAMMA_SCREEN, 0/*need_expand*/, + 0/*gamma: not used*/); + } + + else /* compose on row: implemented below. */ + { + do_local_compose = 1; + /* This leaves the alpha channel in the output, so it has to be + * removed by the code below. Set the encoding to the 'OPTIMIZE' + * one so the code only has to hack on the pixels that require + * composition. + */ + mode = PNG_ALPHA_OPTIMIZED; + } + } + + else /* output needs an alpha channel */ + { + /* This is tricky because it happens before the swap operation has + * been accomplished; however, the swap does *not* swap the added + * alpha channel (weird API), so it must be added in the correct + * place. + */ + png_uint_32 filler; /* opaque filler */ + int where; + + if (linear) + filler = 65535; + + else + filler = 255; + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (format & PNG_FORMAT_FLAG_AFIRST) + { + where = PNG_FILLER_BEFORE; + change &= ~PNG_FORMAT_FLAG_AFIRST; + } + + else +# endif + where = PNG_FILLER_AFTER; + + png_set_add_alpha(png_ptr, filler, where); + } + + /* This stops the (irrelevant) call to swap_alpha below. */ + change &= ~PNG_FORMAT_FLAG_ALPHA; + } + + /* Now set the alpha mode correctly; this is always done, even if there is + * no alpha channel in either the input or the output because it correctly + * sets the output gamma. + */ + png_set_alpha_mode_fixed(png_ptr, mode, output_gamma); + +# ifdef PNG_FORMAT_BGR_SUPPORTED + if (change & PNG_FORMAT_FLAG_BGR) + { + /* Check only the output format; PNG is never BGR; don't do this if + * the output is gray, but fix up the 'format' value in that case. + */ + if (format & PNG_FORMAT_FLAG_COLOR) + png_set_bgr(png_ptr); + + else + format &= ~PNG_FORMAT_FLAG_BGR; + + change &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (change & PNG_FORMAT_FLAG_AFIRST) + { + /* Only relevant if there is an alpha channel - it's particularly + * important to handle this correctly because do_local_compose may + * be set above and then libpng will keep the alpha channel for this + * code to remove. + */ + if (format & PNG_FORMAT_FLAG_ALPHA) + { + /* Disable this if doing a local background, + * TODO: remove this when local background is no longer required. + */ + if (do_local_background != 2) + png_set_swap_alpha(png_ptr); + } + + else + format &= ~PNG_FORMAT_FLAG_AFIRST; + + change &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* If the *output* is 16-bit then we need to check for a byte-swap on this + * architecture. + */ + if (linear) + { + PNG_CONST png_uint_16 le = 0x0001; + + if (*(png_const_bytep)&le) + png_set_swap(png_ptr); + } + + /* If change is not now 0 some transformation is missing - error out. */ + if (change) + png_error(png_ptr, "png_read_image: unsupported transformation"); + } + + PNG_SKIP_CHUNKS(png_ptr); + + /* Update the 'info' structure and make sure the result is as required; first + * make sure to turn on the interlace handling if it will be required + * (because it can't be turned on *after* the call to png_read_update_info!) + * + * TODO: remove the do_local_background fixup below. + */ + if (!do_local_compose && do_local_background != 2) + passes = png_set_interlace_handling(png_ptr); + + png_read_update_info(png_ptr, info_ptr); + + { + png_uint_32 info_format = 0; + + if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_format |= PNG_FORMAT_FLAG_COLOR; + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + { + /* do_local_compose removes this channel below. */ + if (!do_local_compose) + { + /* do_local_background does the same if required. */ + if (do_local_background != 2 || + (format & PNG_FORMAT_FLAG_ALPHA) != 0) + info_format |= PNG_FORMAT_FLAG_ALPHA; + } + } + + else if (do_local_compose) /* internal error */ + png_error(png_ptr, "png_image_read: alpha channel lost"); + + if (info_ptr->bit_depth == 16) + info_format |= PNG_FORMAT_FLAG_LINEAR; + +# ifdef PNG_FORMAT_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + info_format |= PNG_FORMAT_FLAG_BGR; +# endif + +# ifdef PNG_FORMAT_AFIRST_SUPPORTED + if (do_local_background == 2) + { + if (format & PNG_FORMAT_FLAG_AFIRST) + info_format |= PNG_FORMAT_FLAG_AFIRST; + } + + if ((png_ptr->transformations & PNG_SWAP_ALPHA) != 0 || + ((png_ptr->transformations & PNG_ADD_ALPHA) != 0 && + (png_ptr->flags & PNG_FLAG_FILLER_AFTER) == 0)) + { + if (do_local_background == 2) + png_error(png_ptr, "unexpected alpha swap transformation"); + + info_format |= PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* This is actually an internal error. */ + if (info_format != format) + png_error(png_ptr, "png_read_image: invalid transformations"); + } + + /* Now read the rows. If do_local_compose is set then it is necessary to use + * a local row buffer. The output will be GA, RGBA or BGRA and must be + * converted to G, RGB or BGR as appropriate. The 'local_row' member of the + * display acts as a flag. + */ + { + png_voidp first_row = display->buffer; + ptrdiff_t row_bytes = display->row_stride; + + if (linear) + row_bytes *= 2; + + /* The following expression is designed to work correctly whether it gives + * a signed or an unsigned result. + */ + if (row_bytes < 0) + { + char *ptr = png_voidcast(char*, first_row); + ptr += (image->height-1) * (-row_bytes); + first_row = png_voidcast(png_voidp, ptr); + } + + display->first_row = first_row; + display->row_bytes = row_bytes; + } + + if (do_local_compose) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_composite, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else if (do_local_background == 2) + { + int result; + png_voidp row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr)); + + display->local_row = row; + result = png_safe_execute(image, png_image_read_background, display); + display->local_row = NULL; + png_free(png_ptr, row); + + return result; + } + + else + { + png_alloc_size_t row_bytes = display->row_bytes; + + while (--passes >= 0) + { + png_uint_32 y = image->height; + png_bytep row = png_voidcast(png_bytep, display->first_row); + + while (y-- > 0) + { + png_read_row(png_ptr, row, NULL); + row += row_bytes; + } + } + + return 1; + } +} + +int PNGAPI +png_image_finish_read(png_imagep image, png_const_colorp background, + void *buffer, png_int_32 row_stride, void *colormap) +{ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + png_uint_32 check; + + if (row_stride == 0) + row_stride = PNG_IMAGE_ROW_STRIDE(*image); + + if (row_stride < 0) + check = -row_stride; + + else + check = row_stride; + + if (image->opaque != NULL && buffer != NULL && + check >= PNG_IMAGE_ROW_STRIDE(*image)) + { + if ((image->format & PNG_FORMAT_FLAG_COLORMAP) == 0 || + (image->colormap_entries > 0 && colormap != NULL)) + { + int result; + png_image_read_control display; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.background = background; + display.local_row = NULL; + + /* Choose the correct 'end' routine; for the color-map case all the + * setup has already been done. + */ + if (image->format & PNG_FORMAT_FLAG_COLORMAP) + result = + png_safe_execute(image, png_image_read_colormap, &display) && + png_safe_execute(image, png_image_read_colormapped, &display); + + else + result = + png_safe_execute(image, png_image_read_direct, &display); + + png_image_free(image); + return result; + } + + else + return png_image_error(image, + "png_image_finish_read[color-map]: no color-map"); + } + + else + return png_image_error(image, + "png_image_finish_read: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_finish_read: damaged PNG_IMAGE_VERSION"); + + return 0; +} + +#endif /* PNG_SIMPLIFIED_READ_SUPPORTED */ #endif /* PNG_READ_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrio.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrio.c index 2275683..2b9c103 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrio.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrio.c @@ -1,12 +1,15 @@ /* 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 + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * This file provides a location for all input. Users who need * special handling are expected to write a function that has the same * arguments as this and performs a similar function, but that possibly @@ -15,135 +18,84 @@ * libpng use it at run time with png_set_read_fn(...). */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" -#if defined(PNG_READ_SUPPORTED) +#ifdef 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. */ + * reads from a file pointer. Note that this routine sometimes gets called + * with very small lengths, so you should implement some kind of simple + * buffering if you are using unbuffered reads. This should never be asked + * to read more then 64K on a 16 bit machine. + */ void /* PRIVATE */ -png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +png_read_data(png_structrp png_ptr, png_bytep data, png_size_t length) { - png_debug1(4,"reading %d bytes\n", (int)length); + png_debug1(4, "reading %d bytes", (int)length); + if (png_ptr->read_data_fn != NULL) (*(png_ptr->read_data_fn))(png_ptr, data, length); + else png_error(png_ptr, "Call to NULL read function"); } -#if !defined(PNG_NO_STDIO) +#ifdef PNG_STDIO_SUPPORTED /* This is the function that does the actual reading of data. If you are - not reading from a standard C stream, you should create a replacement - read_data function and use it at run time with png_set_read_fn(), rather - than changing the library. */ -#ifndef USE_FAR_KEYWORD -void PNGAPI + * 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. + */ +void PNGCBAPI png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check; - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + /* fread() returns 0 on error, so it is OK to store this in a png_size_t * instead of an int, which is what fread() actually returns. */ -#if defined(_WIN32_WCE) - if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) - check = 0; -#else - check = (png_size_t)fread(data, (png_size_t)1, length, - (png_FILE_p)png_ptr->io_ptr); -#endif + check = fread(data, 1, length, png_voidcast(png_FILE_p, png_ptr->io_ptr)); if (check != length) png_error(png_ptr, "Read Error"); } -#else -/* this is the model-independent version. Since the standard I/O library - can't handle far buffers in the medium and small models, we have to copy - the data. -*/ - -#define NEAR_BUF_SIZE 1024 -#define MIN(a,b) (a <= b ? a : b) - -static void PNGAPI -png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) -{ - int check; - png_byte *n_data; - png_FILE_p io_ptr; - - if(png_ptr == NULL) return; - /* Check if data really is near. If so, use usual code. */ - n_data = (png_byte *)CVT_PTR_NOCHECK(data); - io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); - if ((png_bytep)n_data == data) - { -#if defined(_WIN32_WCE) - if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) - check = 0; -#else - check = fread(n_data, 1, length, io_ptr); -#endif - } - else - { - png_byte buf[NEAR_BUF_SIZE]; - png_size_t read, remaining, err; - check = 0; - remaining = length; - do - { - read = MIN(NEAR_BUF_SIZE, remaining); -#if defined(_WIN32_WCE) - if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) - err = 0; -#else - err = fread(buf, (png_size_t)1, read, io_ptr); -#endif - png_memcpy(data, buf, read); /* copy far buffer to near buffer */ - if(err != read) - break; - else - check += err; - data += read; - remaining -= read; - } - while (remaining != 0); - } - if ((png_uint_32)check != (png_uint_32)length) - png_error(png_ptr, "read Error"); -} -#endif #endif /* This function allows the application to supply a new input function - for libpng if standard C streams aren't being used. - - This function takes as its arguments: - png_ptr - pointer to a png input data structure - io_ptr - pointer to user supplied structure containing info about - the input functions. May be NULL. - read_data_fn - pointer to a new input function that takes as its - arguments a pointer to a png_struct, a pointer to - a location where input data can be stored, and a 32-bit - unsigned int that is the number of bytes to be read. - To exit and output any fatal error messages the new write - function should call png_error(png_ptr, "Error msg"). */ + * for libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * + * png_ptr - pointer to a png input data structure + * + * io_ptr - pointer to user supplied structure containing info about + * the input functions. May be NULL. + * + * read_data_fn - pointer to a new input function that takes as its + * arguments a pointer to a png_struct, a pointer to + * a location where input data can be stored, and a 32-bit + * unsigned int that is the number of bytes to be read. + * To exit and output any fatal error messages the new write + * function should call png_error(png_ptr, "Error msg"). + * May be NULL, in which case libpng's default function will + * be used. + */ void PNGAPI -png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, +png_set_read_fn(png_structrp png_ptr, png_voidp io_ptr, png_rw_ptr read_data_fn) { - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + png_ptr->io_ptr = io_ptr; -#if !defined(PNG_NO_STDIO) +#ifdef PNG_STDIO_SUPPORTED if (read_data_fn != NULL) png_ptr->read_data_fn = read_data_fn; + else png_ptr->read_data_fn = png_default_read_data; #else @@ -155,12 +107,11 @@ png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, { png_ptr->write_data_fn = NULL; png_warning(png_ptr, - "It's an error to set both read_data_fn and write_data_fn in the "); - png_warning(png_ptr, - "same structure. Resetting write_data_fn to NULL."); + "Can't set both read_data_fn and write_data_fn in the" + " same structure"); } -#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#ifdef PNG_WRITE_FLUSH_SUPPORTED png_ptr->output_flush_fn = NULL; #endif } diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrtran.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrtran.c index b02b1d1..034b9c3 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrtran.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrtran.c @@ -1,70 +1,86 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * This file contains functions optionally called by an application * in order to tell libpng how to handle data when reading a PNG. * Transformations that are used in both reading and writing are * in pngtrans.c. */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" -#if defined(PNG_READ_SUPPORTED) +#ifdef 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_set_crc_action(png_structrp png_ptr, int crit_action, int ancil_action) { - png_debug(1, "in png_set_crc_action\n"); + png_debug(1, "in png_set_crc_action"); + + if (png_ptr == NULL) + return; + /* Tell libpng how we react to CRC errors in critical chunks */ - if(png_ptr == NULL) return; switch (crit_action) { - case PNG_CRC_NO_CHANGE: /* leave setting as is */ + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ break; - case PNG_CRC_WARN_USE: /* warn/use data */ + + case PNG_CRC_WARN_USE: /* Warn/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; break; - case PNG_CRC_QUIET_USE: /* quiet/use data */ + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | PNG_FLAG_CRC_CRITICAL_IGNORE; break; - case PNG_CRC_WARN_DISCARD: /* not a valid action for critical data */ - png_warning(png_ptr, "Can't discard critical data on CRC error."); - case PNG_CRC_ERROR_QUIT: /* error/quit */ + + case PNG_CRC_WARN_DISCARD: /* Not a valid action for critical data */ + png_warning(png_ptr, + "Can't discard critical data on CRC error"); + case PNG_CRC_ERROR_QUIT: /* Error/quit */ + case PNG_CRC_DEFAULT: default: png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; break; } + /* Tell libpng how we react to CRC errors in ancillary chunks */ switch (ancil_action) { - case PNG_CRC_NO_CHANGE: /* leave setting as is */ + case PNG_CRC_NO_CHANGE: /* Leave setting as is */ break; - case PNG_CRC_WARN_USE: /* warn/use data */ + + case PNG_CRC_WARN_USE: /* Warn/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; break; - case PNG_CRC_QUIET_USE: /* quiet/use data */ + + case PNG_CRC_QUIET_USE: /* Quiet/use data */ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN; break; - case PNG_CRC_ERROR_QUIT: /* error/quit */ + + case PNG_CRC_ERROR_QUIT: /* Error/quit */ png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; break; - case PNG_CRC_WARN_DISCARD: /* warn/discard data */ + + case PNG_CRC_WARN_DISCARD: /* Warn/discard data */ + case PNG_CRC_DEFAULT: default: png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; @@ -72,88 +88,344 @@ 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, - int need_expand, double background_gamma) +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +/* Is it OK to set a transformation now? Only if png_start_read_image or + * png_read_update_info have not been called. It is not necessary for the IHDR + * to have been read in all cases, the parameter allows for this check too. + */ +static int +png_rtran_ok(png_structrp png_ptr, int need_IHDR) { - png_debug(1, "in png_set_background\n"); - if(png_ptr == NULL) return; + if (png_ptr != NULL) + { + if (png_ptr->flags & PNG_FLAG_ROW_INIT) + png_app_error(png_ptr, + "invalid after png_start_read_image or png_read_update_info"); + + else if (need_IHDR && (png_ptr->mode & PNG_HAVE_IHDR) == 0) + png_app_error(png_ptr, "invalid before the PNG header has been read"); + + else + { + /* Turn on failure to initialize correctly for all transforms. */ + png_ptr->flags |= PNG_FLAG_DETECT_UNINITIALIZED; + + return 1; /* Ok */ + } + } + + return 0; /* no png_error possible! */ +} +#endif + +#ifdef PNG_READ_BACKGROUND_SUPPORTED +/* Handle alpha and tRNS via a background color */ +void PNGFAPI +png_set_background_fixed(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, png_fixed_point background_gamma) +{ + png_debug(1, "in png_set_background_fixed"); + + if (!png_rtran_ok(png_ptr, 0) || background_color == NULL) + return; + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) { png_warning(png_ptr, "Application must supply a known background gamma"); return; } - png_ptr->transformations |= PNG_BACKGROUND; - png_memcpy(&(png_ptr->background), background_color, - png_sizeof(png_color_16)); - png_ptr->background_gamma = (float)background_gamma; + png_ptr->transformations |= PNG_COMPOSE | PNG_STRIP_ALPHA; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + png_ptr->background = *background_color; + png_ptr->background_gamma = background_gamma; png_ptr->background_gamma_type = (png_byte)(background_gamma_code); - png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); + if (need_expand) + png_ptr->transformations |= PNG_BACKGROUND_EXPAND; + else + png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_background(png_structrp png_ptr, + png_const_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_set_background_fixed(png_ptr, background_color, background_gamma_code, + need_expand, png_fixed(png_ptr, background_gamma, "png_set_background")); +} +# endif /* FLOATING_POINT */ +#endif /* READ_BACKGROUND */ + +/* Scale 16-bit depth files to 8-bit depth. If both of these are set then the + * one that pngrtran does first (scale) happens. This is necessary to allow the + * TRANSFORM and API behavior to be somewhat consistent, and it's simpler. + */ +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +void PNGAPI +png_set_scale_16(png_structrp png_ptr) +{ + png_debug(1, "in png_set_scale_16"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_SCALE_16_TO_8; } #endif -#if defined(PNG_READ_16_TO_8_SUPPORTED) -/* strip 16 bit depth files to 8 bit depth */ +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +/* Chop 16-bit depth files to 8-bit depth */ void PNGAPI -png_set_strip_16(png_structp png_ptr) +png_set_strip_16(png_structrp png_ptr) { - png_debug(1, "in png_set_strip_16\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_strip_16"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + png_ptr->transformations |= PNG_16_TO_8; } #endif -#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED void PNGAPI -png_set_strip_alpha(png_structp png_ptr) +png_set_strip_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_strip_alpha\n"); - if(png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; + png_debug(1, "in png_set_strip_alpha"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_STRIP_ALPHA; } #endif -#if defined(PNG_READ_DITHER_SUPPORTED) -/* Dither file to 8 bit. Supply a palette, the current number +#if defined(PNG_READ_ALPHA_MODE_SUPPORTED) || defined(PNG_READ_GAMMA_SUPPORTED) +static png_fixed_point +translate_gamma_flags(png_structrp png_ptr, png_fixed_point output_gamma, + int is_screen) +{ + /* Check for flag values. The main reason for having the old Mac value as a + * flag is that it is pretty near impossible to work out what the correct + * value is from Apple documentation - a working Mac system is needed to + * discover the value! + */ + if (output_gamma == PNG_DEFAULT_sRGB || + output_gamma == PNG_FP_1 / PNG_DEFAULT_sRGB) + { + /* If there is no sRGB support this just sets the gamma to the standard + * sRGB value. (This is a side effect of using this function!) + */ +# ifdef PNG_READ_sRGB_SUPPORTED + png_ptr->flags |= PNG_FLAG_ASSUME_sRGB; +# endif + if (is_screen) + output_gamma = PNG_GAMMA_sRGB; + else + output_gamma = PNG_GAMMA_sRGB_INVERSE; + } + + else if (output_gamma == PNG_GAMMA_MAC_18 || + output_gamma == PNG_FP_1 / PNG_GAMMA_MAC_18) + { + if (is_screen) + output_gamma = PNG_GAMMA_MAC_OLD; + else + output_gamma = PNG_GAMMA_MAC_INVERSE; + } + + return output_gamma; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +static png_fixed_point +convert_gamma_value(png_structrp png_ptr, double output_gamma) +{ + /* The following silently ignores cases where fixed point (times 100,000) + * gamma values are passed to the floating point API. This is safe and it + * means the fixed point constants work just fine with the floating point + * API. The alternative would just lead to undetected errors and spurious + * bug reports. Negative values fail inside the _fixed API unless they + * correspond to the flag values. + */ + if (output_gamma > 0 && output_gamma < 128) + output_gamma *= PNG_FP_1; + + /* This preserves -1 and -2 exactly: */ + output_gamma = floor(output_gamma + .5); + + if (output_gamma > PNG_FP_MAX || output_gamma < PNG_FP_MIN) + png_fixed_error(png_ptr, "gamma value"); + + return (png_fixed_point)output_gamma; +} +# endif +#endif /* READ_ALPHA_MODE || READ_GAMMA */ + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +void PNGFAPI +png_set_alpha_mode_fixed(png_structrp png_ptr, int mode, + png_fixed_point output_gamma) +{ + int compose = 0; + png_fixed_point file_gamma; + + png_debug(1, "in png_set_alpha_mode"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + output_gamma = translate_gamma_flags(png_ptr, output_gamma, 1/*screen*/); + + /* Validate the value to ensure it is in a reasonable range. The value + * is expected to be 1 or greater, but this range test allows for some + * viewing correction values. The intent is to weed out users of this API + * who use the inverse of the gamma value accidentally! Since some of these + * values are reasonable this may have to be changed. + */ + if (output_gamma < 70000 || output_gamma > 300000) + png_error(png_ptr, "output gamma out of expected range"); + + /* The default file gamma is the inverse of the output gamma; the output + * gamma may be changed below so get the file value first: + */ + file_gamma = png_reciprocal(output_gamma); + + /* There are really 8 possibilities here, composed of any combination + * of: + * + * premultiply the color channels + * do not encode non-opaque pixels + * encode the alpha as well as the color channels + * + * The differences disappear if the input/output ('screen') gamma is 1.0, + * because then the encoding is a no-op and there is only the choice of + * premultiplying the color channels or not. + * + * png_set_alpha_mode and png_set_background interact because both use + * png_compose to do the work. Calling both is only useful when + * png_set_alpha_mode is used to set the default mode - PNG_ALPHA_PNG - along + * with a default gamma value. Otherwise PNG_COMPOSE must not be set. + */ + switch (mode) + { + case PNG_ALPHA_PNG: /* default: png standard */ + /* No compose, but it may be set by png_set_background! */ + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + break; + + case PNG_ALPHA_ASSOCIATED: /* color channels premultiplied */ + compose = 1; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + /* The output is linear: */ + output_gamma = PNG_FP_1; + break; + + case PNG_ALPHA_OPTIMIZED: /* associated, non-opaque pixels linear */ + compose = 1; + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags |= PNG_FLAG_OPTIMIZE_ALPHA; + /* output_gamma records the encoding of opaque pixels! */ + break; + + case PNG_ALPHA_BROKEN: /* associated, non-linear, alpha encoded */ + compose = 1; + png_ptr->transformations |= PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + break; + + default: + png_error(png_ptr, "invalid alpha mode"); + } + + /* Only set the default gamma if the file gamma has not been set (this has + * the side effect that the gamma in a second call to png_set_alpha_mode will + * be ignored.) + */ + if (png_ptr->colorspace.gamma == 0) + { + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + } + + /* But always set the output gamma: */ + png_ptr->screen_gamma = output_gamma; + + /* Finally, if pre-multiplying, set the background fields to achieve the + * desired result. + */ + if (compose) + { + /* And obtain alpha pre-multiplication by composing on black: */ + memset(&png_ptr->background, 0, (sizeof png_ptr->background)); + png_ptr->background_gamma = png_ptr->colorspace.gamma; /* just in case */ + png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_FILE; + png_ptr->transformations &= ~PNG_BACKGROUND_EXPAND; + + if (png_ptr->transformations & PNG_COMPOSE) + png_error(png_ptr, + "conflicting calls to set alpha mode and background"); + + png_ptr->transformations |= PNG_COMPOSE; + } +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_alpha_mode(png_structrp png_ptr, int mode, double output_gamma) +{ + png_set_alpha_mode_fixed(png_ptr, mode, convert_gamma_value(png_ptr, + output_gamma)); +} +# endif +#endif + +#ifdef PNG_READ_QUANTIZE_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 + * modified to fit in the maximum number. "full_quantize" indicates + * whether we need a quantizing cube set up for RGB images, or if we * simply are reducing the number of colors in a paletted image. */ typedef struct png_dsort_struct { - struct png_dsort_struct FAR * next; + struct png_dsort_struct * next; png_byte left; png_byte right; } png_dsort; -typedef png_dsort FAR * png_dsortp; -typedef png_dsort FAR * FAR * png_dsortpp; +typedef png_dsort * png_dsortp; +typedef png_dsort * * png_dsortpp; void PNGAPI -png_set_dither(png_structp png_ptr, png_colorp palette, - int num_palette, int maximum_colors, png_uint_16p histogram, - int full_dither) +png_set_quantize(png_structrp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_const_uint_16p histogram, + int full_quantize) { - png_debug(1, "in png_set_dither\n"); - if(png_ptr == NULL) return; - png_ptr->transformations |= PNG_DITHER; + png_debug(1, "in png_set_quantize"); - if (!full_dither) + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= PNG_QUANTIZE; + + if (!full_quantize) { int i; - png_ptr->dither_index = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * png_sizeof (png_byte))); + png_ptr->quantize_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * (sizeof (png_byte)))); for (i = 0; i < num_palette; i++) - png_ptr->dither_index[i] = (png_byte)i; + png_ptr->quantize_index[i] = (png_byte)i; } if (num_palette > maximum_colors) @@ -161,61 +433,66 @@ 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. */ + * 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 an array to sort colors */ + png_ptr->quantize_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * (sizeof (png_byte)))); - /* initialize the dither_sort array */ + /* Initialize the quantize_sort array */ for (i = 0; i < num_palette; i++) - png_ptr->dither_sort[i] = (png_byte)i; + png_ptr->quantize_sort[i] = (png_byte)i; /* Find the least used palette entries by starting a - bubble sort, and running it until we have sorted - out enough colors. Note that we don't care about - sorting all the colors, just finding which are - least used. */ + * bubble sort, and running it until we have sorted + * out enough colors. Note that we don't care about + * sorting all the colors, just finding which are + * least used. + */ for (i = num_palette - 1; i >= maximum_colors; i--) { - int done; /* to stop early if the list is pre-sorted */ + int done; /* To stop early if the list is pre-sorted */ int j; done = 1; for (j = 0; j < i; j++) { - if (histogram[png_ptr->dither_sort[j]] - < histogram[png_ptr->dither_sort[j + 1]]) + if (histogram[png_ptr->quantize_sort[j]] + < histogram[png_ptr->quantize_sort[j + 1]]) { png_byte t; - t = png_ptr->dither_sort[j]; - png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1]; - png_ptr->dither_sort[j + 1] = t; + t = png_ptr->quantize_sort[j]; + png_ptr->quantize_sort[j] = png_ptr->quantize_sort[j + 1]; + png_ptr->quantize_sort[j + 1] = t; done = 0; } } + if (done) break; } - /* swap the palette around, and set up a table, if necessary */ - if (full_dither) + /* Swap the palette around, and set up a table, if necessary */ + if (full_quantize) { int j = num_palette; - /* put all the useful colors within the max, but don't - move the others */ + /* 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) + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) { do j--; - while ((int)png_ptr->dither_sort[j] >= maximum_colors); + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); + palette[i] = palette[j]; } } @@ -224,37 +501,38 @@ 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 */ + /* 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) + /* Only move the colors we need to */ + if ((int)png_ptr->quantize_sort[i] >= maximum_colors) { png_color tmp_color; do j--; - while ((int)png_ptr->dither_sort[j] >= maximum_colors); + while ((int)png_ptr->quantize_sort[j] >= maximum_colors); tmp_color = palette[j]; palette[j] = palette[i]; palette[i] = tmp_color; - /* indicate where the color went */ - png_ptr->dither_index[j] = (png_byte)i; - png_ptr->dither_index[i] = (png_byte)j; + /* Indicate where the color went */ + png_ptr->quantize_index[j] = (png_byte)i; + png_ptr->quantize_index[i] = (png_byte)j; } } - /* find closest color for those colors we are not using */ + /* 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) + if ((int)png_ptr->quantize_index[i] >= maximum_colors) { int min_d, k, min_k, d_index; - /* find the closest color to one we threw out */ - d_index = png_ptr->dither_index[i]; + /* Find the closest color to one we threw out */ + d_index = png_ptr->quantize_index[i]; min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); for (k = 1, min_k = 0; k < maximum_colors; k++) { @@ -268,61 +546,58 @@ 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; + /* Point to closest color */ + png_ptr->quantize_index[i] = (png_byte)min_k; } } } - png_free(png_ptr, png_ptr->dither_sort); - png_ptr->dither_sort=NULL; + png_free(png_ptr, png_ptr->quantize_sort); + png_ptr->quantize_sort = NULL; } else { /* This is much harder to do simply (and quickly). Perhaps - we need to go through a median cut routine, but those - don't always behave themselves with only a few colors - as input. So we will just find the closest two colors, - and throw out one of them (chosen somewhat randomly). - [We don't understand this at all, so if someone wants to - work on improving it, be our guest - AED, GRP] - */ + * we need to go through a median cut routine, but those + * don't always behave themselves with only a few colors + * as input. So we will just find the closest two colors, + * and throw out one of them (chosen somewhat randomly). + * [We don't understand this at all, so if someone wants to + * work on improving it, be our guest - AED, GRP] + */ int i; int max_d; int num_new_palette; png_dsortp t; png_dsortpp hash; - t=NULL; + t = NULL; - /* initialize palette index arrays */ + /* 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_uint_32)(num_palette * (sizeof (png_byte)))); png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(num_palette * png_sizeof (png_byte))); + (png_uint_32)(num_palette * (sizeof (png_byte)))); - /* initialize the sort array */ + /* Initialize the sort array */ for (i = 0; i < num_palette; i++) { png_ptr->index_to_palette[i] = (png_byte)i; png_ptr->palette_to_index[i] = (png_byte)i; } - hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 * - png_sizeof (png_dsortp))); - for (i = 0; i < 769; i++) - hash[i] = NULL; -/* png_memset(hash, 0, 769 * png_sizeof (png_dsortp)); */ + hash = (png_dsortpp)png_calloc(png_ptr, (png_uint_32)(769 * + (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. - */ + /* 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) @@ -341,9 +616,11 @@ png_set_dither(png_structp png_ptr, png_colorp palette, { t = (png_dsortp)png_malloc_warn(png_ptr, - (png_uint_32)(png_sizeof(png_dsort))); + (png_uint_32)(sizeof (png_dsort))); + if (t == NULL) break; + t->next = hash[d]; t->left = (png_byte)i; t->right = (png_byte)j; @@ -364,9 +641,9 @@ png_set_dither(png_structp png_ptr, png_colorp palette, for (p = hash[i]; p; p = p->next) { if ((int)png_ptr->index_to_palette[p->left] - < num_new_palette && - (int)png_ptr->index_to_palette[p->right] - < num_new_palette) + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) { int j, next_j; @@ -383,31 +660,36 @@ png_set_dither(png_structp png_ptr, png_colorp palette, num_new_palette--; palette[png_ptr->index_to_palette[j]] - = palette[num_new_palette]; - if (!full_dither) + = palette[num_new_palette]; + if (!full_quantize) { int k; for (k = 0; k < num_palette; k++) { - if (png_ptr->dither_index[k] == - png_ptr->index_to_palette[j]) - png_ptr->dither_index[k] = - png_ptr->index_to_palette[next_j]; - if ((int)png_ptr->dither_index[k] == - num_new_palette) - png_ptr->dither_index[k] = - png_ptr->index_to_palette[j]; + if (png_ptr->quantize_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[next_j]; + + if ((int)png_ptr->quantize_index[k] == + num_new_palette) + png_ptr->quantize_index[k] = + png_ptr->index_to_palette[j]; } } png_ptr->index_to_palette[png_ptr->palette_to_index - [num_new_palette]] = png_ptr->index_to_palette[j]; - png_ptr->palette_to_index[png_ptr->index_to_palette[j]] - = png_ptr->palette_to_index[num_new_palette]; + [num_new_palette]] = png_ptr->index_to_palette[j]; - png_ptr->index_to_palette[j] = (png_byte)num_new_palette; - png_ptr->palette_to_index[num_new_palette] = (png_byte)j; + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = + (png_byte)num_new_palette; + + png_ptr->palette_to_index[num_new_palette] = + (png_byte)j; } if (num_new_palette <= maximum_colors) break; @@ -436,8 +718,8 @@ png_set_dither(png_structp png_ptr, png_colorp palette, png_free(png_ptr, hash); png_free(png_ptr, png_ptr->palette_to_index); png_free(png_ptr, png_ptr->index_to_palette); - png_ptr->palette_to_index=NULL; - png_ptr->index_to_palette=NULL; + png_ptr->palette_to_index = NULL; + png_ptr->index_to_palette = NULL; } num_palette = maximum_colors; } @@ -447,40 +729,38 @@ png_set_dither(png_structp png_ptr, png_colorp palette, } png_ptr->num_palette = (png_uint_16)num_palette; - if (full_dither) + if (full_quantize) { int i; png_bytep distance; - int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS + - PNG_DITHER_BLUE_BITS; - int num_red = (1 << PNG_DITHER_RED_BITS); - int num_green = (1 << PNG_DITHER_GREEN_BITS); - int num_blue = (1 << PNG_DITHER_BLUE_BITS); + int total_bits = PNG_QUANTIZE_RED_BITS + PNG_QUANTIZE_GREEN_BITS + + PNG_QUANTIZE_BLUE_BITS; + int num_red = (1 << PNG_QUANTIZE_RED_BITS); + int num_green = (1 << PNG_QUANTIZE_GREEN_BITS); + int num_blue = (1 << PNG_QUANTIZE_BLUE_BITS); png_size_t num_entries = ((png_size_t)1 << total_bits); - png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr, - (png_uint_32)(num_entries * png_sizeof (png_byte))); - - png_memset(png_ptr->palette_lookup, 0, num_entries * - png_sizeof (png_byte)); + png_ptr->palette_lookup = (png_bytep)png_calloc(png_ptr, + (png_uint_32)(num_entries * (sizeof (png_byte)))); distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * - png_sizeof(png_byte))); + (sizeof (png_byte)))); - png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); + memset(distance, 0xff, num_entries * (sizeof (png_byte))); for (i = 0; i < num_palette; i++) { int ir, ig, ib; - int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS)); - int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS)); - int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS)); + int r = (palette[i].red >> (8 - PNG_QUANTIZE_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_QUANTIZE_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_QUANTIZE_BLUE_BITS)); for (ir = 0; ir < num_red; ir++) { /* int dr = abs(ir - r); */ int dr = ((ir > r) ? ir - r : r - ir); - int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS)); + int index_r = (ir << (PNG_QUANTIZE_BLUE_BITS + + PNG_QUANTIZE_GREEN_BITS)); for (ig = 0; ig < num_green; ig++) { @@ -488,7 +768,7 @@ png_set_dither(png_structp png_ptr, png_colorp palette, int dg = ((ig > g) ? ig - g : g - ig); int dt = dr + dg; int dm = ((dr > dg) ? dr : dg); - int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS); + int index_g = index_r | (ig << PNG_QUANTIZE_BLUE_BITS); for (ib = 0; ib < num_blue; ib++) { @@ -511,46 +791,72 @@ png_set_dither(png_structp png_ptr, png_colorp palette, png_free(png_ptr, distance); } } -#endif +#endif /* PNG_READ_QUANTIZE_SUPPORTED */ -#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) -/* Transform the image from the file_gamma to the screen_gamma. We - * only do transformations on images where the file_gamma and screen_gamma - * are not close reciprocals, otherwise it slows things down slightly, and - * also needlessly introduces small errors. - * - * We will turn off gamma transformation later if no semitransparent entries - * are present in the tRNS array for palette images. We can't do it here - * because we don't necessarily have the tRNS chunk yet. - */ -void PNGAPI -png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) +#ifdef PNG_READ_GAMMA_SUPPORTED +void PNGFAPI +png_set_gamma_fixed(png_structrp png_ptr, png_fixed_point scrn_gamma, + png_fixed_point file_gamma) { - png_debug(1, "in png_set_gamma\n"); - if(png_ptr == NULL) return; - if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || - (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || - (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) - png_ptr->transformations |= PNG_GAMMA; - png_ptr->gamma = (float)file_gamma; - png_ptr->screen_gamma = (float)scrn_gamma; -} -#endif + png_debug(1, "in png_set_gamma_fixed"); -#if defined(PNG_READ_EXPAND_SUPPORTED) + if (!png_rtran_ok(png_ptr, 0)) + return; + + /* New in libpng-1.5.4 - reserve particular negative values as flags. */ + scrn_gamma = translate_gamma_flags(png_ptr, scrn_gamma, 1/*screen*/); + file_gamma = translate_gamma_flags(png_ptr, file_gamma, 0/*file*/); + + /* Checking the gamma values for being >0 was added in 1.5.4 along with the + * premultiplied alpha support; this actually hides an undocumented feature + * of the previous implementation which allowed gamma processing to be + * disabled in background handling. There is no evidence (so far) that this + * was being used; however, png_set_background itself accepted and must still + * accept '0' for the gamma value it takes, because it isn't always used. + * + * Since this is an API change (albeit a very minor one that removes an + * undocumented API feature) the following checks were only enabled in + * libpng-1.6.0. + */ + if (file_gamma <= 0) + png_error(png_ptr, "invalid file gamma in png_set_gamma"); + + if (scrn_gamma <= 0) + png_error(png_ptr, "invalid screen gamma in png_set_gamma"); + + /* Set the gamma values unconditionally - this overrides the value in the PNG + * file if a gAMA chunk was present. png_set_alpha_mode provides a + * different, easier, way to default the file gamma. + */ + png_ptr->colorspace.gamma = file_gamma; + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + png_ptr->screen_gamma = scrn_gamma; +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gamma(png_structrp png_ptr, double scrn_gamma, double file_gamma) +{ + png_set_gamma_fixed(png_ptr, convert_gamma_value(png_ptr, scrn_gamma), + convert_gamma_value(png_ptr, file_gamma)); +} +# endif /* FLOATING_POINT_SUPPORTED */ +#endif /* READ_GAMMA */ + +#ifdef PNG_READ_EXPAND_SUPPORTED /* Expand paletted images to RGB, expand grayscale images of * less than 8-bit depth to 8-bit depth, and expand tRNS chunks * to alpha channels. */ void PNGAPI -png_set_expand(png_structp png_ptr) +png_set_expand(png_structrp png_ptr) { - png_debug(1, "in png_set_expand\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_expand"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -#ifdef PNG_WARN_UNINITIALIZED_ROW - png_ptr->flags &= ~PNG_FLAG_ROW_INIT; -#endif } /* GRR 19990627: the following three functions currently are identical @@ -566,318 +872,725 @@ png_set_expand(png_structp png_ptr) * 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. + * GRP 20060307: In libpng-1.2.9, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha + * and its name was changed to png_set_expand_gray_1_2_4_to_8(). */ /* Expand paletted images to RGB. */ void PNGAPI -png_set_palette_to_rgb(png_structp png_ptr) +png_set_palette_to_rgb(png_structrp png_ptr) { - png_debug(1, "in png_set_palette_to_rgb\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_palette_to_rgb"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -#ifdef PNG_WARN_UNINITIALIZED_ROW - png_ptr->flags &= !(PNG_FLAG_ROW_INIT); - png_ptr->flags &= ~PNG_FLAG_ROW_INIT; -#endif } -#if !defined(PNG_1_0_X) /* Expand grayscale images of less than 8-bit depth to 8 bits. */ void PNGAPI -png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) +png_set_expand_gray_1_2_4_to_8(png_structrp png_ptr) { - png_debug(1, "in png_set_expand_gray_1_2_4_to_8\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_expand_gray_1_2_4_to_8"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + png_ptr->transformations |= PNG_EXPAND; -#ifdef PNG_WARN_UNINITIALIZED_ROW - png_ptr->flags &= ~PNG_FLAG_ROW_INIT; -#endif } -#endif - -#if defined(PNG_1_0_X) || defined(PNG_1_2_X) -/* Expand grayscale images of less than 8-bit depth to 8 bits. */ -/* Deprecated as of libpng-1.2.9 */ -void PNGAPI -png_set_gray_1_2_4_to_8(png_structp png_ptr) -{ - png_debug(1, "in png_set_gray_1_2_4_to_8\n"); - if(png_ptr == NULL) return; - png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -} -#endif - /* Expand tRNS chunks to alpha channels. */ void PNGAPI -png_set_tRNS_to_alpha(png_structp png_ptr) +png_set_tRNS_to_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_tRNS_to_alpha\n"); + png_debug(1, "in png_set_tRNS_to_alpha"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); -#ifdef PNG_WARN_UNINITIALIZED_ROW - png_ptr->flags &= ~PNG_FLAG_ROW_INIT; -#endif } #endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* Expand to 16-bit channels, expand the tRNS chunk too (because otherwise + * it may not work correctly.) + */ void PNGAPI -png_set_gray_to_rgb(png_structp png_ptr) +png_set_expand_16(png_structrp png_ptr) { - png_debug(1, "in png_set_gray_to_rgb\n"); - png_ptr->transformations |= PNG_GRAY_TO_RGB; -#ifdef PNG_WARN_UNINITIALIZED_ROW - png_ptr->flags &= ~PNG_FLAG_ROW_INIT; -#endif + png_debug(1, "in png_set_expand_16"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + png_ptr->transformations |= (PNG_EXPAND_16 | PNG_EXPAND | PNG_EXPAND_tRNS); } #endif -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) -#if defined(PNG_FLOATING_POINT_SUPPORTED) +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +void PNGAPI +png_set_gray_to_rgb(png_structrp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + + /* Because rgb must be 8 bits or more: */ + png_set_expand_gray_1_2_4_to_8(png_ptr); + png_ptr->transformations |= PNG_GRAY_TO_RGB; +} +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +void PNGFAPI +png_set_rgb_to_gray_fixed(png_structrp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray"); + + /* Need the IHDR here because of the check on color_type below. */ + /* TODO: fix this */ + if (!png_rtran_ok(png_ptr, 1)) + return; + + switch(error_action) + { + case PNG_ERROR_ACTION_NONE: + png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + + case PNG_ERROR_ACTION_WARN: + png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + + case PNG_ERROR_ACTION_ERROR: + png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + break; + + default: + png_error(png_ptr, "invalid error action to rgb_to_gray"); + break; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#ifdef PNG_READ_EXPAND_SUPPORTED + png_ptr->transformations |= PNG_EXPAND; +#else + { + /* Make this an error in 1.6 because otherwise the application may assume + * that it just worked and get a memory overwrite. + */ + png_error(png_ptr, + "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED"); + + /* png_ptr->transformations &= ~PNG_RGB_TO_GRAY; */ + } +#endif + { + if (red >= 0 && green >= 0 && red + green <= PNG_FP_1) + { + png_uint_16 red_int, green_int; + + /* NOTE: this calculation does not round, but this behavior is retained + * for consistency, the inaccuracy is very small. The code here always + * overwrites the coefficients, regardless of whether they have been + * defaulted or set already. + */ + red_int = (png_uint_16)(((png_uint_32)red*32768)/100000); + green_int = (png_uint_16)(((png_uint_32)green*32768)/100000); + + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_coefficients_set = 1; + } + + else + { + if (red >= 0 && green >= 0) + png_app_warning(png_ptr, + "ignoring out of range rgb_to_gray coefficients"); + + /* Use the defaults, from the cHRM chunk if set, else the historical + * values which are close to the sRGB/HDTV/ITU-Rec 709 values. See + * png_do_rgb_to_gray for more discussion of the values. In this case + * the coefficients are not marked as 'set' and are not overwritten if + * something has already provided a default. + */ + if (png_ptr->rgb_to_gray_red_coeff == 0 && + png_ptr->rgb_to_gray_green_coeff == 0) + { + png_ptr->rgb_to_gray_red_coeff = 6968; + png_ptr->rgb_to_gray_green_coeff = 23434; + /* png_ptr->rgb_to_gray_blue_coeff = 2366; */ + } + } + } +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED /* Convert a RGB image to a grayscale of the same width. This allows us, * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. */ void PNGAPI -png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, +png_set_rgb_to_gray(png_structrp png_ptr, int error_action, double red, double green) { - int red_fixed = (int)((float)red*100000.0 + 0.5); - int green_fixed = (int)((float)green*100000.0 + 0.5); - if(png_ptr == NULL) return; - png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); + png_set_rgb_to_gray_fixed(png_ptr, error_action, + png_fixed(png_ptr, red, "rgb to gray red coefficient"), + png_fixed(png_ptr, green, "rgb to gray green coefficient")); } -#endif +#endif /* FLOATING POINT */ -void PNGAPI -png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, - png_fixed_point red, png_fixed_point green) -{ - png_debug(1, "in png_set_rgb_to_gray\n"); - if(png_ptr == NULL) return; - switch(error_action) - { - case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; - break; - case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; - break; - case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; - } - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) -#if defined(PNG_READ_EXPAND_SUPPORTED) - png_ptr->transformations |= PNG_EXPAND; -#else - { - png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED."); - png_ptr->transformations &= ~PNG_RGB_TO_GRAY; - } -#endif - { - png_uint_16 red_int, green_int; - if(red < 0 || green < 0) - { - red_int = 6968; /* .212671 * 32768 + .5 */ - green_int = 23434; /* .715160 * 32768 + .5 */ - } - else if(red + green < 100000L) - { - red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); - green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); - } - else - { - png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); - red_int = 6968; - green_int = 23434; - } - png_ptr->rgb_to_gray_red_coeff = red_int; - png_ptr->rgb_to_gray_green_coeff = green_int; - png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(32768-red_int-green_int); - } -} -#endif +#endif /* RGB_TO_GRAY */ #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) void PNGAPI -png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr - read_user_transform_fn) +png_set_read_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + read_user_transform_fn) { - png_debug(1, "in png_set_read_user_transform_fn\n"); - if(png_ptr == NULL) return; -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_debug(1, "in png_set_read_user_transform_fn"); + + if (!png_rtran_ok(png_ptr, 0)) + return; + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED png_ptr->transformations |= PNG_USER_TRANSFORM; png_ptr->read_user_transform_fn = read_user_transform_fn; #endif -#ifdef PNG_LEGACY_SUPPORTED - if(read_user_transform_fn) - png_warning(png_ptr, - "This version of libpng does not support user transforms"); +} #endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +#ifdef PNG_READ_GAMMA_SUPPORTED +/* In the case of gamma transformations 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. + */ +static int /* PRIVATE */ +png_gamma_threshold(png_fixed_point screen_gamma, png_fixed_point file_gamma) +{ + /* PNG_GAMMA_THRESHOLD is the threshold for performing gamma + * correction as a difference of the overall transform from 1.0 + * + * We want to compare the threshold with s*f - 1, if we get + * overflow here it is because of wacky gamma values so we + * turn on processing anyway. + */ + png_fixed_point gtest; + return !png_muldiv(>est, screen_gamma, file_gamma, PNG_FP_1) || + png_gamma_significant(gtest); } #endif /* Initialize everything needed for the read. This includes modifying * the palette. */ -void /* PRIVATE */ -png_init_read_transformations(png_structp png_ptr) + +/*For the moment 'png_init_palette_transformations' and + * 'png_init_rgb_transformations' only do some flag canceling optimizations. + * The intent is that these two routines should have palette or rgb operations + * extracted from 'png_init_read_transformations'. + */ +static void /* PRIVATE */ +png_init_palette_transformations(png_structrp png_ptr) { - png_debug(1, "in png_init_read_transformations\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if(png_ptr != NULL) -#endif - { -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \ - || defined(PNG_READ_GAMMA_SUPPORTED) - int color_type = png_ptr->color_type; -#endif + /* Called to handle the (input) palette case. In png_do_read_transformations + * the first step is to expand the palette if requested, so this code must + * take care to only make changes that are invariant with respect to the + * palette expansion, or only do them if there is no expansion. + * + * STRIP_ALPHA has already been handled in the caller (by setting num_trans + * to 0.) + */ + int input_has_alpha = 0; + int input_has_transparency = 0; + + if (png_ptr->num_trans > 0) + { + int i; + + /* Ignore if all the entries are opaque (unlikely!) */ + for (i=0; inum_trans; ++i) + if (png_ptr->trans_alpha[i] == 255) + continue; + else if (png_ptr->trans_alpha[i] == 0) + input_has_transparency = 1; + else + input_has_alpha = 1; + } + + /* If no alpha we can optimize. */ + if (!input_has_alpha) + { + /* Any alpha means background and associative alpha processing is + * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA + * and ENCODE_ALPHA are irrelevant. + */ + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + if (!input_has_transparency) + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + } #if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* png_set_background handling - deals with the complexity of whether the + * background color is in the file format or the screen format in the case + * where an 'expand' will happen. + */ -#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 + /* The following code cannot be entered in the alpha pre-multiplication case + * because PNG_BACKGROUND_EXPAND is cancelled below. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + /* Invert the alpha channel (in tRNS) unless the pixels are + * going to be expanded, in which case leave it for later + */ + int i, istop = png_ptr->num_trans; + + for (i=0; itrans_alpha[i] = (png_byte)(255 - + png_ptr->trans_alpha[i]); + } + } +#endif /* PNG_READ_INVERT_ALPHA_SUPPORTED */ + } + } /* background expand and (therefore) no alpha association. */ +#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ +} + +static void /* PRIVATE */ +png_init_rgb_transformations(png_structrp png_ptr) +{ + /* Added to libpng-1.5.4: check the color type to determine whether there + * is any alpha or transparency in the image and simply cancel the + * background and alpha mode stuff if there isn't. + */ + int input_has_alpha = (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) != 0; + int input_has_transparency = png_ptr->num_trans > 0; + + /* If no alpha we can optimize. */ + if (!input_has_alpha) + { + /* Any alpha means background and associative alpha processing is + * required, however if the alpha is 0 or 1 throughout OPTIIMIZE_ALPHA + * and ENCODE_ALPHA are irrelevant. + */ +# ifdef PNG_READ_ALPHA_MODE_SUPPORTED + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; +# endif + + if (!input_has_transparency) + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_BACKGROUND_EXPAND); + } + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + /* png_set_background handling - deals with the complexity of whether the + * background color is in the file format or the screen format in the case + * where an 'expand' will happen. + */ + + /* The following code cannot be entered in the alpha pre-multiplication case + * because PNG_BACKGROUND_EXPAND is cancelled below. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND) && + !(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) + /* i.e., GRAY or GRAY_ALPHA */ + { + { + /* Expand background and tRNS chunks */ + int gray = png_ptr->background.gray; + int trans_gray = png_ptr->trans_color.gray; + + switch (png_ptr->bit_depth) + { + case 1: + gray *= 0xff; + trans_gray *= 0xff; + break; + + case 2: + gray *= 0x55; + trans_gray *= 0x55; + break; + + case 4: + gray *= 0x11; + trans_gray *= 0x11; + break; + + default: + + case 8: + /* FALL THROUGH (Already 8 bits) */ + + case 16: + /* Already a full 16 bits */ + break; + } + + png_ptr->background.red = png_ptr->background.green = + png_ptr->background.blue = (png_uint_16)gray; + + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_color.red = png_ptr->trans_color.green = + png_ptr->trans_color.blue = (png_uint_16)trans_gray; + } + } + } /* background expand and (therefore) no alpha association. */ +#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ +} + +void /* PRIVATE */ +png_init_read_transformations(png_structrp png_ptr) +{ + png_debug(1, "in png_init_read_transformations"); + + /* This internal function is called from png_read_start_row in pngrutil.c + * and it is called before the 'rowbytes' calculation is done, so the code + * in here can change or update the transformations flags. + * + * First do updates that do not depend on the details of the PNG image data + * being processed. + */ + +#ifdef PNG_READ_GAMMA_SUPPORTED + /* Prior to 1.5.4 these tests were performed from png_set_gamma, 1.5.4 adds + * png_set_alpha_mode and this is another source for a default file gamma so + * the test needs to be performed later - here. In addition prior to 1.5.4 + * the tests were repeated for the PALETTE color type here - this is no + * longer necessary (and doesn't seem to have been necessary before.) + */ + { + /* The following temporary indicates if overall gamma correction is + * required. + */ + int gamma_correction = 0; + + if (png_ptr->colorspace.gamma != 0) /* has been set */ + { + if (png_ptr->screen_gamma != 0) /* screen set too */ + gamma_correction = png_gamma_threshold(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); + + else + /* Assume the output matches the input; a long time default behavior + * of libpng, although the standard has nothing to say about this. + */ + png_ptr->screen_gamma = png_reciprocal(png_ptr->colorspace.gamma); + } + + else if (png_ptr->screen_gamma != 0) + /* The converse - assume the file matches the screen, note that this + * perhaps undesireable default can (from 1.5.4) be changed by calling + * png_set_alpha_mode (even if the alpha handling mode isn't required + * or isn't changed from the default.) + */ + png_ptr->colorspace.gamma = png_reciprocal(png_ptr->screen_gamma); + + else /* neither are set */ + /* Just in case the following prevents any processing - file and screen + * are both assumed to be linear and there is no way to introduce a + * third gamma value other than png_set_background with 'UNIQUE', and, + * prior to 1.5.4 + */ + png_ptr->screen_gamma = png_ptr->colorspace.gamma = PNG_FP_1; + + /* We have a gamma value now. */ + png_ptr->colorspace.flags |= PNG_COLORSPACE_HAVE_GAMMA; + + /* Now turn the gamma transformation on or off as appropriate. Notice + * that PNG_GAMMA just refers to the file->screen correction. Alpha + * composition may independently cause gamma correction because it needs + * linear data (e.g. if the file has a gAMA chunk but the screen gamma + * hasn't been specified.) In any case this flag may get turned off in + * the code immediately below if the transform can be handled outside the + * row loop. + */ + if (gamma_correction) + png_ptr->transformations |= PNG_GAMMA; + + else + png_ptr->transformations &= ~PNG_GAMMA; + } +#endif + + /* Certain transformations have the effect of preventing other + * transformations that happen afterward in png_do_read_transformations, + * resolve the interdependencies here. From the code of + * png_do_read_transformations the order is: + * + * 1) PNG_EXPAND (including PNG_EXPAND_tRNS) + * 2) PNG_STRIP_ALPHA (if no compose) + * 3) PNG_RGB_TO_GRAY + * 4) PNG_GRAY_TO_RGB iff !PNG_BACKGROUND_IS_GRAY + * 5) PNG_COMPOSE + * 6) PNG_GAMMA + * 7) PNG_STRIP_ALPHA (if compose) + * 8) PNG_ENCODE_ALPHA + * 9) PNG_SCALE_16_TO_8 + * 10) PNG_16_TO_8 + * 11) PNG_QUANTIZE (converts to palette) + * 12) PNG_EXPAND_16 + * 13) PNG_GRAY_TO_RGB iff PNG_BACKGROUND_IS_GRAY + * 14) PNG_INVERT_MONO + * 15) PNG_SHIFT + * 16) PNG_PACK + * 17) PNG_BGR + * 18) PNG_PACKSWAP + * 19) PNG_FILLER (includes PNG_ADD_ALPHA) + * 20) PNG_INVERT_ALPHA + * 21) PNG_SWAP_ALPHA + * 22) PNG_SWAP_BYTES + * 23) PNG_USER_TRANSFORM [must be last] + */ +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) && + !(png_ptr->transformations & PNG_COMPOSE)) + { + /* Stripping the alpha channel happens immediately after the 'expand' + * transformations, before all other transformation, so it cancels out + * the alpha handling. It has the side effect negating the effect of + * PNG_EXPAND_tRNS too: + */ + png_ptr->transformations &= ~(PNG_BACKGROUND_EXPAND | PNG_ENCODE_ALPHA | + PNG_EXPAND_tRNS); + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + + /* Kill the tRNS chunk itself too. Prior to 1.5.4 this did not happen + * so transparency information would remain just so long as it wasn't + * expanded. This produces unexpected API changes if the set of things + * that do PNG_EXPAND_tRNS changes (perfectly possible given the + * documentation - which says ask for what you want, accept what you + * get.) This makes the behavior consistent from 1.5.4: + */ + png_ptr->num_trans = 0; + } +#endif /* STRIP_ALPHA supported, no COMPOSE */ + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED + /* If the screen gamma is about 1.0 then the OPTIMIZE_ALPHA and ENCODE_ALPHA + * settings will have no effect. + */ + if (!png_gamma_significant(png_ptr->screen_gamma)) + { + png_ptr->transformations &= ~PNG_ENCODE_ALPHA; + png_ptr->flags &= ~PNG_FLAG_OPTIMIZE_ALPHA; + } +#endif + +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Make sure the coefficients for the rgb to gray conversion are set + * appropriately. + */ + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + png_colorspace_set_rgb_coefficients(png_ptr); +#endif + +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_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. + * + * TODO: this code needs to be revised to avoid the complexity and + * interdependencies. The color type of the background should be recorded in + * png_set_background, along with the bit depth, then the code has a record + * of exactly what color space the background is currently in. */ - if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && - !(color_type & PNG_COLOR_MASK_COLOR)) + if (png_ptr->transformations & PNG_BACKGROUND_EXPAND) { - png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; - } else if ((png_ptr->transformations & PNG_BACKGROUND) && - !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && - (png_ptr->transformations & PNG_GRAY_TO_RGB) && - png_ptr->background.red == png_ptr->background.green && - png_ptr->background.red == png_ptr->background.blue) - { - png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; - png_ptr->background.gray = png_ptr->background.red; + /* PNG_BACKGROUND_EXPAND: the background is in the file color space, so if + * the file was grayscale the background value is gray. + */ + if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; } -#endif - if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && - (png_ptr->transformations & PNG_EXPAND)) + else if (png_ptr->transformations & PNG_COMPOSE) { - if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + /* PNG_COMPOSE: png_set_background was called with need_expand false, + * so the color is in the color space of the output or png_set_alpha_mode + * was called and the color is black. Ignore RGB_TO_GRAY because that + * happens before GRAY_TO_RGB. + */ + if (png_ptr->transformations & PNG_GRAY_TO_RGB) { - /* expand background and tRNS chunks */ - switch (png_ptr->bit_depth) + if (png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) { - case 1: - png_ptr->background.gray *= (png_uint_16)0xff; - png_ptr->background.red = png_ptr->background.green - = png_ptr->background.blue = png_ptr->background.gray; - if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) - { - png_ptr->trans_values.gray *= (png_uint_16)0xff; - png_ptr->trans_values.red = png_ptr->trans_values.green - = png_ptr->trans_values.blue = png_ptr->trans_values.gray; - } - break; - case 2: - png_ptr->background.gray *= (png_uint_16)0x55; - png_ptr->background.red = png_ptr->background.green - = png_ptr->background.blue = png_ptr->background.gray; - if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) - { - png_ptr->trans_values.gray *= (png_uint_16)0x55; - png_ptr->trans_values.red = png_ptr->trans_values.green - = png_ptr->trans_values.blue = png_ptr->trans_values.gray; - } - break; - case 4: - png_ptr->background.gray *= (png_uint_16)0x11; - png_ptr->background.red = png_ptr->background.green - = png_ptr->background.blue = png_ptr->background.gray; - if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) - { - png_ptr->trans_values.gray *= (png_uint_16)0x11; - png_ptr->trans_values.red = png_ptr->trans_values.green - = png_ptr->trans_values.blue = png_ptr->trans_values.gray; - } - break; - case 8: - case 16: - png_ptr->background.red = png_ptr->background.green - = png_ptr->background.blue = png_ptr->background.gray; - break; + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; } } - else if (color_type == PNG_COLOR_TYPE_PALETTE) - { - png_ptr->background.red = - png_ptr->palette[png_ptr->background.index].red; - png_ptr->background.green = - png_ptr->palette[png_ptr->background.index].green; - png_ptr->background.blue = - png_ptr->palette[png_ptr->background.index].blue; + } +#endif /* PNG_READ_EXPAND_SUPPORTED && PNG_READ_BACKGROUND_SUPPORTED */ +#endif /* PNG_READ_GRAY_TO_RGB_SUPPORTED */ -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) - if (png_ptr->transformations & PNG_INVERT_ALPHA) - { -#if defined(PNG_READ_EXPAND_SUPPORTED) - if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) -#endif - { - /* invert the alpha channel (in tRNS) unless the pixels are - going to be expanded, in which case leave it for later */ - int i,istop; - istop=(int)png_ptr->num_trans; - for (i=0; itrans[i] = (png_byte)(255 - png_ptr->trans[i]); - } - } -#endif + /* For indexed PNG data (PNG_COLOR_TYPE_PALETTE) many of the transformations + * can be performed directly on the palette, and some (such as rgb to gray) + * can be optimized inside the palette. This is particularly true of the + * composite (background and alpha) stuff, which can be pretty much all done + * in the palette even if the result is expanded to RGB or gray afterward. + * + * NOTE: this is Not Yet Implemented, the code behaves as in 1.5.1 and + * earlier and the palette stuff is actually handled on the first row. This + * leads to the reported bug that the palette returned by png_get_PLTE is not + * updated. + */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_init_palette_transformations(png_ptr); - } + else + png_init_rgb_transformations(png_ptr); + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_EXPAND_16_SUPPORTED) + if ((png_ptr->transformations & PNG_EXPAND_16) && + (png_ptr->transformations & PNG_COMPOSE) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + png_ptr->bit_depth != 16) + { + /* TODO: fix this. Because the expand_16 operation is after the compose + * handling the background color must be 8, not 16, bits deep, but the + * application will supply a 16-bit value so reduce it here. + * + * The PNG_BACKGROUND_EXPAND code above does not expand to 16 bits at + * present, so that case is ok (until do_expand_16 is moved.) + * + * NOTE: this discards the low 16 bits of the user supplied background + * color, but until expand_16 works properly there is no choice! + */ +# define CHOP(x) (x)=((png_uint_16)PNG_DIV257(x)) + CHOP(png_ptr->background.red); + CHOP(png_ptr->background.green); + CHOP(png_ptr->background.blue); + CHOP(png_ptr->background.gray); +# undef CHOP + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED && PNG_READ_EXPAND_16_SUPPORTED */ + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + (defined(PNG_READ_SCALE_16_TO_8_SUPPORTED) || \ + defined(PNG_READ_STRIP_16_TO_8_SUPPORTED)) + if ((png_ptr->transformations & (PNG_16_TO_8|PNG_SCALE_16_TO_8)) && + (png_ptr->transformations & PNG_COMPOSE) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + png_ptr->bit_depth == 16) + { + /* On the other hand, if a 16-bit file is to be reduced to 8-bits per + * component this will also happen after PNG_COMPOSE and so the background + * color must be pre-expanded here. + * + * TODO: fix this too. + */ + png_ptr->background.red = (png_uint_16)(png_ptr->background.red * 257); + png_ptr->background.green = + (png_uint_16)(png_ptr->background.green * 257); + png_ptr->background.blue = (png_uint_16)(png_ptr->background.blue * 257); + png_ptr->background.gray = (png_uint_16)(png_ptr->background.gray * 257); } #endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) - png_ptr->background_1 = png_ptr->background; -#endif -#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + /* NOTE: below 'PNG_READ_ALPHA_MODE_SUPPORTED' is presumed to also enable the + * background support (see the comments in scripts/pnglibconf.dfa), this + * allows pre-multiplication of the alpha channel to be implemented as + * compositing on black. This is probably sub-optimal and has been done in + * 1.5.4 betas simply to enable external critique and testing (i.e. to + * implement the new API quickly, without lots of internal changes.) + */ - if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) - && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) - < PNG_GAMMA_THRESHOLD)) - { - int i,k; - k=0; - for (i=0; inum_trans; i++) - { - if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff) - k=1; /* partial transparency is present */ - } - if (k == 0) - png_ptr->transformations &= (~PNG_GAMMA); - } +#ifdef PNG_READ_GAMMA_SUPPORTED +# ifdef PNG_READ_BACKGROUND_SUPPORTED + /* Includes ALPHA_MODE */ + png_ptr->background_1 = png_ptr->background; +# endif - if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && - png_ptr->gamma != 0.0) + /* This needs to change - in the palette image case a whole set of tables are + * built when it would be quicker to just calculate the correct value for + * each palette entry directly. Also, the test is too tricky - why check + * PNG_RGB_TO_GRAY if PNG_GAMMA is not set? The answer seems to be that + * PNG_GAMMA is cancelled even if the gamma is known? The test excludes the + * PNG_COMPOSE case, so apparently if there is no *overall* gamma correction + * the gamma tables will not be built even if composition is required on a + * gamma encoded value. + * + * In 1.5.4 this is addressed below by an additional check on the individual + * file gamma - if it is not 1.0 both RGB_TO_GRAY and COMPOSE need the + * tables. + */ + if ((png_ptr->transformations & PNG_GAMMA) + || ((png_ptr->transformations & PNG_RGB_TO_GRAY) + && (png_gamma_significant(png_ptr->colorspace.gamma) || + png_gamma_significant(png_ptr->screen_gamma))) + || ((png_ptr->transformations & PNG_COMPOSE) + && (png_gamma_significant(png_ptr->colorspace.gamma) + || png_gamma_significant(png_ptr->screen_gamma) +# ifdef PNG_READ_BACKGROUND_SUPPORTED + || (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_UNIQUE + && png_gamma_significant(png_ptr->background_gamma)) +# endif + )) || ((png_ptr->transformations & PNG_ENCODE_ALPHA) + && png_gamma_significant(png_ptr->screen_gamma)) + ) { - png_build_gamma_table(png_ptr); -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->transformations & PNG_BACKGROUND) + png_build_gamma_table(png_ptr, png_ptr->bit_depth); + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + if (png_ptr->transformations & PNG_COMPOSE) { - if (color_type == PNG_COLOR_TYPE_PALETTE) + /* Issue a warning about this combination: because RGB_TO_GRAY is + * optimized to do the gamma transform if present yet do_background has + * to do the same thing if both options are set a + * double-gamma-correction happens. This is true in all versions of + * libpng to date. + */ + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + png_warning(png_ptr, + "libpng does not support gamma+background+rgb_to_gray"); + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - /* could skip if no transparency and - */ + /* We don't get to here unless there is a tRNS chunk with non-opaque + * entries - see the checking code at the start of this function. + */ png_color back, back_1; png_colorp palette = png_ptr->palette; int num_palette = png_ptr->num_palette; int i; if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) { + back.red = png_ptr->gamma_table[png_ptr->background.red]; back.green = png_ptr->gamma_table[png_ptr->background.green]; back.blue = png_ptr->gamma_table[png_ptr->background.blue]; @@ -888,73 +1601,90 @@ png_init_read_transformations(png_structp png_ptr) } else { - double g, gs; + png_fixed_point g, gs; switch (png_ptr->background_gamma_type) { case PNG_BACKGROUND_GAMMA_SCREEN: g = (png_ptr->screen_gamma); - gs = 1.0; + gs = PNG_FP_1; break; + case PNG_BACKGROUND_GAMMA_FILE: - g = 1.0 / (png_ptr->gamma); - gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, + png_ptr->screen_gamma); break; + case PNG_BACKGROUND_GAMMA_UNIQUE: - g = 1.0 / (png_ptr->background_gamma); - gs = 1.0 / (png_ptr->background_gamma * - png_ptr->screen_gamma); + g = png_reciprocal(png_ptr->background_gamma); + gs = png_reciprocal2(png_ptr->background_gamma, + png_ptr->screen_gamma); break; default: - g = 1.0; /* back_1 */ - gs = 1.0; /* back */ + g = PNG_FP_1; /* back_1 */ + gs = PNG_FP_1; /* back */ + break; } - if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + if (png_gamma_significant(gs)) + { + back.red = png_gamma_8bit_correct(png_ptr->background.red, + gs); + back.green = png_gamma_8bit_correct(png_ptr->background.green, + gs); + back.blue = png_gamma_8bit_correct(png_ptr->background.blue, + gs); + } + + else { back.red = (png_byte)png_ptr->background.red; back.green = (png_byte)png_ptr->background.green; back.blue = (png_byte)png_ptr->background.blue; } - else + + if (png_gamma_significant(g)) { - back.red = (png_byte)(pow( - (double)png_ptr->background.red/255, gs) * 255.0 + .5); - back.green = (png_byte)(pow( - (double)png_ptr->background.green/255, gs) * 255.0 + .5); - back.blue = (png_byte)(pow( - (double)png_ptr->background.blue/255, gs) * 255.0 + .5); + back_1.red = png_gamma_8bit_correct(png_ptr->background.red, + g); + back_1.green = png_gamma_8bit_correct( + png_ptr->background.green, g); + back_1.blue = png_gamma_8bit_correct(png_ptr->background.blue, + g); } - back_1.red = (png_byte)(pow( - (double)png_ptr->background.red/255, g) * 255.0 + .5); - back_1.green = (png_byte)(pow( - (double)png_ptr->background.green/255, g) * 255.0 + .5); - back_1.blue = (png_byte)(pow( - (double)png_ptr->background.blue/255, g) * 255.0 + .5); + else + { + back_1.red = (png_byte)png_ptr->background.red; + back_1.green = (png_byte)png_ptr->background.green; + back_1.blue = (png_byte)png_ptr->background.blue; + } } + for (i = 0; i < num_palette; i++) { - if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff) + if (i < (int)png_ptr->num_trans && + png_ptr->trans_alpha[i] != 0xff) { - if (png_ptr->trans[i] == 0) + if (png_ptr->trans_alpha[i] == 0) { palette[i] = back; } - else /* if (png_ptr->trans[i] != 0xff) */ + else /* if (png_ptr->trans_alpha[i] != 0xff) */ { png_byte v, w; v = png_ptr->gamma_to_1[palette[i].red]; - png_composite(w, v, png_ptr->trans[i], back_1.red); + png_composite(w, v, png_ptr->trans_alpha[i], back_1.red); palette[i].red = png_ptr->gamma_from_1[w]; v = png_ptr->gamma_to_1[palette[i].green]; - png_composite(w, v, png_ptr->trans[i], back_1.green); + png_composite(w, v, png_ptr->trans_alpha[i], back_1.green); palette[i].green = png_ptr->gamma_from_1[w]; v = png_ptr->gamma_to_1[palette[i].blue]; - png_composite(w, v, png_ptr->trans[i], back_1.blue); + png_composite(w, v, png_ptr->trans_alpha[i], back_1.blue); palette[i].blue = png_ptr->gamma_from_1[w]; } } @@ -965,90 +1695,140 @@ png_init_read_transformations(png_structp png_ptr) palette[i].blue = png_ptr->gamma_table[palette[i].blue]; } } - } + + /* Prevent the transformations being done again. + * + * NOTE: this is highly dubious; it removes the transformations in + * place. This seems inconsistent with the general treatment of the + * transformations elsewhere. + */ + png_ptr->transformations &= ~(PNG_COMPOSE | PNG_GAMMA); + } /* color_type == PNG_COLOR_TYPE_PALETTE */ + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ - else - /* color_type != PNG_COLOR_TYPE_PALETTE */ + else /* color_type != PNG_COLOR_TYPE_PALETTE */ { - double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); - double g = 1.0; - double gs = 1.0; + int gs_sig, g_sig; + png_fixed_point g = PNG_FP_1; /* Correction to linear */ + png_fixed_point gs = PNG_FP_1; /* Correction to screen */ switch (png_ptr->background_gamma_type) { case PNG_BACKGROUND_GAMMA_SCREEN: - g = (png_ptr->screen_gamma); - gs = 1.0; + g = png_ptr->screen_gamma; + /* gs = PNG_FP_1; */ break; + case PNG_BACKGROUND_GAMMA_FILE: - g = 1.0 / (png_ptr->gamma); - gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); - break; - case PNG_BACKGROUND_GAMMA_UNIQUE: - g = 1.0 / (png_ptr->background_gamma); - gs = 1.0 / (png_ptr->background_gamma * + g = png_reciprocal(png_ptr->colorspace.gamma); + gs = png_reciprocal2(png_ptr->colorspace.gamma, png_ptr->screen_gamma); break; + + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = png_reciprocal(png_ptr->background_gamma); + gs = png_reciprocal2(png_ptr->background_gamma, + png_ptr->screen_gamma); + break; + + default: + png_error(png_ptr, "invalid background gamma type"); } - png_ptr->background_1.gray = (png_uint_16)(pow( - (double)png_ptr->background.gray / m, g) * m + .5); - png_ptr->background.gray = (png_uint_16)(pow( - (double)png_ptr->background.gray / m, gs) * m + .5); + g_sig = png_gamma_significant(g); + gs_sig = png_gamma_significant(gs); + + if (g_sig) + png_ptr->background_1.gray = png_gamma_correct(png_ptr, + png_ptr->background.gray, g); + + if (gs_sig) + png_ptr->background.gray = png_gamma_correct(png_ptr, + png_ptr->background.gray, gs); if ((png_ptr->background.red != png_ptr->background.green) || (png_ptr->background.red != png_ptr->background.blue) || (png_ptr->background.red != png_ptr->background.gray)) { /* RGB or RGBA with color background */ - png_ptr->background_1.red = (png_uint_16)(pow( - (double)png_ptr->background.red / m, g) * m + .5); - png_ptr->background_1.green = (png_uint_16)(pow( - (double)png_ptr->background.green / m, g) * m + .5); - png_ptr->background_1.blue = (png_uint_16)(pow( - (double)png_ptr->background.blue / m, g) * m + .5); - png_ptr->background.red = (png_uint_16)(pow( - (double)png_ptr->background.red / m, gs) * m + .5); - png_ptr->background.green = (png_uint_16)(pow( - (double)png_ptr->background.green / m, gs) * m + .5); - png_ptr->background.blue = (png_uint_16)(pow( - (double)png_ptr->background.blue / m, gs) * m + .5); + if (g_sig) + { + png_ptr->background_1.red = png_gamma_correct(png_ptr, + png_ptr->background.red, g); + + png_ptr->background_1.green = png_gamma_correct(png_ptr, + png_ptr->background.green, g); + + png_ptr->background_1.blue = png_gamma_correct(png_ptr, + png_ptr->background.blue, g); + } + + if (gs_sig) + { + png_ptr->background.red = png_gamma_correct(png_ptr, + png_ptr->background.red, gs); + + png_ptr->background.green = png_gamma_correct(png_ptr, + png_ptr->background.green, gs); + + png_ptr->background.blue = png_gamma_correct(png_ptr, + png_ptr->background.blue, gs); + } } + 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_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green - = png_ptr->background.blue = png_ptr->background.gray; + = png_ptr->background.blue = png_ptr->background.gray; } - } - } + + /* The background is now in screen gamma: */ + png_ptr->background_gamma_type = PNG_BACKGROUND_GAMMA_SCREEN; + } /* color_type != PNG_COLOR_TYPE_PALETTE */ + }/* png_ptr->transformations & PNG_BACKGROUND */ + else - /* transformation does not include PNG_BACKGROUND */ + /* Transformation does not include PNG_BACKGROUND */ #endif /* PNG_READ_BACKGROUND_SUPPORTED */ - if (color_type == PNG_COLOR_TYPE_PALETTE) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* RGB_TO_GRAY needs to have non-gamma-corrected values! */ + && ((png_ptr->transformations & PNG_EXPAND) == 0 || + (png_ptr->transformations & PNG_RGB_TO_GRAY) == 0) +#endif + ) { png_colorp palette = png_ptr->palette; int num_palette = png_ptr->num_palette; int i; + /* NOTE: there are other transformations that should probably be in + * here too. + */ for (i = 0; i < num_palette; i++) { palette[i].red = png_ptr->gamma_table[palette[i].red]; palette[i].green = png_ptr->gamma_table[palette[i].green]; palette[i].blue = png_ptr->gamma_table[palette[i].blue]; } - } + + /* Done the gamma correction. */ + png_ptr->transformations &= ~PNG_GAMMA; + } /* color_type == PALETTE && !PNG_BACKGROUND transformation */ } -#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#ifdef PNG_READ_BACKGROUND_SUPPORTED else #endif -#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - /* No GAMMA transformation */ - if ((png_ptr->transformations & PNG_BACKGROUND) && - (color_type == PNG_COLOR_TYPE_PALETTE)) +#endif /* PNG_READ_GAMMA_SUPPORTED */ + +#ifdef PNG_READ_BACKGROUND_SUPPORTED + /* No GAMMA transformation (see the hanging else 4 lines above) */ + if ((png_ptr->transformations & PNG_COMPOSE) && + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) { int i; int istop = (int)png_ptr->num_trans; @@ -1061,54 +1841,71 @@ png_init_read_transformations(png_structp png_ptr) for (i = 0; i < istop; i++) { - if (png_ptr->trans[i] == 0) + if (png_ptr->trans_alpha[i] == 0) { palette[i] = back; } - else if (png_ptr->trans[i] != 0xff) + + else if (png_ptr->trans_alpha[i] != 0xff) { /* The png_composite() macro is defined in png.h */ png_composite(palette[i].red, palette[i].red, - png_ptr->trans[i], back.red); + png_ptr->trans_alpha[i], back.red); + png_composite(palette[i].green, palette[i].green, - png_ptr->trans[i], back.green); + png_ptr->trans_alpha[i], back.green); + png_composite(palette[i].blue, palette[i].blue, - png_ptr->trans[i], back.blue); + png_ptr->trans_alpha[i], back.blue); } } + + png_ptr->transformations &= ~PNG_COMPOSE; } #endif /* PNG_READ_BACKGROUND_SUPPORTED */ -#if defined(PNG_READ_SHIFT_SUPPORTED) +#ifdef PNG_READ_SHIFT_SUPPORTED if ((png_ptr->transformations & PNG_SHIFT) && - (color_type == PNG_COLOR_TYPE_PALETTE)) + !(png_ptr->transformations & PNG_EXPAND) && + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) { - png_uint_16 i; - png_uint_16 istop = png_ptr->num_palette; - int sr = 8 - png_ptr->sig_bit.red; - int sg = 8 - png_ptr->sig_bit.green; - int sb = 8 - png_ptr->sig_bit.blue; + int i; + int istop = png_ptr->num_palette; + int shift = 8 - png_ptr->sig_bit.red; - if (sr < 0 || sr > 8) - sr = 0; - if (sg < 0 || sg > 8) - sg = 0; - if (sb < 0 || sb > 8) - sb = 0; - for (i = 0; i < istop; i++) + png_ptr->transformations &= ~PNG_SHIFT; + + /* significant bits can be in the range 1 to 7 for a meaninful result, if + * the number of significant bits is 0 then no shift is done (this is an + * error condition which is silently ignored.) + */ + if (shift > 0 && shift < 8) for (i=0; ipalette[i].red >>= sr; - png_ptr->palette[i].green >>= sg; - png_ptr->palette[i].blue >>= sb; + int component = png_ptr->palette[i].red; + + component >>= shift; + png_ptr->palette[i].red = (png_byte)component; + } + + shift = 8 - png_ptr->sig_bit.green; + if (shift > 0 && shift < 8) for (i=0; ipalette[i].green; + + component >>= shift; + png_ptr->palette[i].green = (png_byte)component; + } + + shift = 8 - png_ptr->sig_bit.blue; + if (shift > 0 && shift < 8) for (i=0; ipalette[i].blue; + + component >>= shift; + png_ptr->palette[i].blue = (png_byte)component; } } #endif /* PNG_READ_SHIFT_SUPPORTED */ - } -#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ - && !defined(PNG_READ_BACKGROUND_SUPPORTED) - if(png_ptr) - return; -#endif } /* Modify the info structure to reflect the transformations. The @@ -1116,18 +1913,25 @@ png_init_read_transformations(png_structp png_ptr) * assuming the transformations result in valid PNG data. */ void /* PRIVATE */ -png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +png_read_transform_info(png_structrp png_ptr, png_inforp info_ptr) { - png_debug(1, "in png_read_transform_info\n"); -#if defined(PNG_READ_EXPAND_SUPPORTED) + png_debug(1, "in png_read_transform_info"); + +#ifdef PNG_READ_EXPAND_SUPPORTED if (png_ptr->transformations & PNG_EXPAND) { if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) + /* This check must match what actually happens in + * png_do_expand_palette; if it ever checks the tRNS chunk to see if + * it is all opaque we must do the same (at present it does not.) + */ + if (png_ptr->num_trans > 0) info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; info_ptr->num_trans = 0; } @@ -1136,118 +1940,174 @@ png_read_transform_info(png_structp png_ptr, png_infop info_ptr) if (png_ptr->num_trans) { if (png_ptr->transformations & PNG_EXPAND_tRNS) - info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; - else - info_ptr->color_type |= PNG_COLOR_MASK_COLOR; + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; } if (info_ptr->bit_depth < 8) info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; } } #endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->transformations & PNG_BACKGROUND) - { - info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; - info_ptr->num_trans = 0; +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + /* The following is almost certainly wrong unless the background value is in + * the screen space! + */ + if (png_ptr->transformations & PNG_COMPOSE) info_ptr->background = png_ptr->background; - } #endif -#if defined(PNG_READ_GAMMA_SUPPORTED) - if (png_ptr->transformations & PNG_GAMMA) +#ifdef PNG_READ_GAMMA_SUPPORTED + /* The following used to be conditional on PNG_GAMMA (prior to 1.5.4), + * however it seems that the code in png_init_read_transformations, which has + * been called before this from png_read_update_info->png_read_start_row + * sometimes does the gamma transform and cancels the flag. + * + * TODO: this looks wrong; the info_ptr should end up with a gamma equal to + * the screen_gamma value. The following probably results in weirdness if + * the info_ptr is used by the app after the rows have been read. + */ + info_ptr->colorspace.gamma = png_ptr->colorspace.gamma; +#endif + + if (info_ptr->bit_depth == 16) { -#ifdef PNG_FLOATING_POINT_SUPPORTED - info_ptr->gamma = png_ptr->gamma; -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED - info_ptr->int_gamma = png_ptr->int_gamma; -#endif +# ifdef PNG_READ_16BIT_SUPPORTED +# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_SCALE_16_TO_8) + info_ptr->bit_depth = 8; +# endif + +# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_16_TO_8) + info_ptr->bit_depth = 8; +# endif + +# else + /* No 16 bit support: force chopping 16-bit input down to 8, in this case + * the app program can chose if both APIs are available by setting the + * correct scaling to use. + */ +# ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* For compatibility with previous versions use the strip method by + * default. This code works because if PNG_SCALE_16_TO_8 is already + * set the code below will do that in preference to the chop. + */ + png_ptr->transformations |= PNG_16_TO_8; + info_ptr->bit_depth = 8; +# else + +# ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + png_ptr->transformations |= PNG_SCALE_16_TO_8; + info_ptr->bit_depth = 8; +# else + + CONFIGURATION ERROR: you must enable at least one 16 to 8 method +# endif +# endif +#endif /* !READ_16BIT_SUPPORTED */ } -#endif -#if defined(PNG_READ_16_TO_8_SUPPORTED) - if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) - info_ptr->bit_depth = 8; -#endif - -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED if (png_ptr->transformations & PNG_GRAY_TO_RGB) - info_ptr->color_type |= PNG_COLOR_MASK_COLOR; + info_ptr->color_type = (png_byte)(info_ptr->color_type | + PNG_COLOR_MASK_COLOR); #endif -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED if (png_ptr->transformations & PNG_RGB_TO_GRAY) - info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; + info_ptr->color_type = (png_byte)(info_ptr->color_type & + ~PNG_COLOR_MASK_COLOR); #endif -#if defined(PNG_READ_DITHER_SUPPORTED) - if (png_ptr->transformations & PNG_DITHER) +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if (png_ptr->transformations & PNG_QUANTIZE) { if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || - (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && - png_ptr->palette_lookup && info_ptr->bit_depth == 8) + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) { info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; } } #endif -#if defined(PNG_READ_PACK_SUPPORTED) +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND_16 && info_ptr->bit_depth == 8 && + info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + info_ptr->bit_depth = 16; + } +#endif + +#ifdef PNG_READ_PACK_SUPPORTED if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) info_ptr->bit_depth = 8; #endif if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) info_ptr->channels = 3; + else info_ptr->channels = 1; -#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) - if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) - info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if (png_ptr->transformations & PNG_STRIP_ALPHA) + { + info_ptr->color_type = (png_byte)(info_ptr->color_type & + ~PNG_COLOR_MASK_ALPHA); + info_ptr->num_trans = 0; + } #endif if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) info_ptr->channels++; -#if defined(PNG_READ_FILLER_SUPPORTED) +#ifdef PNG_READ_FILLER_SUPPORTED /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ if ((png_ptr->transformations & PNG_FILLER) && ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) { info_ptr->channels++; - /* if adding a true alpha channel not just filler */ -#if !defined(PNG_1_0_X) + /* If adding a true alpha channel not just filler */ if (png_ptr->transformations & PNG_ADD_ALPHA) - info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; -#endif + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; } #endif #if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ defined(PNG_READ_USER_TRANSFORM_SUPPORTED) - if(png_ptr->transformations & PNG_USER_TRANSFORM) - { - if(info_ptr->bit_depth < png_ptr->user_transform_depth) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if (info_ptr->bit_depth < png_ptr->user_transform_depth) info_ptr->bit_depth = png_ptr->user_transform_depth; - if(info_ptr->channels < png_ptr->user_transform_channels) + + if (info_ptr->channels < png_ptr->user_transform_channels) info_ptr->channels = png_ptr->user_transform_channels; - } + } #endif info_ptr->pixel_depth = (png_byte)(info_ptr->channels * - info_ptr->bit_depth); + info_ptr->bit_depth); - info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width); + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, info_ptr->width); -#if !defined(PNG_READ_EXPAND_SUPPORTED) - if(png_ptr) + /* Adding in 1.5.4: cache the above value in png_struct so that we can later + * check in png_rowbytes that the user buffer won't get overwritten. Note + * that the field is not always set - if png_read_update_info isn't called + * the application has to either not do any transforms or get the calculation + * right itself. + */ + png_ptr->info_rowbytes = info_ptr->rowbytes; + +#ifndef PNG_READ_EXPAND_SUPPORTED + if (png_ptr) return; #endif } @@ -1257,245 +2117,299 @@ defined(PNG_READ_USER_TRANSFORM_SUPPORTED) * decide how it fits in with the other transformations here. */ void /* PRIVATE */ -png_do_read_transformations(png_structp png_ptr) +png_do_read_transformations(png_structrp png_ptr, png_row_infop row_info) { - png_debug(1, "in png_do_read_transformations\n"); + png_debug(1, "in png_do_read_transformations"); + if (png_ptr->row_buf == NULL) { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char msg[50]; - - png_snprintf2(msg, 50, - "NULL row buffer for row %ld, pass %d", png_ptr->row_number, - png_ptr->pass); - png_error(png_ptr, msg); -#else + /* Prior to 1.5.4 this output row/pass where the NULL pointer is, but this + * error is incredibly rare and incredibly easy to debug without this + * information. + */ png_error(png_ptr, "NULL row buffer"); -#endif } -#ifdef PNG_WARN_UNINITIALIZED_ROW - if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) - /* Application has failed to call either png_read_start_image() - * or png_read_update_info() after setting transforms that expand - * pixels. This check added to libpng-1.2.19 */ -#if (PNG_WARN_UNINITIALIZED_ROW==1) - png_error(png_ptr, "Uninitialized row"); -#else - png_warning(png_ptr, "Uninitialized row"); -#endif -#endif -#if defined(PNG_READ_EXPAND_SUPPORTED) + /* The following is debugging; prior to 1.5.4 the code was never compiled in; + * in 1.5.4 PNG_FLAG_DETECT_UNINITIALIZED was added and the macro + * PNG_WARN_UNINITIALIZED_ROW removed. In 1.6 the new flag is set only for + * all transformations, however in practice the ROW_INIT always gets done on + * demand, if necessary. + */ + if ((png_ptr->flags & PNG_FLAG_DETECT_UNINITIALIZED) != 0 && + !(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 (but not enabled until 1.5.4). + */ + png_error(png_ptr, "Uninitialized row"); + } + +#ifdef PNG_READ_EXPAND_SUPPORTED if (png_ptr->transformations & PNG_EXPAND) { - if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) { - png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, - png_ptr->palette, png_ptr->trans, png_ptr->num_trans); + png_do_expand_palette(row_info, png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans_alpha, png_ptr->num_trans); } + else { if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) - png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, - &(png_ptr->trans_values)); + png_do_expand(row_info, png_ptr->row_buf + 1, + &(png_ptr->trans_color)); + else - png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, - NULL); + png_do_expand(row_info, png_ptr->row_buf + 1, + NULL); } } #endif -#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) - if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) - png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, - PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) && + !(png_ptr->transformations & PNG_COMPOSE) && + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + 0 /* at_start == false, because SWAP_ALPHA happens later */); #endif -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED if (png_ptr->transformations & PNG_RGB_TO_GRAY) { int rgb_error = - png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1); - if(rgb_error) + png_do_rgb_to_gray(png_ptr, row_info, + png_ptr->row_buf + 1); + + if (rgb_error) { png_ptr->rgb_to_gray_status=1; - if((png_ptr->transformations & PNG_RGB_TO_GRAY) == + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == PNG_RGB_TO_GRAY_WARN) png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); - if((png_ptr->transformations & PNG_RGB_TO_GRAY) == + + if ((png_ptr->transformations & PNG_RGB_TO_GRAY) == PNG_RGB_TO_GRAY_ERR) png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); } } #endif -/* -From Andreas Dilger e-mail to png-implement, 26 March 1998: - - In most cases, the "simple transparency" should be done prior to doing - gray-to-RGB, or you will have to test 3x as many bytes to check if a - pixel is transparent. You would also need to make sure that the - transparency information is upgraded to RGB. - - To summarize, the current flow is: - - Gray + simple transparency -> compare 1 or 2 gray bytes and composite - with background "in place" if transparent, - convert to RGB if necessary - - Gray + alpha -> composite with gray background and remove alpha bytes, - convert to RGB if necessary - - To support RGB backgrounds for gray images we need: - - Gray + simple transparency -> convert to RGB + simple transparency, compare - 3 or 6 bytes and composite with background - "in place" if transparent (3x compare/pixel - compared to doing composite with gray bkgrnd) - - Gray + alpha -> convert to RGB + alpha, composite with background and - remove alpha bytes (3x float operations/pixel - compared with composite on gray background) - - Greg's change will do this. The reason it wasn't done before is for - performance, as this increases the per-pixel operations. If we would check - in advance if the background was gray or RGB, and position the gray-to-RGB - transform appropriately, then it would save a lot of work/time. +/* 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 */ +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* If gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons + */ if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) - png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - if ((png_ptr->transformations & PNG_BACKGROUND) && - ((png_ptr->num_trans != 0 ) || - (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) - png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, - &(png_ptr->trans_values), &(png_ptr->background) -#if defined(PNG_READ_GAMMA_SUPPORTED) - , &(png_ptr->background_1), - png_ptr->gamma_table, png_ptr->gamma_from_1, - png_ptr->gamma_to_1, png_ptr->gamma_16_table, - png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, - png_ptr->gamma_shift -#endif -); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + if (png_ptr->transformations & PNG_COMPOSE) + png_do_compose(row_info, png_ptr->row_buf + 1, png_ptr); #endif -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if ((png_ptr->transformations & PNG_GAMMA) && -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - !((png_ptr->transformations & PNG_BACKGROUND) && - ((png_ptr->num_trans != 0) || - (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + /* Because RGB_TO_GRAY does the gamma transform. */ + !(png_ptr->transformations & PNG_RGB_TO_GRAY) && #endif - (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) - png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, - png_ptr->gamma_table, png_ptr->gamma_16_table, - png_ptr->gamma_shift); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + /* Because PNG_COMPOSE does the gamma transform if there is something to + * do (if there is an alpha channel or transparency.) + */ + !((png_ptr->transformations & PNG_COMPOSE) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + /* Because png_init_read_transformations transforms the palette, unless + * RGB_TO_GRAY will do the transform. + */ + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(row_info, png_ptr->row_buf + 1, png_ptr); #endif -#if defined(PNG_READ_16_TO_8_SUPPORTED) +#ifdef PNG_READ_STRIP_ALPHA_SUPPORTED + if ((png_ptr->transformations & PNG_STRIP_ALPHA) && + (png_ptr->transformations & PNG_COMPOSE) && + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA || + row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + 0 /* at_start == false, because SWAP_ALPHA happens later */); +#endif + +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED + if ((png_ptr->transformations & PNG_ENCODE_ALPHA) && + (row_info->color_type & PNG_COLOR_MASK_ALPHA)) + png_do_encode_alpha(row_info, png_ptr->row_buf + 1, png_ptr); +#endif + +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED + if (png_ptr->transformations & PNG_SCALE_16_TO_8) + png_do_scale_16_to_8(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED + /* There is no harm in doing both of these because only one has any effect, + * by putting the 'scale' option first if the app asks for scale (either by + * calling the API or in a TRANSFORM flag) this is what happens. + */ if (png_ptr->transformations & PNG_16_TO_8) - png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_chop(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_READ_DITHER_SUPPORTED) - if (png_ptr->transformations & PNG_DITHER) +#ifdef PNG_READ_QUANTIZE_SUPPORTED + if (png_ptr->transformations & PNG_QUANTIZE) { - png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, - png_ptr->palette_lookup, png_ptr->dither_index); - if(png_ptr->row_info.rowbytes == (png_uint_32)0) - png_error(png_ptr, "png_do_dither returned rowbytes=0"); + png_do_quantize(row_info, png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->quantize_index); + + if (row_info->rowbytes == 0) + png_error(png_ptr, "png_do_quantize returned rowbytes=0"); } +#endif /* PNG_READ_QUANTIZE_SUPPORTED */ + +#ifdef PNG_READ_EXPAND_16_SUPPORTED + /* Do the expansion now, after all the arithmetic has been done. Notice + * that previous transformations can handle the PNG_EXPAND_16 flag if this + * is efficient (particularly true in the case of gamma correction, where + * better accuracy results faster!) + */ + if (png_ptr->transformations & PNG_EXPAND_16) + png_do_expand_16(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_READ_INVERT_SUPPORTED) - if (png_ptr->transformations & PNG_INVERT_MONO) - png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); -#endif - -#if defined(PNG_READ_SHIFT_SUPPORTED) - if (png_ptr->transformations & PNG_SHIFT) - png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, - &(png_ptr->shift)); -#endif - -#if defined(PNG_READ_PACK_SUPPORTED) - if (png_ptr->transformations & PNG_PACK) - png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); -#endif - -#if defined(PNG_READ_BGR_SUPPORTED) - if (png_ptr->transformations & PNG_BGR) - png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); -#endif - -#if defined(PNG_READ_PACKSWAP_SUPPORTED) - if (png_ptr->transformations & PNG_PACKSWAP) - png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); -#endif - -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) - /* if gray -> RGB, do so now only if we did not do so above */ +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED + /* NOTE: moved here in 1.5.4 (from much later in this list.) */ 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); + png_do_gray_to_rgb(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_READ_FILLER_SUPPORTED) +#ifdef PNG_READ_INVERT_SUPPORTED + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_SHIFT_SUPPORTED + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(row_info, png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#ifdef PNG_READ_PACK_SUPPORTED + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Added at libpng-1.5.10 */ + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max >= 0) + png_do_check_palette_indexes(png_ptr, row_info); +#endif + +#ifdef PNG_READ_BGR_SUPPORTED + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(row_info, png_ptr->row_buf + 1); +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED if (png_ptr->transformations & PNG_FILLER) - png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, - (png_uint_32)png_ptr->filler, png_ptr->flags); + png_do_read_filler(row_info, png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); #endif -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_INVERT_ALPHA) - png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_read_invert_alpha(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_SWAP_ALPHA) - png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_read_swap_alpha(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_READ_SWAP_SUPPORTED) +#ifdef PNG_READ_16BIT_SUPPORTED +#ifdef PNG_READ_SWAP_SUPPORTED if (png_ptr->transformations & PNG_SWAP_BYTES) - png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_swap(row_info, png_ptr->row_buf + 1); +#endif #endif -#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED if (png_ptr->transformations & PNG_USER_TRANSFORM) { - if(png_ptr->read_user_transform_fn != NULL) - (*(png_ptr->read_user_transform_fn)) /* user read transform function */ - (png_ptr, /* png_ptr */ - &(png_ptr->row_info), /* row_info: */ - /* png_uint_32 width; width of row */ - /* png_uint_32 rowbytes; number of bytes in row */ - /* png_byte color_type; color type of pixels */ - /* png_byte bit_depth; bit depth of samples */ - /* png_byte channels; number of channels (1-4) */ - /* png_byte pixel_depth; bits per pixel (depth*channels) */ - png_ptr->row_buf + 1); /* start of pixel data for row */ -#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) - if(png_ptr->user_transform_depth) - png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; - if(png_ptr->user_transform_channels) - png_ptr->row_info.channels = png_ptr->user_transform_channels; + if (png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* User read transform function */ + (png_ptr, /* png_ptr */ + row_info, /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_size_t rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED + if (png_ptr->user_transform_depth) + row_info->bit_depth = png_ptr->user_transform_depth; + + if (png_ptr->user_transform_channels) + row_info->channels = png_ptr->user_transform_channels; #endif - png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * - png_ptr->row_info.channels); - png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, - png_ptr->row_info.width); + row_info->pixel_depth = (png_byte)(row_info->bit_depth * + row_info->channels); + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_info->width); } #endif - } -#if defined(PNG_READ_PACK_SUPPORTED) +#ifdef PNG_READ_PACK_SUPPORTED /* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, * without changing the actual values. Thus, if you had a row with * a bit depth of 1, you would end up with bytes that only contained @@ -1505,12 +2419,9 @@ From Andreas Dilger e-mail to png-implement, 26 March 1998: void /* PRIVATE */ png_do_unpack(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_unpack\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL && row_info->bit_depth < 8) -#else + png_debug(1, "in png_do_unpack"); + if (row_info->bit_depth < 8) -#endif { png_uint_32 i; png_uint_32 row_width=row_info->width; @@ -1525,11 +2436,13 @@ png_do_unpack(png_row_infop row_info, png_bytep row) for (i = 0; i < row_width; i++) { *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) { shift = 0; sp--; } + else shift++; @@ -1537,6 +2450,7 @@ png_do_unpack(png_row_infop row_info, png_bytep row) } break; } + case 2: { @@ -1546,11 +2460,13 @@ png_do_unpack(png_row_infop row_info, png_bytep row) for (i = 0; i < row_width; i++) { *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) { shift = 0; sp--; } + else shift += 2; @@ -1558,6 +2474,7 @@ png_do_unpack(png_row_infop row_info, png_bytep row) } break; } + case 4: { png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); @@ -1566,11 +2483,13 @@ png_do_unpack(png_row_infop row_info, png_bytep row) for (i = 0; i < row_width; i++) { *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) { shift = 0; sp--; } + else shift = 4; @@ -1578,6 +2497,9 @@ png_do_unpack(png_row_infop row_info, png_bytep row) } break; } + + default: + break; } row_info->bit_depth = 8; row_info->pixel_depth = (png_byte)(8 * row_info->channels); @@ -1586,164 +2508,201 @@ png_do_unpack(png_row_infop row_info, png_bytep row) } #endif -#if defined(PNG_READ_SHIFT_SUPPORTED) +#ifdef PNG_READ_SHIFT_SUPPORTED /* Reverse the effects of png_do_shift. This routine merely shifts the * pixels back to their significant bits values. Thus, if you have * a row of bit depth 8, but only 5 are significant, this will shift * the values back to 0 through 31. */ void /* PRIVATE */ -png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +png_do_unshift(png_row_infop row_info, png_bytep row, + png_const_color_8p sig_bits) { - png_debug(1, "in png_do_unshift\n"); - if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && sig_bits != NULL && -#endif - row_info->color_type != PNG_COLOR_TYPE_PALETTE) + int color_type; + + png_debug(1, "in png_do_unshift"); + + /* The palette case has already been handled in the _init routine. */ + color_type = row_info->color_type; + + if (color_type != PNG_COLOR_TYPE_PALETTE) { int shift[4]; int channels = 0; - int c; - png_uint_16 value = 0; - png_uint_32 row_width = row_info->width; + int bit_depth = row_info->bit_depth; - if (row_info->color_type & PNG_COLOR_MASK_COLOR) + if (color_type & PNG_COLOR_MASK_COLOR) { - shift[channels++] = row_info->bit_depth - sig_bits->red; - shift[channels++] = row_info->bit_depth - sig_bits->green; - shift[channels++] = row_info->bit_depth - sig_bits->blue; + shift[channels++] = bit_depth - sig_bits->red; + shift[channels++] = bit_depth - sig_bits->green; + shift[channels++] = bit_depth - sig_bits->blue; } + else { - shift[channels++] = row_info->bit_depth - sig_bits->gray; - } - if (row_info->color_type & PNG_COLOR_MASK_ALPHA) - { - shift[channels++] = row_info->bit_depth - sig_bits->alpha; + shift[channels++] = bit_depth - sig_bits->gray; } - for (c = 0; c < channels; c++) + if (color_type & PNG_COLOR_MASK_ALPHA) { - if (shift[c] <= 0) - shift[c] = 0; - else - value = 1; + shift[channels++] = bit_depth - sig_bits->alpha; } - if (!value) - return; - - switch (row_info->bit_depth) { + int c, have_shift; + + for (c = have_shift = 0; c < channels; ++c) + { + /* A shift of more than the bit depth is an error condition but it + * gets ignored here. + */ + if (shift[c] <= 0 || shift[c] >= bit_depth) + shift[c] = 0; + + else + have_shift = 1; + } + + if (!have_shift) + return; + } + + switch (bit_depth) + { + default: + /* Must be 1bpp gray: should not be here! */ + /* NOTREACHED */ + break; + case 2: + /* Must be 2bpp gray */ + /* assert(channels == 1 && shift[0] == 1) */ { - png_bytep bp; - png_uint_32 i; - png_uint_32 istop = row_info->rowbytes; + png_bytep bp = row; + png_bytep bp_end = bp + row_info->rowbytes; - for (bp = row, i = 0; i < istop; i++) + while (bp < bp_end) { - *bp >>= 1; - *bp++ &= 0x55; + int b = (*bp >> 1) & 0x55; + *bp++ = (png_byte)b; } break; } + case 4: + /* Must be 4bpp gray */ + /* assert(channels == 1) */ { png_bytep bp = row; - png_uint_32 i; - png_uint_32 istop = row_info->rowbytes; - png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | - (png_byte)((int)0xf >> shift[0])); + png_bytep bp_end = bp + row_info->rowbytes; + int gray_shift = shift[0]; + int mask = 0xf >> gray_shift; - for (i = 0; i < istop; i++) + mask |= mask << 4; + + while (bp < bp_end) { - *bp >>= shift[0]; - *bp++ &= mask; + int b = (*bp >> gray_shift) & mask; + *bp++ = (png_byte)b; } break; } + case 8: + /* Single byte components, G, GA, RGB, RGBA */ { png_bytep bp = row; - png_uint_32 i; - png_uint_32 istop = row_width * channels; + png_bytep bp_end = bp + row_info->rowbytes; + int channel = 0; - for (i = 0; i < istop; i++) + while (bp < bp_end) { - *bp++ >>= shift[i%channels]; + int b = *bp >> shift[channel]; + if (++channel >= channels) + channel = 0; + *bp++ = (png_byte)b; } break; } + +#ifdef PNG_READ_16BIT_SUPPORTED case 16: + /* Double byte components, G, GA, RGB, RGBA */ { png_bytep bp = row; - png_uint_32 i; - png_uint_32 istop = channels * row_width; + png_bytep bp_end = bp + row_info->rowbytes; + int channel = 0; - for (i = 0; i < istop; i++) + while (bp < bp_end) { - value = (png_uint_16)((*bp << 8) + *(bp + 1)); - value >>= shift[i%channels]; + int value = (bp[0] << 8) + bp[1]; + + value >>= shift[channel]; + if (++channel >= channels) + channel = 0; *bp++ = (png_byte)(value >> 8); *bp++ = (png_byte)(value & 0xff); } break; } +#endif } } } #endif -#if defined(PNG_READ_16_TO_8_SUPPORTED) -/* chop rows of bit depth 16 down to 8 */ +#ifdef PNG_READ_SCALE_16_TO_8_SUPPORTED +/* Scale rows of bit depth 16 down to 8 accurately */ void /* PRIVATE */ -png_do_chop(png_row_infop row_info, png_bytep row) +png_do_scale_16_to_8(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_chop\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL && row_info->bit_depth == 16) -#else + png_debug(1, "in png_do_scale_16_to_8"); + if (row_info->bit_depth == 16) -#endif { - png_bytep sp = row; - png_bytep dp = row; - png_uint_32 i; - png_uint_32 istop = row_info->width * row_info->channels; + png_bytep sp = row; /* source */ + png_bytep dp = row; /* destination */ + png_bytep ep = sp + row_info->rowbytes; /* end+1 */ - 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: - */ + /* The input is an array of 16 bit components, these must be scaled to + * 8 bits each. For a 16 bit value V the required value (from the PNG + * specification) is: + * + * (V * 255) / 65535 + * + * This reduces to round(V / 257), or floor((V + 128.5)/257) + * + * Represent V as the two byte value vhi.vlo. Make a guess that the + * result is the top byte of V, vhi, then the correction to this value + * is: + * + * error = floor(((V-vhi.vhi) + 128.5) / 257) + * = floor(((vlo-vhi) + 128.5) / 257) + * + * This can be approximated using integer arithmetic (and a signed + * shift): + * + * error = (vlo-vhi+128) >> 8; + * + * The approximate differs from the exact answer only when (vlo-vhi) is + * 128; it then gives a correction of +1 when the exact correction is + * 0. This gives 128 errors. The exact answer (correct for all 16 bit + * input values) is: + * + * error = (vlo-vhi+128)*65535 >> 24; + * + * An alternative arithmetic calculation which also gives no errors is: + * + * (V * 255 + 32895) >> 16 + */ - *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); -#else - /* Simply discard the low order byte */ - *dp = *sp; -#endif + png_int_32 tmp = *sp++; /* must be signed! */ + tmp += (((int)*sp++ - tmp + 128) * 65535) >> 24; + *dp++ = (png_byte)tmp; } + row_info->bit_depth = 8; row_info->pixel_depth = (png_byte)(8 * row_info->channels); row_info->rowbytes = row_info->width * row_info->channels; @@ -1751,14 +2710,40 @@ png_do_chop(png_row_infop row_info, png_bytep row) } #endif -#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +#ifdef PNG_READ_STRIP_16_TO_8_SUPPORTED +void /* PRIVATE */ +/* Simply discard the low byte. This was the default behavior prior + * to libpng-1.5.4. + */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop"); + + if (row_info->bit_depth == 16) + { + png_bytep sp = row; /* source */ + png_bytep dp = row; /* destination */ + png_bytep ep = sp + row_info->rowbytes; /* end+1 */ + + while (sp < ep) + { + *dp++ = *sp; + sp += 2; /* skip low byte */ + } + + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#ifdef PNG_READ_SWAP_ALPHA_SUPPORTED void /* PRIVATE */ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_read_swap_alpha\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL) -#endif + png_debug(1, "in png_do_read_swap_alpha"); + { png_uint_32 row_width = row_info->width; if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) @@ -1780,6 +2765,8 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) *(--dp) = save; } } + +#ifdef PNG_READ_16BIT_SUPPORTED /* This converts from RRGGBBAA to AARRGGBB */ else { @@ -1802,7 +2789,9 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) *(--dp) = save[1]; } } +#endif } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { /* This converts from GA to AG */ @@ -1820,6 +2809,8 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) *(--dp) = save; } } + +#ifdef PNG_READ_16BIT_SUPPORTED /* This converts from GGAA to AAGG */ else { @@ -1838,133 +2829,137 @@ png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) *(--dp) = save[1]; } } +#endif } } } #endif -#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +#ifdef PNG_READ_INVERT_ALPHA_SUPPORTED void /* PRIVATE */ png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_read_invert_alpha\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL) -#endif + png_uint_32 row_width; + png_debug(1, "in png_do_read_invert_alpha"); + + row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - png_uint_32 row_width = row_info->width; - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + if (row_info->bit_depth == 8) { /* This inverts the alpha channel in RGBA */ - if (row_info->bit_depth == 8) + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; + *(--dp) = (png_byte)(255 - *(--sp)); - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - -/* This does nothing: - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - We can replace it with: +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: */ - sp-=3; - dp=sp; - } - } - /* This inverts the alpha channel in RRGGBBAA */ - else - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - *(--dp) = (png_byte)(255 - *(--sp)); - -/* This does nothing: - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - *(--dp) = *(--sp); - We can replace it with: -*/ - sp-=6; - dp=sp; - } + sp-=3; + dp=sp; } } - else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + +#ifdef PNG_READ_16BIT_SUPPORTED + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } +#endif + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) { /* This inverts the alpha channel in GA */ - if (row_info->bit_depth == 8) - { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - *(--dp) = *(--sp); - } - } - /* This inverts the alpha channel in GGAA */ - else + for (i = 0; i < row_width; i++) { - png_bytep sp = row + row_info->rowbytes; - png_bytep dp = sp; - png_uint_32 i; - - for (i = 0; i < row_width; i++) - { - *(--dp) = (png_byte)(255 - *(--sp)); - *(--dp) = (png_byte)(255 - *(--sp)); -/* - *(--dp) = *(--sp); - *(--dp) = *(--sp); -*/ - sp-=2; - dp=sp; - } + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); } } + +#ifdef PNG_READ_16BIT_SUPPORTED + else + { + /* This inverts the alpha channel in GGAA */ + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } +#endif } } #endif -#if defined(PNG_READ_FILLER_SUPPORTED) +#ifdef PNG_READ_FILLER_SUPPORTED /* Add filler channel if we have RGB color */ void /* PRIVATE */ png_do_read_filler(png_row_infop row_info, png_bytep row, - png_uint_32 filler, png_uint_32 flags) + png_uint_32 filler, png_uint_32 flags) { png_uint_32 i; png_uint_32 row_width = row_info->width; +#ifdef PNG_READ_16BIT_SUPPORTED png_byte hi_filler = (png_byte)((filler>>8) & 0xff); +#endif png_byte lo_filler = (png_byte)(filler & 0xff); - png_debug(1, "in png_do_read_filler\n"); + png_debug(1, "in png_do_read_filler"); + if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif row_info->color_type == PNG_COLOR_TYPE_GRAY) { - if(row_info->bit_depth == 8) + if (row_info->bit_depth == 8) { - /* This changes the data from G to GX */ if (flags & PNG_FLAG_FILLER_AFTER) { + /* This changes the data from G to GX */ png_bytep sp = row + (png_size_t)row_width; png_bytep dp = sp + (png_size_t)row_width; for (i = 1; i < row_width; i++) @@ -1977,9 +2972,10 @@ 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 { + /* This changes the data from G to XG */ png_bytep sp = row + (png_size_t)row_width; png_bytep dp = sp + (png_size_t)row_width; for (i = 0; i < row_width; i++) @@ -1992,11 +2988,13 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, row_info->rowbytes = row_width * 2; } } - else if(row_info->bit_depth == 16) + +#ifdef PNG_READ_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) { - /* This changes the data from GG to GGXX */ if (flags & PNG_FLAG_FILLER_AFTER) { + /* This changes the data from GG to GGXX */ png_bytep sp = row + (png_size_t)row_width * 2; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 1; i < row_width; i++) @@ -2012,9 +3010,10 @@ 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 { + /* This changes the data from GG to XXGG */ png_bytep sp = row + (png_size_t)row_width * 2; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) @@ -2029,14 +3028,15 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, row_info->rowbytes = row_width * 4; } } +#endif } /* COLOR_TYPE == GRAY */ else if (row_info->color_type == PNG_COLOR_TYPE_RGB) { - if(row_info->bit_depth == 8) + if (row_info->bit_depth == 8) { - /* This changes the data from RGB to RGBX */ if (flags & PNG_FLAG_FILLER_AFTER) { + /* This changes the data from RGB to RGBX */ png_bytep sp = row + (png_size_t)row_width * 3; png_bytep dp = sp + (png_size_t)row_width; for (i = 1; i < row_width; i++) @@ -2051,9 +3051,10 @@ 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 { + /* This changes the data from RGB to XRGB */ png_bytep sp = row + (png_size_t)row_width * 3; png_bytep dp = sp + (png_size_t)row_width; for (i = 0; i < row_width; i++) @@ -2068,11 +3069,13 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, row_info->rowbytes = row_width * 4; } } - else if(row_info->bit_depth == 16) + +#ifdef PNG_READ_16BIT_SUPPORTED + else if (row_info->bit_depth == 16) { - /* This changes the data from RRGGBB to RRGGBBXX */ if (flags & PNG_FLAG_FILLER_AFTER) { + /* This changes the data from RRGGBB to RRGGBBXX */ png_bytep sp = row + (png_size_t)row_width * 6; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 1; i < row_width; i++) @@ -2092,9 +3095,10 @@ 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 { + /* This changes the data from RRGGBB to XXRRGGBB */ png_bytep sp = row + (png_size_t)row_width * 6; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) @@ -2108,34 +3112,35 @@ png_do_read_filler(png_row_infop row_info, png_bytep row, *(--dp) = hi_filler; *(--dp) = lo_filler; } + row_info->channels = 4; row_info->pixel_depth = 64; row_info->rowbytes = row_width * 8; } } +#endif } /* COLOR_TYPE == RGB */ } #endif -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) -/* expand grayscale files to RGB, with or without alpha */ +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED +/* Expand grayscale files to RGB, with or without alpha */ void /* PRIVATE */ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) { png_uint_32 i; png_uint_32 row_width = row_info->width; - png_debug(1, "in png_do_gray_to_rgb\n"); + png_debug(1, "in png_do_gray_to_rgb"); + if (row_info->bit_depth >= 8 && -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) { if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { if (row_info->bit_depth == 8) { + /* This changes G to RGB */ png_bytep sp = row + (png_size_t)row_width - 1; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) @@ -2145,8 +3150,10 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) *(dp--) = *(sp--); } } + else { + /* This changes GG to RRGGBB */ png_bytep sp = row + (png_size_t)row_width * 2 - 1; png_bytep dp = sp + (png_size_t)row_width * 4; for (i = 0; i < row_width; i++) @@ -2160,10 +3167,12 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) } } } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { if (row_info->bit_depth == 8) { + /* This changes GA to RGBA */ png_bytep sp = row + (png_size_t)row_width * 2 - 1; png_bytep dp = sp + (png_size_t)row_width * 2; for (i = 0; i < row_width; i++) @@ -2174,8 +3183,10 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) *(dp--) = *(sp--); } } + else { + /* This changes GGAA to RRGGBBAA */ png_bytep sp = row + (png_size_t)row_width * 4 - 1; png_bytep dp = sp + (png_size_t)row_width * 4; for (i = 0; i < row_width; i++) @@ -2191,273 +3202,269 @@ png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) } } } - row_info->channels += (png_byte)2; + row_info->channels = (png_byte)(row_info->channels + 2); row_info->color_type |= PNG_COLOR_MASK_COLOR; row_info->pixel_depth = (png_byte)(row_info->channels * - row_info->bit_depth); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } } #endif -#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) -/* reduce RGB files to grayscale, with or without alpha - * using the equation given in Poynton's ColorFAQ at - * - * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED +/* Reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ of 1998-01-04 at + * (THIS LINK IS DEAD June 2008 but + * versions dated 1998 through November 2002 have been archived at + * http://web.archive.org/web/20000816232553/http://www.inforamp.net/ + * ~poynton/notes/colour_and_gamma/ColorFAQ.txt ) + * Charles Poynton poynton at poynton.com * * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B * - * We approximate this with - * - * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B - * * which can be expressed with integers as * * Y = (6969 * R + 23434 * G + 2365 * B)/32768 * - * The calculation is to be done in a linear colorspace. + * Poynton's current link (as of January 2003 through July 2011): + * + * has changed the numbers slightly: * - * Other integer coefficents can be used via png_set_rgb_to_gray(). + * Y = 0.2126*R + 0.7152*G + 0.0722*B + * + * which can be expressed with integers as + * + * Y = (6966 * R + 23436 * G + 2366 * B)/32768 + * + * Historically, however, libpng uses numbers derived from the ITU-R Rec 709 + * end point chromaticities and the D65 white point. Depending on the + * precision used for the D65 white point this produces a variety of different + * numbers, however if the four decimal place value used in ITU-R Rec 709 is + * used (0.3127,0.3290) the Y calculation would be: + * + * Y = (6968 * R + 23435 * G + 2366 * B)/32768 + * + * While this is correct the rounding results in an overflow for white, because + * the sum of the rounded coefficients is 32769, not 32768. Consequently + * libpng uses, instead, the closest non-overflowing approximation: + * + * Y = (6968 * R + 23434 * G + 2366 * B)/32768 + * + * Starting with libpng-1.5.5, if the image being converted has a cHRM chunk + * (including an sRGB chunk) then the chromaticities are used to calculate the + * coefficients. See the chunk handling in pngrutil.c for more information. + * + * In all cases the calculation is to be done in a linear colorspace. If no + * gamma information is available to correct the encoding of the original RGB + * values this results in an implicit assumption that the original PNG RGB + * values were linear. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). Because + * the API takes just red and green coefficients the blue coefficient is + * calculated to make the sum 32768. This will result in different rounding + * to that used above. */ int /* PRIVATE */ -png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) +png_do_rgb_to_gray(png_structrp png_ptr, png_row_infop row_info, png_bytep row) { - png_uint_32 i; - - png_uint_32 row_width = row_info->width; int rgb_error = 0; - png_debug(1, "in png_do_rgb_to_gray\n"); - if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - (row_info->color_type & PNG_COLOR_MASK_COLOR)) + png_debug(1, "in png_do_rgb_to_gray"); + + if (!(row_info->color_type & PNG_COLOR_MASK_PALETTE) && + (row_info->color_type & PNG_COLOR_MASK_COLOR)) { - png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; - png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; - png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + PNG_CONST png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + PNG_CONST png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + PNG_CONST png_uint_32 bc = 32768 - rc - gc; + PNG_CONST png_uint_32 row_width = row_info->width; + PNG_CONST int have_alpha = + (row_info->color_type & PNG_COLOR_MASK_ALPHA) != 0; - if (row_info->color_type == PNG_COLOR_TYPE_RGB) + if (row_info->bit_depth == 8) { - if (row_info->bit_depth == 8) +#ifdef PNG_READ_GAMMA_SUPPORTED + /* Notice that gamma to/from 1 are not necessarily inverses (if + * there is an overall gamma correction). Prior to 1.5.5 this code + * checked the linearized values for equality; this doesn't match + * the documentation, the original values must be checked. + */ + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) { -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) - { - png_bytep sp = row; - png_bytep dp = row; + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; - for (i = 0; i < row_width; i++) - { - png_byte red = png_ptr->gamma_to_1[*(sp++)]; - png_byte green = png_ptr->gamma_to_1[*(sp++)]; - png_byte blue = png_ptr->gamma_to_1[*(sp++)]; - if(red != green || red != blue) - { - rgb_error |= 1; - *(dp++) = png_ptr->gamma_from_1[ - (rc*red+gc*green+bc*blue)>>15]; - } - else - *(dp++) = *(sp-1); - } - } - else -#endif + for (i = 0; i < row_width; i++) { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + + if (red != green || red != blue) { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); - if(red != green || red != blue) - { - rgb_error |= 1; - *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>15); - } - else - *(dp++) = *(sp-1); + red = png_ptr->gamma_to_1[red]; + green = png_ptr->gamma_to_1[green]; + blue = png_ptr->gamma_to_1[blue]; + + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red + gc*green + bc*blue + 16384)>>15]; } + + else + { + /* If there is no overall correction the table will not be + * set. + */ + if (png_ptr->gamma_table != NULL) + red = png_ptr->gamma_table[red]; + + *(dp++) = red; + } + + if (have_alpha) + *(dp++) = *(sp++); } } - - else /* RGB bit_depth == 16 */ - { -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_16_to_1 != NULL && - png_ptr->gamma_16_from_1 != NULL) - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 red, green, blue, w; - - red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - - if(red == green && red == blue) - w = red; - else - { - png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> - png_ptr->gamma_shift][red>>8]; - png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> - png_ptr->gamma_shift][green>>8]; - png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> - png_ptr->gamma_shift][blue>>8]; - png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 - + bc*blue_1)>>15); - w = png_ptr->gamma_16_from_1[(gray16&0xff) >> - png_ptr->gamma_shift][gray16 >> 8]; - rgb_error |= 1; - } - - *(dp++) = (png_byte)((w>>8) & 0xff); - *(dp++) = (png_byte)(w & 0xff); - } - } - else + else #endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + + if (red != green || red != blue) { - png_uint_16 red, green, blue, gray16; - - red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - - if(red != green || red != blue) - rgb_error |= 1; - gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); - *(dp++) = (png_byte)((gray16>>8) & 0xff); - *(dp++) = (png_byte)(gray16 & 0xff); + rgb_error |= 1; + /* NOTE: this is the historical approach which simply + * truncates the results. + */ + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); } + + else + *(dp++) = red; + + if (have_alpha) + *(dp++) = *(sp++); } } } - if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + + else /* RGB bit_depth == 16 */ { - if (row_info->bit_depth == 8) +#ifdef PNG_READ_GAMMA_SUPPORTED + if (png_ptr->gamma_16_to_1 != NULL && png_ptr->gamma_16_from_1 != NULL) { -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_byte red = png_ptr->gamma_to_1[*(sp++)]; - png_byte green = png_ptr->gamma_to_1[*(sp++)]; - png_byte blue = png_ptr->gamma_to_1[*(sp++)]; - if(red != green || red != blue) - rgb_error |= 1; - *(dp++) = png_ptr->gamma_from_1 - [(rc*red + gc*green + bc*blue)>>15]; - *(dp++) = *(sp++); /* alpha */ - } - } - else -#endif - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_byte red = *(sp++); - png_byte green = *(sp++); - png_byte blue = *(sp++); - if(red != green || red != blue) - rgb_error |= 1; - *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); - *(dp++) = *(sp++); /* alpha */ - } - } - } - else /* RGBA bit_depth == 16 */ - { -#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->gamma_16_to_1 != NULL && - png_ptr->gamma_16_from_1 != NULL) - { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) - { - png_uint_16 red, green, blue, w; + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; - red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; - blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + + if (red == green && red == blue) + { + if (png_ptr->gamma_16_table != NULL) + w = png_ptr->gamma_16_table[(red&0xff) + >> png_ptr->gamma_shift][red>>8]; - if(red == green && red == blue) - w = red; else - { - png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> - png_ptr->gamma_shift][red>>8]; - png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> - png_ptr->gamma_shift][green>>8]; - png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> - png_ptr->gamma_shift][blue>>8]; - png_uint_16 gray16 = (png_uint_16)((rc * red_1 - + gc * green_1 + bc * blue_1)>>15); - w = png_ptr->gamma_16_from_1[(gray16&0xff) >> - png_ptr->gamma_shift][gray16 >> 8]; - rgb_error |= 1; - } + w = red; + } - *(dp++) = (png_byte)((w>>8) & 0xff); - *(dp++) = (png_byte)(w & 0xff); - *(dp++) = *(sp++); /* alpha */ + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) + >> png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = + png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) + >> png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1 + 16384)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + + if (have_alpha) + { + *(dp++) = *(sp++); *(dp++) = *(sp++); } } - else + } + else #endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + + for (i = 0; i < row_width; i++) { - png_bytep sp = row; - png_bytep dp = row; - for (i = 0; i < row_width; i++) + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + green = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp + 1)); sp += 2; + + if (red != green || red != blue) + rgb_error |= 1; + + /* From 1.5.5 in the 16 bit case do the accurate conversion even + * in the 'fast' case - this is because this is where the code + * ends up when handling linear 16 bit data. + */ + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue + 16384) >> + 15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + + if (have_alpha) { - png_uint_16 red, green, blue, gray16; - red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; - green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; - blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; - if(red != green || red != blue) - rgb_error |= 1; - gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); - *(dp++) = (png_byte)((gray16>>8) & 0xff); - *(dp++) = (png_byte)(gray16 & 0xff); - *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); *(dp++) = *(sp++); } } } } - row_info->channels -= (png_byte)2; - row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + + row_info->channels = (png_byte)(row_info->channels - 2); + row_info->color_type = (png_byte)(row_info->color_type & + ~PNG_COLOR_MASK_COLOR); row_info->pixel_depth = (png_byte)(row_info->channels * - row_info->bit_depth); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } return rgb_error; } #endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ +#ifdef PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED /* 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. + * of code. This API is not used internally. */ void PNGAPI png_build_grayscale_palette(int bit_depth, png_colorp palette) @@ -2467,7 +3474,8 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette) int i; int v; - png_debug(1, "in png_do_build_grayscale_palette\n"); + png_debug(1, "in png_do_build_grayscale_palette"); + if (palette == NULL) return; @@ -2477,18 +3485,22 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette) num_palette = 2; color_inc = 0xff; break; + case 2: num_palette = 4; color_inc = 0x55; break; + case 4: num_palette = 16; color_inc = 0x11; break; + case 8: num_palette = 256; color_inc = 1; break; + default: num_palette = 0; color_inc = 0; @@ -2502,217 +3514,37 @@ png_build_grayscale_palette(int bit_depth, png_colorp palette) palette[i].blue = (png_byte)v; } } - -/* This function is currently unused. Do we really need it? */ -#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED) -void /* PRIVATE */ -png_correct_palette(png_structp png_ptr, png_colorp palette, - int num_palette) -{ - png_debug(1, "in png_correct_palette\n"); -#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ - defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) - if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND)) - { - png_color back, back_1; - - if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) - { - back.red = png_ptr->gamma_table[png_ptr->background.red]; - back.green = png_ptr->gamma_table[png_ptr->background.green]; - back.blue = png_ptr->gamma_table[png_ptr->background.blue]; - - back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; - back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; - back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; - } - else - { - double g; - - g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); - - if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN || - fabs(g - 1.0) < PNG_GAMMA_THRESHOLD) - { - back.red = png_ptr->background.red; - back.green = png_ptr->background.green; - back.blue = png_ptr->background.blue; - } - else - { - back.red = - (png_byte)(pow((double)png_ptr->background.red/255, g) * - 255.0 + 0.5); - back.green = - (png_byte)(pow((double)png_ptr->background.green/255, g) * - 255.0 + 0.5); - back.blue = - (png_byte)(pow((double)png_ptr->background.blue/255, g) * - 255.0 + 0.5); - } - - g = 1.0 / png_ptr->background_gamma; - - back_1.red = - (png_byte)(pow((double)png_ptr->background.red/255, g) * - 255.0 + 0.5); - back_1.green = - (png_byte)(pow((double)png_ptr->background.green/255, g) * - 255.0 + 0.5); - back_1.blue = - (png_byte)(pow((double)png_ptr->background.blue/255, g) * - 255.0 + 0.5); - } - - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - png_uint_32 i; - - for (i = 0; i < (png_uint_32)num_palette; i++) - { - if (i < png_ptr->num_trans && png_ptr->trans[i] == 0) - { - palette[i] = back; - } - else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff) - { - png_byte v, w; - - v = png_ptr->gamma_to_1[png_ptr->palette[i].red]; - png_composite(w, v, png_ptr->trans[i], back_1.red); - palette[i].red = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[png_ptr->palette[i].green]; - png_composite(w, v, png_ptr->trans[i], back_1.green); - palette[i].green = png_ptr->gamma_from_1[w]; - - v = png_ptr->gamma_to_1[png_ptr->palette[i].blue]; - png_composite(w, v, png_ptr->trans[i], back_1.blue); - palette[i].blue = png_ptr->gamma_from_1[w]; - } - else - { - palette[i].red = png_ptr->gamma_table[palette[i].red]; - palette[i].green = png_ptr->gamma_table[palette[i].green]; - palette[i].blue = png_ptr->gamma_table[palette[i].blue]; - } - } - } - else - { - int i; - - for (i = 0; i < num_palette; i++) - { - if (palette[i].red == (png_byte)png_ptr->trans_values.gray) - { - palette[i] = back; - } - else - { - palette[i].red = png_ptr->gamma_table[palette[i].red]; - palette[i].green = png_ptr->gamma_table[palette[i].green]; - palette[i].blue = png_ptr->gamma_table[palette[i].blue]; - } - } - } - } - else -#endif -#if defined(PNG_READ_GAMMA_SUPPORTED) - if (png_ptr->transformations & PNG_GAMMA) - { - int i; - - for (i = 0; i < num_palette; i++) - { - palette[i].red = png_ptr->gamma_table[palette[i].red]; - palette[i].green = png_ptr->gamma_table[palette[i].green]; - palette[i].blue = png_ptr->gamma_table[palette[i].blue]; - } - } -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - else -#endif -#endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) - if (png_ptr->transformations & PNG_BACKGROUND) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - png_color back; - - back.red = (png_byte)png_ptr->background.red; - back.green = (png_byte)png_ptr->background.green; - back.blue = (png_byte)png_ptr->background.blue; - - for (i = 0; i < (int)png_ptr->num_trans; i++) - { - if (png_ptr->trans[i] == 0) - { - palette[i].red = back.red; - palette[i].green = back.green; - palette[i].blue = back.blue; - } - else if (png_ptr->trans[i] != 0xff) - { - png_composite(palette[i].red, png_ptr->palette[i].red, - png_ptr->trans[i], back.red); - png_composite(palette[i].green, png_ptr->palette[i].green, - png_ptr->trans[i], back.green); - png_composite(palette[i].blue, png_ptr->palette[i].blue, - png_ptr->trans[i], back.blue); - } - } - } - else /* assume grayscale palette (what else could it be?) */ - { - int i; - - for (i = 0; i < num_palette; i++) - { - if (i == (png_byte)png_ptr->trans_values.gray) - { - palette[i].red = (png_byte)png_ptr->background.red; - palette[i].green = (png_byte)png_ptr->background.green; - palette[i].blue = (png_byte)png_ptr->background.blue; - } - } - } - } -#endif -} #endif -#if defined(PNG_READ_BACKGROUND_SUPPORTED) + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +#if defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) /* Replace any alpha or transparency with the supplied background color. * "background" is already in the screen gamma, while "background_1" is * at a gamma of 1.0. Paletted files have already been taken care of. */ void /* PRIVATE */ -png_do_background(png_row_infop row_info, png_bytep row, - png_color_16p trans_values, png_color_16p background -#if defined(PNG_READ_GAMMA_SUPPORTED) - , png_color_16p background_1, - png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, - png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, - png_uint_16pp gamma_16_to_1, int gamma_shift -#endif - ) +png_do_compose(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { - png_bytep sp, dp; +#ifdef PNG_READ_GAMMA_SUPPORTED + png_const_bytep gamma_table = png_ptr->gamma_table; + png_const_bytep gamma_from_1 = png_ptr->gamma_from_1; + png_const_bytep gamma_to_1 = png_ptr->gamma_to_1; + png_const_uint_16pp gamma_16 = png_ptr->gamma_16_table; + png_const_uint_16pp gamma_16_from_1 = png_ptr->gamma_16_from_1; + png_const_uint_16pp gamma_16_to_1 = png_ptr->gamma_16_to_1; + int gamma_shift = png_ptr->gamma_shift; + int optimize = (png_ptr->flags & PNG_FLAG_OPTIMIZE_ALPHA) != 0; +#endif + + png_bytep sp; png_uint_32 i; - png_uint_32 row_width=row_info->width; + png_uint_32 row_width = row_info->width; int shift; - png_debug(1, "in png_do_background\n"); - if (background != NULL && -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || - (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values))) + png_debug(1, "in png_do_compose"); + { switch (row_info->color_type) { @@ -2727,24 +3559,28 @@ png_do_background(png_row_infop row_info, png_bytep row, for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x01) - == trans_values->gray) + == png_ptr->trans_color.gray) { - *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); - *sp |= (png_byte)(background->gray << shift); + unsigned int tmp = *sp & (0x7f7f >> (7 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); } + if (!shift) { shift = 7; sp++; } + else shift--; } break; } + case 2: { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; @@ -2752,28 +3588,34 @@ png_do_background(png_row_infop row_info, png_bytep row, for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x03) - == trans_values->gray) + == png_ptr->trans_color.gray) { - *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); - *sp |= (png_byte)(background->gray << shift); + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); } + else { - png_byte p = (png_byte)((*sp >> shift) & 0x03); - png_byte g = (png_byte)((gamma_table [p | (p << 2) | - (p << 4) | (p << 6)] >> 6) & 0x03); - *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); - *sp |= (png_byte)(g << shift); + unsigned int p = (*sp >> shift) & 0x03; + unsigned int g = (gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03; + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= g << shift; + *sp = (png_byte)(tmp & 0xff); } + if (!shift) { shift = 6; sp++; } + else shift -= 2; } } + else #endif { @@ -2782,25 +3624,29 @@ png_do_background(png_row_infop row_info, png_bytep row, for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x03) - == trans_values->gray) + == png_ptr->trans_color.gray) { - *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); - *sp |= (png_byte)(background->gray << shift); + unsigned int tmp = *sp & (0x3f3f >> (6 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); } + if (!shift) { shift = 6; sp++; } + else shift -= 2; } } break; } + case 4: { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; @@ -2808,28 +3654,34 @@ png_do_background(png_row_infop row_info, png_bytep row, for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x0f) - == trans_values->gray) + == png_ptr->trans_color.gray) { - *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); - *sp |= (png_byte)(background->gray << shift); + unsigned int tmp = *sp & (0xf0f >> (4 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); } + else { - png_byte p = (png_byte)((*sp >> shift) & 0x0f); - png_byte g = (png_byte)((gamma_table[p | - (p << 4)] >> 4) & 0x0f); - *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); - *sp |= (png_byte)(g << shift); + unsigned int p = (*sp >> shift) & 0x0f; + unsigned int g = (gamma_table[p | (p << 4)] >> 4) & + 0x0f; + unsigned int tmp = *sp & (0xf0f >> (4 - shift)); + tmp |= g << shift; + *sp = (png_byte)(tmp & 0xff); } + if (!shift) { shift = 4; sp++; } + else shift -= 4; } } + else #endif { @@ -2838,38 +3690,39 @@ png_do_background(png_row_infop row_info, png_bytep row, for (i = 0; i < row_width; i++) { if ((png_uint_16)((*sp >> shift) & 0x0f) - == trans_values->gray) + == png_ptr->trans_color.gray) { - *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); - *sp |= (png_byte)(background->gray << shift); + unsigned int tmp = *sp & (0xf0f >> (4 - shift)); + tmp |= png_ptr->background.gray << shift; + *sp = (png_byte)(tmp & 0xff); } + if (!shift) { shift = 4; sp++; } + else shift -= 4; } } break; } + case 8: { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; for (i = 0; i < row_width; i++, sp++) { - if (*sp == trans_values->gray) - { - *sp = (png_byte)background->gray; - } + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; + else - { *sp = gamma_table[*sp]; - } } } else @@ -2878,17 +3731,16 @@ png_do_background(png_row_infop row_info, png_bytep row, sp = row; for (i = 0; i < row_width; i++, sp++) { - if (*sp == trans_values->gray) - { - *sp = (png_byte)background->gray; - } + if (*sp == png_ptr->trans_color.gray) + *sp = (png_byte)png_ptr->background.gray; } } break; } + case 16: { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL) { sp = row; @@ -2897,12 +3749,16 @@ png_do_background(png_row_infop row_info, png_bytep row, png_uint_16 v; v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - if (v == trans_values->gray) + + if (v == png_ptr->trans_color.gray) { - /* background is already in screen gamma */ - *sp = (png_byte)((background->gray >> 8) & 0xff); - *(sp + 1) = (png_byte)(background->gray & 0xff); + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); } + else { v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; @@ -2920,36 +3776,44 @@ png_do_background(png_row_infop row_info, png_bytep row, png_uint_16 v; v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - if (v == trans_values->gray) + + if (v == png_ptr->trans_color.gray) { - *sp = (png_byte)((background->gray >> 8) & 0xff); - *(sp + 1) = (png_byte)(background->gray & 0xff); + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray + & 0xff); } } } break; } + + default: + break; } break; } + case PNG_COLOR_TYPE_RGB: { if (row_info->bit_depth == 8) { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_table != NULL) { sp = row; for (i = 0; i < row_width; i++, sp += 3) { - if (*sp == trans_values->red && - *(sp + 1) == trans_values->green && - *(sp + 2) == trans_values->blue) + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) { - *sp = (png_byte)background->red; - *(sp + 1) = (png_byte)background->green; - *(sp + 2) = (png_byte)background->blue; + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; } + else { *sp = gamma_table[*sp]; @@ -2964,108 +3828,131 @@ png_do_background(png_row_infop row_info, png_bytep row, sp = row; for (i = 0; i < row_width; i++, sp += 3) { - if (*sp == trans_values->red && - *(sp + 1) == trans_values->green && - *(sp + 2) == trans_values->blue) + if (*sp == png_ptr->trans_color.red && + *(sp + 1) == png_ptr->trans_color.green && + *(sp + 2) == png_ptr->trans_color.blue) { - *sp = (png_byte)background->red; - *(sp + 1) = (png_byte)background->green; - *(sp + 2) = (png_byte)background->blue; + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; } } } } else /* if (row_info->bit_depth == 16) */ { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL) { sp = row; for (i = 0; i < row_width; i++, sp += 6) { png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); - png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); - if (r == trans_values->red && g == trans_values->green && - b == trans_values->blue) + + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) { - /* background is already in screen gamma */ - *sp = (png_byte)((background->red >> 8) & 0xff); - *(sp + 1) = (png_byte)(background->red & 0xff); - *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); - *(sp + 3) = (png_byte)(background->green & 0xff); - *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); - *(sp + 5) = (png_byte)(background->blue & 0xff); + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); } + else { png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; *(sp + 2) = (png_byte)((v >> 8) & 0xff); *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; *(sp + 4) = (png_byte)((v >> 8) & 0xff); *(sp + 5) = (png_byte)(v & 0xff); } } } + else #endif { sp = row; for (i = 0; i < row_width; i++, sp += 6) { - png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); - png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); - png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - if (r == trans_values->red && g == trans_values->green && - b == trans_values->blue) + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + if (r == png_ptr->trans_color.red && + g == png_ptr->trans_color.green && + b == png_ptr->trans_color.blue) { - *sp = (png_byte)((background->red >> 8) & 0xff); - *(sp + 1) = (png_byte)(background->red & 0xff); - *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); - *(sp + 3) = (png_byte)(background->green & 0xff); - *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); - *(sp + 5) = (png_byte)(background->blue & 0xff); + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); } } } } break; } + case PNG_COLOR_TYPE_GRAY_ALPHA: { if (row_info->bit_depth == 8) { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_to_1 != NULL && gamma_from_1 != NULL && gamma_table != NULL) { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 2, dp++) + for (i = 0; i < row_width; i++, sp += 2) { png_uint_16 a = *(sp + 1); if (a == 0xff) - { - *dp = gamma_table[*sp]; - } + *sp = gamma_table[*sp]; + else if (a == 0) { - /* background is already in screen gamma */ - *dp = (png_byte)background->gray; + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.gray; } + else { png_byte v, w; v = gamma_to_1[*sp]; - png_composite(w, v, a, background_1->gray); - *dp = gamma_from_1[w]; + png_composite(w, v, a, png_ptr->background_1.gray); + if (!optimize) + w = gamma_from_1[w]; + *sp = w; } } } @@ -3073,151 +3960,139 @@ png_do_background(png_row_infop row_info, png_bytep row, #endif { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 2, dp++) + for (i = 0; i < row_width; i++, sp += 2) { png_byte a = *(sp + 1); - if (a == 0xff) - { - *dp = *sp; - } -#if defined(PNG_READ_GAMMA_SUPPORTED) - else if (a == 0) - { - *dp = (png_byte)background->gray; - } - else - { - png_composite(*dp, *sp, a, background_1->gray); - } -#else - *dp = (png_byte)background->gray; -#endif + if (a == 0) + *sp = (png_byte)png_ptr->background.gray; + + else if (a < 0xff) + png_composite(*sp, *sp, a, png_ptr->background.gray); } } } else /* if (png_ptr->bit_depth == 16) */ { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL && gamma_16_from_1 != NULL && gamma_16_to_1 != NULL) { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 4, dp += 2) + for (i = 0; i < row_width; i++, sp += 4) { - png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); if (a == (png_uint_16)0xffff) { png_uint_16 v; v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; - *dp = (png_byte)((v >> 8) & 0xff); - *(dp + 1) = (png_byte)(v & 0xff); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); } -#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) -#else - else -#endif { - /* background is already in screen gamma */ - *dp = (png_byte)((background->gray >> 8) & 0xff); - *(dp + 1) = (png_byte)(background->gray & 0xff); + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); } -#if defined(PNG_READ_GAMMA_SUPPORTED) + else { png_uint_16 g, v, w; g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; - png_composite_16(v, g, a, background_1->gray); - w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; - *dp = (png_byte)((w >> 8) & 0xff); - *(dp + 1) = (png_byte)(w & 0xff); + png_composite_16(v, g, a, png_ptr->background_1.gray); + if (optimize) + w = v; + else + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); } -#endif } } else #endif { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 4, dp += 2) + for (i = 0; i < row_width; i++, sp += 4) { - png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); - if (a == (png_uint_16)0xffff) + png_uint_16 a = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + + if (a == 0) { - png_memcpy(dp, sp, 2); + *sp = (png_byte)((png_ptr->background.gray >> 8) + & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.gray & 0xff); } -#if defined(PNG_READ_GAMMA_SUPPORTED) - else if (a == 0) -#else - else -#endif - { - *dp = (png_byte)((background->gray >> 8) & 0xff); - *(dp + 1) = (png_byte)(background->gray & 0xff); - } -#if defined(PNG_READ_GAMMA_SUPPORTED) - else + + else if (a < 0xffff) { png_uint_16 g, v; g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); - png_composite_16(v, g, a, background_1->gray); - *dp = (png_byte)((v >> 8) & 0xff); - *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, png_ptr->background.gray); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); } -#endif } } } break; } + case PNG_COLOR_TYPE_RGB_ALPHA: { if (row_info->bit_depth == 8) { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_to_1 != NULL && gamma_from_1 != NULL && gamma_table != NULL) { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 4, dp += 3) + for (i = 0; i < row_width; i++, sp += 4) { png_byte a = *(sp + 3); if (a == 0xff) { - *dp = gamma_table[*sp]; - *(dp + 1) = gamma_table[*(sp + 1)]; - *(dp + 2) = gamma_table[*(sp + 2)]; + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; } + else if (a == 0) { - /* background is already in screen gamma */ - *dp = (png_byte)background->red; - *(dp + 1) = (png_byte)background->green; - *(dp + 2) = (png_byte)background->blue; + /* Background is already in screen gamma */ + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; } + else { png_byte v, w; v = gamma_to_1[*sp]; - png_composite(w, v, a, background_1->red); - *dp = gamma_from_1[w]; + png_composite(w, v, a, png_ptr->background_1.red); + if (!optimize) w = gamma_from_1[w]; + *sp = w; + v = gamma_to_1[*(sp + 1)]; - png_composite(w, v, a, background_1->green); - *(dp + 1) = gamma_from_1[w]; + png_composite(w, v, a, png_ptr->background_1.green); + if (!optimize) w = gamma_from_1[w]; + *(sp + 1) = w; + v = gamma_to_1[*(sp + 2)]; - png_composite(w, v, a, background_1->blue); - *(dp + 2) = gamma_from_1[w]; + png_composite(w, v, a, png_ptr->background_1.blue); + if (!optimize) w = gamma_from_1[w]; + *(sp + 2) = w; } } } @@ -3225,115 +4100,129 @@ png_do_background(png_row_infop row_info, png_bytep row, #endif { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 4, dp += 3) + for (i = 0; i < row_width; i++, sp += 4) { png_byte a = *(sp + 3); - if (a == 0xff) + if (a == 0) { - *dp = *sp; - *(dp + 1) = *(sp + 1); - *(dp + 2) = *(sp + 2); + *sp = (png_byte)png_ptr->background.red; + *(sp + 1) = (png_byte)png_ptr->background.green; + *(sp + 2) = (png_byte)png_ptr->background.blue; } - else if (a == 0) + + else if (a < 0xff) { - *dp = (png_byte)background->red; - *(dp + 1) = (png_byte)background->green; - *(dp + 2) = (png_byte)background->blue; - } - else - { - png_composite(*dp, *sp, a, background->red); - png_composite(*(dp + 1), *(sp + 1), a, - background->green); - png_composite(*(dp + 2), *(sp + 2), a, - background->blue); + png_composite(*sp, *sp, a, png_ptr->background.red); + + png_composite(*(sp + 1), *(sp + 1), a, + png_ptr->background.green); + + png_composite(*(sp + 2), *(sp + 2), a, + png_ptr->background.blue); } } } } else /* if (row_info->bit_depth == 16) */ { -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED if (gamma_16 != NULL && gamma_16_from_1 != NULL && gamma_16_to_1 != NULL) { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 8, dp += 6) + for (i = 0; i < row_width; i++, sp += 8) { png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) { png_uint_16 v; v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; - *dp = (png_byte)((v >> 8) & 0xff); - *(dp + 1) = (png_byte)(v & 0xff); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; - *(dp + 2) = (png_byte)((v >> 8) & 0xff); - *(dp + 3) = (png_byte)(v & 0xff); + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; - *(dp + 4) = (png_byte)((v >> 8) & 0xff); - *(dp + 5) = (png_byte)(v & 0xff); + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); } + else if (a == 0) { - /* background is already in screen gamma */ - *dp = (png_byte)((background->red >> 8) & 0xff); - *(dp + 1) = (png_byte)(background->red & 0xff); - *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); - *(dp + 3) = (png_byte)(background->green & 0xff); - *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); - *(dp + 5) = (png_byte)(background->blue & 0xff); + /* Background is already in screen gamma */ + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); } + else { - png_uint_16 v, w, x; + png_uint_16 v, w; v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; - png_composite_16(w, v, a, background_1->red); - x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; - *dp = (png_byte)((x >> 8) & 0xff); - *(dp + 1) = (png_byte)(x & 0xff); + png_composite_16(w, v, a, png_ptr->background_1.red); + if (!optimize) + w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> + 8]; + *sp = (png_byte)((w >> 8) & 0xff); + *(sp + 1) = (png_byte)(w & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; - png_composite_16(w, v, a, background_1->green); - x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; - *(dp + 2) = (png_byte)((x >> 8) & 0xff); - *(dp + 3) = (png_byte)(x & 0xff); + png_composite_16(w, v, a, png_ptr->background_1.green); + if (!optimize) + w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 2) = (png_byte)((w >> 8) & 0xff); + *(sp + 3) = (png_byte)(w & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; - png_composite_16(w, v, a, background_1->blue); - x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; - *(dp + 4) = (png_byte)((x >> 8) & 0xff); - *(dp + 5) = (png_byte)(x & 0xff); + png_composite_16(w, v, a, png_ptr->background_1.blue); + if (!optimize) + w = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> + 8]; + + *(sp + 4) = (png_byte)((w >> 8) & 0xff); + *(sp + 5) = (png_byte)(w & 0xff); } } } + else #endif { sp = row; - dp = row; - for (i = 0; i < row_width; i++, sp += 8, dp += 6) + for (i = 0; i < row_width; i++, sp += 8) { png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) - << 8) + (png_uint_16)(*(sp + 7))); - if (a == (png_uint_16)0xffff) + << 8) + (png_uint_16)(*(sp + 7))); + + if (a == 0) { - png_memcpy(dp, sp, 6); + *sp = (png_byte)((png_ptr->background.red >> 8) & 0xff); + *(sp + 1) = (png_byte)(png_ptr->background.red & 0xff); + *(sp + 2) = (png_byte)((png_ptr->background.green >> 8) + & 0xff); + *(sp + 3) = (png_byte)(png_ptr->background.green + & 0xff); + *(sp + 4) = (png_byte)((png_ptr->background.blue >> 8) + & 0xff); + *(sp + 5) = (png_byte)(png_ptr->background.blue & 0xff); } - else if (a == 0) - { - *dp = (png_byte)((background->red >> 8) & 0xff); - *(dp + 1) = (png_byte)(background->red & 0xff); - *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); - *(dp + 3) = (png_byte)(background->green & 0xff); - *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); - *(dp + 5) = (png_byte)(background->blue & 0xff); - } - else + + else if (a < 0xffff) { png_uint_16 v; @@ -3343,107 +4232,62 @@ png_do_background(png_row_infop row_info, png_bytep row, png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + *(sp + 5)); - png_composite_16(v, r, a, background->red); - *dp = (png_byte)((v >> 8) & 0xff); - *(dp + 1) = (png_byte)(v & 0xff); - png_composite_16(v, g, a, background->green); - *(dp + 2) = (png_byte)((v >> 8) & 0xff); - *(dp + 3) = (png_byte)(v & 0xff); - png_composite_16(v, b, a, background->blue); - *(dp + 4) = (png_byte)((v >> 8) & 0xff); - *(dp + 5) = (png_byte)(v & 0xff); + png_composite_16(v, r, a, png_ptr->background.red); + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + + png_composite_16(v, g, a, png_ptr->background.green); + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + + png_composite_16(v, b, a, png_ptr->background.blue); + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); } } } } break; } - } - if (row_info->color_type & PNG_COLOR_MASK_ALPHA) - { - row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; - row_info->channels--; - row_info->pixel_depth = (png_byte)(row_info->channels * - row_info->bit_depth); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + default: + break; } } } -#endif +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_READ_ALPHA_MODE_SUPPORTED */ -#if defined(PNG_READ_GAMMA_SUPPORTED) +#ifdef PNG_READ_GAMMA_SUPPORTED /* Gamma correct the image, avoiding the alpha channel. Make sure * you do this after you deal with the transparency issue on grayscale * or RGB images. If your bit depth is 8, use gamma_table, if it * is 16, use gamma_16_table and gamma_shift. Build these with * build_gamma_table(). */ - -// (Note: the reason I've pulled this block of code out of png_do_gamma and into its own function -// is because the android NDK compiler was crashing when it tried to compile it) -static void doGamma_RGB (png_uint_32 row_width, png_row_infop row_info, png_bytep row, png_bytep gamma_table, png_uint_16pp gamma_16_table, int gamma_shift) -{ - if (row_info->bit_depth == 8) - { - png_bytep sp = row; - for (png_uint_32 i = 0; i < row_width; i++) - { - *sp = gamma_table[*sp]; - sp++; - *sp = gamma_table[*sp]; - sp++; - *sp = gamma_table[*sp]; - sp++; - } - } - else /* if (row_info->bit_depth == 16) */ - { - png_bytep sp = row; - for (png_uint_32 i = 0; i < row_width; i++) - { - png_uint_16 v; - - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; - *sp = (png_byte)((v >> 8) & 0xff); - *(sp + 1) = (png_byte)(v & 0xff); - sp += 2; - } - } -} - void /* PRIVATE */ -png_do_gamma(png_row_infop row_info, png_bytep row, - png_bytep gamma_table, png_uint_16pp gamma_16_table, - int gamma_shift) +png_do_gamma(png_row_infop row_info, png_bytep row, png_structrp png_ptr) { - png_bytep sp; - const png_uint_32 row_width=row_info->width; + png_const_bytep gamma_table = png_ptr->gamma_table; + png_const_uint_16pp gamma_16_table = png_ptr->gamma_16_table; + int gamma_shift = png_ptr->gamma_shift; + + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma"); - png_debug(1, "in png_do_gamma\n"); if (((row_info->bit_depth <= 8 && gamma_table != NULL) || - (row_info->bit_depth == 16 && gamma_16_table != NULL))) + (row_info->bit_depth == 16 && gamma_16_table != NULL))) { switch (row_info->color_type) { - case PNG_COLOR_TYPE_RGB: - doGamma_RGB (row_width, row_info, row, gamma_table, gamma_16_table, gamma_shift); - break; - - case PNG_COLOR_TYPE_RGB_ALPHA: + case PNG_COLOR_TYPE_RGB: { if (row_info->bit_depth == 8) { sp = row; - for (png_uint_32 i = 0; i < row_width; i++) + for (i = 0; i < row_width; i++) { *sp = gamma_table[*sp]; sp++; @@ -3451,22 +4295,70 @@ png_do_gamma(png_row_infop row_info, png_bytep row, sp++; *sp = gamma_table[*sp]; sp++; - sp++; } } + else /* if (row_info->bit_depth == 16) */ { sp = row; - for (png_uint_32 i = 0; i < row_width; i++) + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + + *sp = gamma_table[*sp]; + sp++; + + *sp = gamma_table[*sp]; + sp++; + + sp++; + } + } + + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) { png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); *(sp + 1) = (png_byte)(v & 0xff); @@ -3475,21 +4367,23 @@ png_do_gamma(png_row_infop row_info, png_bytep row, } break; } + case PNG_COLOR_TYPE_GRAY_ALPHA: { if (row_info->bit_depth == 8) { sp = row; - for (png_uint_32 i = 0; i < row_width; i++) + for (i = 0; i < row_width; i++) { *sp = gamma_table[*sp]; sp += 2; } } + else /* if (row_info->bit_depth == 16) */ { sp = row; - for (png_uint_32 i = 0; i < row_width; i++) + for (i = 0; i < row_width; i++) { png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); @@ -3499,12 +4393,13 @@ png_do_gamma(png_row_infop row_info, png_bytep row, } break; } + case PNG_COLOR_TYPE_GRAY: { if (row_info->bit_depth == 2) { sp = row; - for (png_uint_32 i = 0; i < row_width; i += 4) + for (i = 0; i < row_width; i += 4) { int a = *sp & 0xc0; int b = *sp & 0x30; @@ -3512,39 +4407,42 @@ png_do_gamma(png_row_infop row_info, png_bytep row, int d = *sp & 0x03; *sp = (png_byte)( - ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| - ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| - ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| - ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); sp++; } } + if (row_info->bit_depth == 4) { sp = row; - for (png_uint_32 i = 0; i < row_width; i += 2) + for (i = 0; i < row_width; i += 2) { int msb = *sp & 0xf0; int lsb = *sp & 0x0f; *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) - | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); sp++; } } + else if (row_info->bit_depth == 8) { sp = row; - for (png_uint_32 i = 0; i < row_width; i++) + for (i = 0; i < row_width; i++) { *sp = gamma_table[*sp]; sp++; } } + else if (row_info->bit_depth == 16) { sp = row; - for (png_uint_32 i = 0; i < row_width; i++) + for (i = 0; i < row_width; i++) { png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; *sp = (png_byte)((v >> 8) & 0xff); @@ -3554,30 +4452,97 @@ png_do_gamma(png_row_infop row_info, png_bytep row, } break; } + + default: + break; } } } #endif -#if defined(PNG_READ_EXPAND_SUPPORTED) +#ifdef PNG_READ_ALPHA_MODE_SUPPORTED +/* Encode the alpha channel to the output gamma (the input channel is always + * linear.) Called only with color types that have an alpha channel. Needs the + * from_1 tables. + */ +void /* PRIVATE */ +png_do_encode_alpha(png_row_infop row_info, png_bytep row, png_structrp png_ptr) +{ + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_encode_alpha"); + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + if (row_info->bit_depth == 8) + { + PNG_CONST png_bytep table = png_ptr->gamma_from_1; + + if (table != NULL) + { + PNG_CONST int step = + (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 4 : 2; + + /* The alpha channel is the last component: */ + row += step - 1; + + for (; row_width > 0; --row_width, row += step) + *row = table[*row]; + + return; + } + } + + else if (row_info->bit_depth == 16) + { + PNG_CONST png_uint_16pp table = png_ptr->gamma_16_from_1; + PNG_CONST int gamma_shift = png_ptr->gamma_shift; + + if (table != NULL) + { + PNG_CONST int step = + (row_info->color_type & PNG_COLOR_MASK_COLOR) ? 8 : 4; + + /* The alpha channel is the last component: */ + row += step - 2; + + for (; row_width > 0; --row_width, row += step) + { + png_uint_16 v; + + v = table[*(row + 1) >> gamma_shift][*row]; + *row = (png_byte)((v >> 8) & 0xff); + *(row + 1) = (png_byte)(v & 0xff); + } + + return; + } + } + } + + /* Only get to here if called with a weird row_info; no harm has been done, + * so just issue a warning. + */ + png_warning(png_ptr, "png_do_encode_alpha: unexpected call"); +} +#endif + +#ifdef PNG_READ_EXPAND_SUPPORTED /* Expands a palette row to an RGB or RGBA row depending * upon whether you supply trans and num_trans. */ void /* PRIVATE */ png_do_expand_palette(png_row_infop row_info, png_bytep row, - png_colorp palette, png_bytep trans, int num_trans) + png_const_colorp palette, png_const_bytep trans_alpha, int num_trans) { int shift, value; png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width=row_info->width; - png_debug(1, "in png_do_expand_palette\n"); - if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - row_info->color_type == PNG_COLOR_TYPE_PALETTE) + png_debug(1, "in png_do_expand_palette"); + + if (row_info->color_type == PNG_COLOR_TYPE_PALETTE) { if (row_info->bit_depth < 8) { @@ -3592,13 +4557,16 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, { if ((*sp >> shift) & 0x01) *dp = 1; + else *dp = 0; + if (shift == 7) { shift = 0; sp--; } + else shift++; @@ -3606,6 +4574,7 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, } break; } + case 2: { sp = row + (png_size_t)((row_width - 1) >> 2); @@ -3620,6 +4589,7 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, shift = 0; sp--; } + else shift += 2; @@ -3627,6 +4597,7 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, } break; } + case 4: { sp = row + (png_size_t)((row_width - 1) >> 1); @@ -3641,6 +4612,7 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, shift = 0; sp--; } + else shift += 4; @@ -3648,16 +4620,19 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, } break; } + + default: + break; } row_info->bit_depth = 8; row_info->pixel_depth = 8; row_info->rowbytes = row_width; } - switch (row_info->bit_depth) + + if (row_info->bit_depth == 8) { - case 8: { - if (trans != NULL) + if (num_trans > 0) { sp = row + (png_size_t)row_width - 1; dp = row + (png_size_t)(row_width << 2) - 1; @@ -3666,8 +4641,10 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, { if ((int)(*sp) >= num_trans) *dp-- = 0xff; + else - *dp-- = trans[*sp]; + *dp-- = trans_alpha[*sp]; + *dp-- = palette[*sp].blue; *dp-- = palette[*sp].green; *dp-- = palette[*sp].red; @@ -3679,6 +4656,7 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, row_info->color_type = 6; row_info->channels = 4; } + else { sp = row + (png_size_t)row_width - 1; @@ -3691,13 +4669,13 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, *dp-- = palette[*sp].red; sp--; } + row_info->bit_depth = 8; row_info->pixel_depth = 24; row_info->rowbytes = row_width * 3; row_info->color_type = 2; row_info->channels = 3; } - break; } } } @@ -3708,21 +4686,19 @@ png_do_expand_palette(png_row_infop row_info, png_bytep row, */ void /* PRIVATE */ png_do_expand(png_row_infop row_info, png_bytep row, - png_color_16p trans_value) + png_const_color_16p trans_color) { int shift, value; png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width=row_info->width; - png_debug(1, "in png_do_expand\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL) -#endif + png_debug(1, "in png_do_expand"); + { if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { - png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + unsigned int gray = trans_color ? trans_color->gray : 0; if (row_info->bit_depth < 8) { @@ -3730,7 +4706,7 @@ png_do_expand(png_row_infop row_info, png_bytep row, { case 1: { - gray = (png_uint_16)((gray&0x01)*0xff); + gray = (gray & 0x01) * 0xff; sp = row + (png_size_t)((row_width - 1) >> 3); dp = row + (png_size_t)row_width - 1; shift = 7 - (int)((row_width + 7) & 0x07); @@ -3738,13 +4714,16 @@ png_do_expand(png_row_infop row_info, png_bytep row, { if ((*sp >> shift) & 0x01) *dp = 0xff; + else *dp = 0; + if (shift == 7) { shift = 0; sp--; } + else shift++; @@ -3752,9 +4731,10 @@ png_do_expand(png_row_infop row_info, png_bytep row, } break; } + case 2: { - gray = (png_uint_16)((gray&0x03)*0x55); + gray = (gray & 0x03) * 0x55; sp = row + (png_size_t)((row_width - 1) >> 2); dp = row + (png_size_t)row_width - 1; shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); @@ -3768,6 +4748,7 @@ png_do_expand(png_row_infop row_info, png_bytep row, shift = 0; sp--; } + else shift += 2; @@ -3775,9 +4756,10 @@ png_do_expand(png_row_infop row_info, png_bytep row, } break; } + case 4: { - gray = (png_uint_16)((gray&0x0f)*0x11); + gray = (gray & 0x0f) * 0x11; sp = row + (png_size_t)((row_width - 1) >> 1); dp = row + (png_size_t)row_width - 1; shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); @@ -3790,6 +4772,7 @@ png_do_expand(png_row_infop row_info, png_bytep row, shift = 0; sp--; } + else shift = 4; @@ -3797,50 +4780,61 @@ png_do_expand(png_row_infop row_info, png_bytep row, } break; } + + default: + break; } + row_info->bit_depth = 8; row_info->pixel_depth = 8; row_info->rowbytes = row_width; } - if (trans_value != NULL) + if (trans_color != NULL) { if (row_info->bit_depth == 8) { gray = gray & 0xff; sp = row + (png_size_t)row_width - 1; dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) { if (*sp == gray) *dp-- = 0; + else *dp-- = 0xff; + *dp-- = *sp--; } } + else if (row_info->bit_depth == 16) { - png_byte gray_high = (gray >> 8) & 0xff; - png_byte gray_low = gray & 0xff; + unsigned int gray_high = (gray >> 8) & 0xff; + unsigned int gray_low = gray & 0xff; sp = row + row_info->rowbytes - 1; dp = row + (row_info->rowbytes << 1) - 1; for (i = 0; i < row_width; i++) { - if (*(sp-1) == gray_high && *(sp) == gray_low) + if (*(sp - 1) == gray_high && *(sp) == gray_low) { *dp-- = 0; *dp-- = 0; } + else { *dp-- = 0xff; *dp-- = 0xff; } + *dp-- = *sp--; *dp-- = *sp--; } } + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; row_info->channels = 2; row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); @@ -3848,21 +4842,23 @@ png_do_expand(png_row_infop row_info, png_bytep row, row_width); } } - else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_color) { if (row_info->bit_depth == 8) { - png_byte red = trans_value->red & 0xff; - png_byte green = trans_value->green & 0xff; - png_byte blue = trans_value->blue & 0xff; + png_byte red = (png_byte)(trans_color->red & 0xff); + png_byte green = (png_byte)(trans_color->green & 0xff); + png_byte blue = (png_byte)(trans_color->blue & 0xff); sp = row + (png_size_t)row_info->rowbytes - 1; dp = row + (png_size_t)(row_width << 2) - 1; for (i = 0; i < row_width; i++) { if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) *dp-- = 0; + else *dp-- = 0xff; + *dp-- = *sp--; *dp-- = *sp--; *dp-- = *sp--; @@ -3870,31 +4866,33 @@ png_do_expand(png_row_infop row_info, png_bytep row, } else if (row_info->bit_depth == 16) { - png_byte red_high = (trans_value->red >> 8) & 0xff; - png_byte green_high = (trans_value->green >> 8) & 0xff; - png_byte blue_high = (trans_value->blue >> 8) & 0xff; - png_byte red_low = trans_value->red & 0xff; - png_byte green_low = trans_value->green & 0xff; - png_byte blue_low = trans_value->blue & 0xff; + png_byte red_high = (png_byte)((trans_color->red >> 8) & 0xff); + png_byte green_high = (png_byte)((trans_color->green >> 8) & 0xff); + png_byte blue_high = (png_byte)((trans_color->blue >> 8) & 0xff); + png_byte red_low = (png_byte)(trans_color->red & 0xff); + png_byte green_low = (png_byte)(trans_color->green & 0xff); + png_byte blue_low = (png_byte)(trans_color->blue & 0xff); sp = row + row_info->rowbytes - 1; dp = row + (png_size_t)(row_width << 3) - 1; for (i = 0; i < row_width; i++) { if (*(sp - 5) == red_high && - *(sp - 4) == red_low && - *(sp - 3) == green_high && - *(sp - 2) == green_low && - *(sp - 1) == blue_high && - *(sp ) == blue_low) + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) { *dp-- = 0; *dp-- = 0; } + else { *dp-- = 0xff; *dp-- = 0xff; } + *dp-- = *sp--; *dp-- = *sp--; *dp-- = *sp--; @@ -3906,28 +4904,57 @@ png_do_expand(png_row_infop row_info, png_bytep row, row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; row_info->channels = 4; row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } } } #endif -#if defined(PNG_READ_DITHER_SUPPORTED) +#ifdef PNG_READ_EXPAND_16_SUPPORTED +/* If the bit depth is 8 and the color type is not a palette type expand the + * whole row to 16 bits. Has no effect otherwise. + */ void /* PRIVATE */ -png_do_dither(png_row_infop row_info, png_bytep row, - png_bytep palette_lookup, png_bytep dither_lookup) +png_do_expand_16(png_row_infop row_info, png_bytep row) +{ + if (row_info->bit_depth == 8 && + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + /* The row have a sequence of bytes containing [0..255] and we need + * to turn it into another row containing [0..65535], to do this we + * calculate: + * + * (input / 255) * 65535 + * + * Which happens to be exactly input * 257 and this can be achieved + * simply by byte replication in place (copying backwards). + */ + png_byte *sp = row + row_info->rowbytes; /* source, last byte + 1 */ + png_byte *dp = sp + row_info->rowbytes; /* destination, end + 1 */ + while (dp > sp) + dp[-2] = dp[-1] = *--sp, dp -= 2; + + row_info->rowbytes *= 2; + row_info->bit_depth = 16; + row_info->pixel_depth = (png_byte)(row_info->channels * 16); + } +} +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +void /* PRIVATE */ +png_do_quantize(png_row_infop row_info, png_bytep row, + png_const_bytep palette_lookup, png_const_bytep quantize_lookup) { png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width=row_info->width; - png_debug(1, "in png_do_dither\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL) -#endif + png_debug(1, "in png_do_quantize"); + + if (row_info->bit_depth == 8) { - if (row_info->color_type == PNG_COLOR_TYPE_RGB && - palette_lookup && row_info->bit_depth == 8) + if (row_info->color_type == PNG_COLOR_TYPE_RGB && palette_lookup) { int r, g, b, p; sp = row; @@ -3938,31 +4965,33 @@ 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)) | - (((g >> (8 - PNG_DITHER_GREEN_BITS)) & - ((1 << PNG_DITHER_GREEN_BITS) - 1)) << - (PNG_DITHER_BLUE_BITS)) | - ((b >> (8 - PNG_DITHER_BLUE_BITS)) & - ((1 << PNG_DITHER_BLUE_BITS) - 1)); + /* This looks real messy, but the compiler will reduce + * it down to a reasonable formula. For example, with + * 5 bits per color, we get: + * p = (((r >> 3) & 0x1f) << 10) | + * (((g >> 3) & 0x1f) << 5) | + * ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); *dp++ = palette_lookup[p]; } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; row_info->channels = 1; row_info->pixel_depth = row_info->bit_depth; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && - palette_lookup != NULL && row_info->bit_depth == 8) + palette_lookup != NULL) { int r, g, b, p; sp = row; @@ -3974,270 +5003,52 @@ png_do_dither(png_row_infop row_info, png_bytep row, b = *sp++; sp++; - p = (((r >> (8 - PNG_DITHER_RED_BITS)) & - ((1 << PNG_DITHER_RED_BITS) - 1)) << - (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | - (((g >> (8 - PNG_DITHER_GREEN_BITS)) & - ((1 << PNG_DITHER_GREEN_BITS) - 1)) << - (PNG_DITHER_BLUE_BITS)) | - ((b >> (8 - PNG_DITHER_BLUE_BITS)) & - ((1 << PNG_DITHER_BLUE_BITS) - 1)); + p = (((r >> (8 - PNG_QUANTIZE_RED_BITS)) & + ((1 << PNG_QUANTIZE_RED_BITS) - 1)) << + (PNG_QUANTIZE_GREEN_BITS + PNG_QUANTIZE_BLUE_BITS)) | + (((g >> (8 - PNG_QUANTIZE_GREEN_BITS)) & + ((1 << PNG_QUANTIZE_GREEN_BITS) - 1)) << + (PNG_QUANTIZE_BLUE_BITS)) | + ((b >> (8 - PNG_QUANTIZE_BLUE_BITS)) & + ((1 << PNG_QUANTIZE_BLUE_BITS) - 1)); *dp++ = palette_lookup[p]; } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; row_info->channels = 1; row_info->pixel_depth = row_info->bit_depth; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, row_width); } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && - dither_lookup && row_info->bit_depth == 8) + quantize_lookup) { sp = row; + for (i = 0; i < row_width; i++, sp++) { - *sp = dither_lookup[*sp]; + *sp = quantize_lookup[*sp]; } } } } -#endif +#endif /* PNG_READ_QUANTIZE_SUPPORTED */ +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ -#ifdef PNG_FLOATING_POINT_SUPPORTED -#if defined(PNG_READ_GAMMA_SUPPORTED) -static PNG_CONST int png_gamma_shift[] = - {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; - -/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit - * tables, we don't make a full table if we are reducing to 8-bit in - * the future. Note also how the gamma_16 tables are segmented so that - * we don't need to allocate > 64K chunks for a full 16-bit table. - */ -void /* PRIVATE */ -png_build_gamma_table(png_structp png_ptr) -{ - png_debug(1, "in png_build_gamma_table\n"); - - if (png_ptr->bit_depth <= 8) - { - int i; - double g; - - if (png_ptr->screen_gamma > .000001) - g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); - else - g = 1.0; - - png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, - (png_uint_32)256); - - for (i = 0; i < 256; i++) - { - png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, - g) * 255.0 + .5); - } - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) - { - - g = 1.0 / (png_ptr->gamma); - - png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, - (png_uint_32)256); - - for (i = 0; i < 256; i++) - { - png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, - g) * 255.0 + .5); - } - - - png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, - (png_uint_32)256); - - if(png_ptr->screen_gamma > 0.000001) - g = 1.0 / png_ptr->screen_gamma; - else - g = png_ptr->gamma; /* probably doing rgb_to_gray */ - - for (i = 0; i < 256; i++) - { - png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, - g) * 255.0 + .5); - - } - } -#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ - } - else - { - double g; - int i, j, shift, num; - int sig_bit; - png_uint_32 ig; - - if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) - { - sig_bit = (int)png_ptr->sig_bit.red; - if ((int)png_ptr->sig_bit.green > sig_bit) - sig_bit = png_ptr->sig_bit.green; - if ((int)png_ptr->sig_bit.blue > sig_bit) - sig_bit = png_ptr->sig_bit.blue; - } - else - { - sig_bit = (int)png_ptr->sig_bit.gray; - } - - if (sig_bit > 0) - shift = 16 - sig_bit; - else - shift = 0; - - if (png_ptr->transformations & PNG_16_TO_8) - { - if (shift < (16 - PNG_MAX_GAMMA_8)) - shift = (16 - PNG_MAX_GAMMA_8); - } - - if (shift > 8) - shift = 8; - if (shift < 0) - shift = 0; - - png_ptr->gamma_shift = (png_byte)shift; - - num = (1 << (8 - shift)); - - if (png_ptr->screen_gamma > .000001) - g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); - else - g = 1.0; - - png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr, - (png_uint_32)(num * png_sizeof (png_uint_16p))); - - if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) - { - double fin, fout; - png_uint_32 last, max; - - for (i = 0; i < num; i++) - { - png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(256 * png_sizeof (png_uint_16))); - } - - g = 1.0 / g; - last = 0; - for (i = 0; i < 256; i++) - { - fout = ((double)i + 0.5) / 256.0; - fin = pow(fout, g); - max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); - while (last <= max) - { - png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] - [(int)(last >> (8 - shift))] = (png_uint_16)( - (png_uint_16)i | ((png_uint_16)i << 8)); - last++; - } - } - while (last < ((png_uint_32)num << 8)) - { - png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] - [(int)(last >> (8 - shift))] = (png_uint_16)65535L; - last++; - } - } - else - { - for (i = 0; i < num; i++) - { - png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(256 * png_sizeof (png_uint_16))); - - ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); - for (j = 0; j < 256; j++) - { - png_ptr->gamma_16_table[i][j] = - (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / - 65535.0, g) * 65535.0 + .5); - } - } - } - -#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ - defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) - if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) - { - - g = 1.0 / (png_ptr->gamma); - - png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr, - (png_uint_32)(num * png_sizeof (png_uint_16p ))); - - for (i = 0; i < num; i++) - { - png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(256 * png_sizeof (png_uint_16))); - - ig = (((png_uint_32)i * - (png_uint_32)png_gamma_shift[shift]) >> 4); - for (j = 0; j < 256; j++) - { - png_ptr->gamma_16_to_1[i][j] = - (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / - 65535.0, g) * 65535.0 + .5); - } - } - - if(png_ptr->screen_gamma > 0.000001) - g = 1.0 / png_ptr->screen_gamma; - else - g = png_ptr->gamma; /* probably doing rgb_to_gray */ - - png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr, - (png_uint_32)(num * png_sizeof (png_uint_16p))); - - for (i = 0; i < num; i++) - { - png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(256 * png_sizeof (png_uint_16))); - - ig = (((png_uint_32)i * - (png_uint_32)png_gamma_shift[shift]) >> 4); - for (j = 0; j < 256; j++) - { - png_ptr->gamma_16_from_1[i][j] = - (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / - 65535.0, g) * 65535.0 + .5); - } - } - } -#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ - } -} -#endif -/* To do: install integer version of png_build_gamma_table here */ -#endif - -#if defined(PNG_MNG_FEATURES_SUPPORTED) -/* undoes intrapixel differencing */ +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ void /* PRIVATE */ png_do_read_intrapixel(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_read_intrapixel\n"); + png_debug(1, "in png_do_read_intrapixel"); + if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif (row_info->color_type & PNG_COLOR_MASK_COLOR)) { int bytes_per_pixel; png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) { png_bytep rp; @@ -4245,15 +5056,17 @@ png_do_read_intrapixel(png_row_infop row_info, png_bytep row) if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 4; + else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { - *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); - *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + *(rp) = (png_byte)((256 + *rp + *(rp + 1)) & 0xff); + *(rp+2) = (png_byte)((256 + *(rp + 2) + *(rp + 1)) & 0xff); } } else if (row_info->bit_depth == 16) @@ -4263,22 +5076,24 @@ png_do_read_intrapixel(png_row_infop row_info, png_bytep row) if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 8; + else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { - png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); - png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); - png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); - png_uint_32 red = (png_uint_32)((s0+s1+65536L) & 0xffffL); - png_uint_32 blue = (png_uint_32)((s2+s1+65536L) & 0xffffL); - *(rp ) = (png_byte)((red >> 8) & 0xff); - *(rp+1) = (png_byte)(red & 0xff); - *(rp+4) = (png_byte)((blue >> 8) & 0xff); - *(rp+5) = (png_byte)(blue & 0xff); + png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (s0 + s1 + 65536) & 0xffff; + png_uint_32 blue = (s2 + s1 + 65536) & 0xffff; + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp + 1) = (png_byte)(red & 0xff); + *(rp + 4) = (png_byte)((blue >> 8) & 0xff); + *(rp + 5) = (png_byte)(blue & 0xff); } } } diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrutil.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrutil.c index 6a705a4..b998fe6 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrutil.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngrutil.c @@ -1,138 +1,239 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * This file contains routines that are only called from within * libpng itself during the course of reading an image. */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" -#if defined(PNG_READ_SUPPORTED) +#ifdef PNG_READ_SUPPORTED -#if defined(_WIN32_WCE) && (_WIN32_WCE<0x500) -# define WIN32_WCE_OLD -#endif - -#ifdef PNG_FLOATING_POINT_SUPPORTED -# if defined(WIN32_WCE_OLD) -/* strtod() function is not supported on WindowsCE */ -__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, char **endptr) -{ - double result = 0; - int len; - wchar_t *str, *end; - - len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0); - str = (wchar_t *)png_malloc(png_ptr, len * sizeof(wchar_t)); - if ( NULL != str ) - { - MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len); - result = wcstod(str, &end); - len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL); - *endptr = (char *)nptr + (png_strlen(nptr) - len + 1); - png_free(png_ptr, str); - } - return result; -} -# else -# define png_strtod(p,a,b) strtod(a,b) -# endif -#endif +#define png_strtod(p,a,b) strtod(a,b) png_uint_32 PNGAPI -png_get_uint_31(png_structp png_ptr, png_bytep buf) +png_get_uint_31(png_const_structrp png_ptr, png_const_bytep buf) { - png_uint_32 i = png_get_uint_32(buf); - if (i > PNG_UINT_31_MAX) - png_error(png_ptr, "PNG unsigned integer out of range."); - return (i); + png_uint_32 uval = png_get_uint_32(buf); + + if (uval > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range"); + + return (uval); } -#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED + +#if defined(PNG_READ_gAMA_SUPPORTED) || defined(PNG_READ_cHRM_SUPPORTED) +/* The following is a variation on the above for use with the fixed + * point values used for gAMA and cHRM. Instead of png_error it + * issues a warning and returns (-1) - an invalid value because both + * gAMA and cHRM use *unsigned* integers for fixed point values. + */ +#define PNG_FIXED_ERROR (-1) + +static png_fixed_point /* PRIVATE */ +png_get_fixed_point(png_structrp png_ptr, png_const_bytep buf) +{ + png_uint_32 uval = png_get_uint_32(buf); + + if (uval <= PNG_UINT_31_MAX) + return (png_fixed_point)uval; /* known to be in range */ + + /* The caller can turn off the warning by passing NULL. */ + if (png_ptr != NULL) + png_warning(png_ptr, "PNG fixed point integer out of range"); + + return PNG_FIXED_ERROR; +} +#endif + +#ifdef PNG_READ_INT_FUNCTIONS_SUPPORTED +/* NOTE: the read macros will obscure these definitions, so that if + * PNG_USE_READ_MACROS is set the library will not use them internally, + * but the APIs will still be available externally. + * + * The parentheses around "PNGAPI function_name" in the following three + * functions are necessary because they allow the macros to co-exist with + * these (unused but exported) functions. + */ + /* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ -png_uint_32 PNGAPI -png_get_uint_32(png_bytep buf) +png_uint_32 (PNGAPI +png_get_uint_32)(png_const_bytep buf) { - png_uint_32 i = ((png_uint_32)(*buf) << 24) + - ((png_uint_32)(*(buf + 1)) << 16) + - ((png_uint_32)(*(buf + 2)) << 8) + - (png_uint_32)(*(buf + 3)); + png_uint_32 uval = + ((png_uint_32)(*(buf )) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + ((png_uint_32)(*(buf + 3)) ) ; - return (i); + return uval; } /* 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) + * data is stored in the PNG file in two's complement format and there + * is no guarantee that a 'png_int_32' is exactly 32 bits, therefore + * the following code does a two's complement to native conversion. + */ +png_int_32 (PNGAPI +png_get_int_32)(png_const_bytep buf) { - png_int_32 i = ((png_int_32)(*buf) << 24) + - ((png_int_32)(*(buf + 1)) << 16) + - ((png_int_32)(*(buf + 2)) << 8) + - (png_int_32)(*(buf + 3)); + png_uint_32 uval = png_get_uint_32(buf); + if ((uval & 0x80000000) == 0) /* non-negative */ + return uval; - return (i); + uval = (uval ^ 0xffffffff) + 1; /* 2's complement: -x = ~x+1 */ + return -(png_int_32)uval; } /* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ -png_uint_16 PNGAPI -png_get_uint_16(png_bytep buf) +png_uint_16 (PNGAPI +png_get_uint_16)(png_const_bytep buf) { - png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) + - (png_uint_16)(*(buf + 1))); + /* ANSI-C requires an int value to accomodate at least 16 bits so this + * works and allows the compiler not to worry about possible narrowing + * on 32 bit systems. (Pre-ANSI systems did not make integers smaller + * than 16 bits either.) + */ + unsigned int val = + ((unsigned int)(*buf) << 8) + + ((unsigned int)(*(buf + 1))); - return (i); + return (png_uint_16)val; +} + +#endif /* PNG_READ_INT_FUNCTIONS_SUPPORTED */ + +/* Read and check the PNG file signature */ +void /* PRIVATE */ +png_read_sig(png_structrp png_ptr, png_inforp info_ptr) +{ + png_size_t num_checked, num_to_check; + + /* Exit if the user application does not expect a signature. */ + if (png_ptr->sig_bytes >= 8) + return; + + num_checked = png_ptr->sig_bytes; + num_to_check = 8 - num_checked; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_SIGNATURE; +#endif + + /* The signature must be serialized in a single I/O call. */ + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Read the chunk header (length + type name). + * Put the type name into png_ptr->chunk_name, and return the length. + */ +png_uint_32 /* PRIVATE */ +png_read_chunk_header(png_structrp png_ptr) +{ + png_byte buf[8]; + png_uint_32 length; + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_HDR; +#endif + + /* Read the length and the chunk name. + * This must be performed in a single I/O call. + */ + png_read_data(png_ptr, buf, 8); + length = png_get_uint_31(png_ptr, buf); + + /* Put the chunk name into png_ptr->chunk_name. */ + png_ptr->chunk_name = PNG_CHUNK_FROM_STRING(buf+4); + + png_debug2(0, "Reading %lx chunk, length = %lu", + (unsigned long)png_ptr->chunk_name, (unsigned long)length); + + /* Reset the crc and run it over the chunk name. */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, buf + 4, 4); + + /* Check to see if chunk name is valid. */ + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_DATA; +#endif + + return length; } -#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) +png_crc_read(png_structrp png_ptr, png_bytep buf, png_uint_32 length) { - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + png_read_data(png_ptr, buf, length); png_calculate_crc(png_ptr, buf, length); } /* Optionally skip data and then check the CRC. Depending on whether we - are reading a ancillary or critical chunk, and how the program has set - things up, we may calculate the CRC on the data and print a message. - Returns '1' if there was a CRC error, '0' otherwise. */ + * are reading an ancillary or critical chunk, and how the program has set + * things up, we may calculate the CRC on the data and print a message. + * Returns '1' if there was a CRC error, '0' otherwise. + */ int /* PRIVATE */ -png_crc_finish(png_structp png_ptr, png_uint_32 skip) +png_crc_finish(png_structrp png_ptr, png_uint_32 skip) { - png_size_t i; - png_size_t istop = png_ptr->zbuf_size; + /* The size of the local buffer for inflate is a good guess as to a + * reasonable size to use for buffering reads from the application. + */ + while (skip > 0) + { + png_uint_32 len; + png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; - for (i = (png_size_t)skip; i > istop; i -= istop) - { - png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); - } - if (i) - { - png_crc_read(png_ptr, png_ptr->zbuf, i); + len = (sizeof tmpbuf); + if (len > skip) + len = skip; + skip -= len; + + png_crc_read(png_ptr, tmpbuf, len); } if (png_crc_error(png_ptr)) { - if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ - !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || - (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ - (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + if (PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name) ? + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) : + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)) { png_chunk_warning(png_ptr, "CRC error"); } + else { - png_chunk_error(png_ptr, "CRC error"); + png_chunk_benign_error(png_ptr, "CRC error"); + return (0); } + return (1); } @@ -140,26 +241,33 @@ png_crc_finish(png_structp png_ptr, png_uint_32 skip) } /* Compare the CRC stored in the PNG file with that calculated by libpng from - the data it has read thus far. */ + * the data it has read thus far. + */ int /* PRIVATE */ -png_crc_error(png_structp png_ptr) +png_crc_error(png_structrp png_ptr) { png_byte crc_bytes[4]; png_uint_32 crc; int need_crc = 1; - if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + if (PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name)) { if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) need_crc = 0; } - else /* critical */ + + else /* critical */ { if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) need_crc = 0; } +#ifdef PNG_IO_STATE_SUPPORTED + png_ptr->io_state = PNG_IO_READING | PNG_IO_CHUNK_CRC; +#endif + + /* The chunk CRC must be serialized in a single I/O call. */ png_read_data(png_ptr, crc_bytes, 4); if (need_crc) @@ -167,199 +275,527 @@ png_crc_error(png_structp png_ptr) crc = png_get_uint_32(crc_bytes); return ((int)(crc != png_ptr->crc)); } + else return (0); } -#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ - defined(PNG_READ_iCCP_SUPPORTED) +/* Manage the read buffer; this simply reallocates the buffer if it is not small + * enough (or if it is not allocated). The routine returns a pointer to the + * buffer; if an error occurs and 'warn' is set the routine returns NULL, else + * it will call png_error (via png_malloc) on failure. (warn == 2 means + * 'silent'). + */ +static png_bytep +png_read_buffer(png_structrp png_ptr, png_alloc_size_t new_size, int warn) +{ + png_bytep buffer = png_ptr->read_buffer; + + if (buffer != NULL && new_size > png_ptr->read_buffer_size) + { + png_ptr->read_buffer = NULL; + png_ptr->read_buffer = NULL; + png_ptr->read_buffer_size = 0; + png_free(png_ptr, buffer); + buffer = NULL; + } + + if (buffer == NULL) + { + buffer = png_voidcast(png_bytep, png_malloc_base(png_ptr, new_size)); + + if (buffer != NULL) + { + png_ptr->read_buffer = buffer; + png_ptr->read_buffer_size = new_size; + } + + else if (warn < 2) /* else silent */ + { +#ifdef PNG_WARNINGS_SUPPORTED + if (warn) + png_chunk_warning(png_ptr, "insufficient memory to read chunk"); + else +#endif + { +#ifdef PNG_ERROR_TEXT_SUPPORTED + png_chunk_error(png_ptr, "insufficient memory to read chunk"); +#endif + } + } + } + + return buffer; +} + +/* png_inflate_claim: claim the zstream for some nefarious purpose that involves + * decompression. Returns Z_OK on success, else a zlib error code. It checks + * the owner but, in final release builds, just issues a warning if some other + * chunk apparently owns the stream. Prior to release it does a png_error. + */ +static int +png_inflate_claim(png_structrp png_ptr, png_uint_32 owner, int window_bits) +{ + if (png_ptr->zowner != 0) + { + char msg[64]; + + PNG_STRING_FROM_CHUNK(msg, png_ptr->zowner); + /* So the message that results is " using zstream"; this is an + * internal error, but is very useful for debugging. i18n requirements + * are minimal. + */ + (void)png_safecat(msg, (sizeof msg), 4, " using zstream"); +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_chunk_warning(png_ptr, msg); + png_ptr->zowner = 0; +# else + png_chunk_error(png_ptr, msg); +# endif + } + + /* Implementation note: unlike 'png_deflate_claim' this internal function + * does not take the size of the data as an argument. Some efficiency could + * be gained by using this when it is known *if* the zlib stream itself does + * not record the number; however, this is an illusion: the original writer + * of the PNG may have selected a lower window size, and we really must + * follow that because, for systems with with limited capabilities, we + * would otherwise reject the application's attempts to use a smaller window + * size (zlib doesn't have an interface to say "this or lower"!). + * + * inflateReset2 was added to zlib 1.2.4; before this the window could not be + * reset, therefore it is necessary to always allocate the maximum window + * size with earlier zlibs just in case later compressed chunks need it. + */ + { + int ret; /* zlib return code */ + + /* Set this for safety, just in case the previous owner left pointers to + * memory allocations. + */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + { +# if ZLIB_VERNUM < 0x1240 + PNG_UNUSED(window_bits) + ret = inflateReset(&png_ptr->zstream); +# else + ret = inflateReset2(&png_ptr->zstream, window_bits); +# endif + } + + else + { +# if ZLIB_VERNUM < 0x1240 + ret = inflateInit(&png_ptr->zstream); +# else + ret = inflateInit2(&png_ptr->zstream, window_bits); +# endif + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + + if (ret == Z_OK) + png_ptr->zowner = owner; + + else + png_zstream_error(png_ptr, ret); + + return ret; + } +} + +#ifdef PNG_READ_COMPRESSED_TEXT_SUPPORTED +/* png_inflate now returns zlib error codes including Z_OK and Z_STREAM_END to + * allow the caller to do multiple calls if required. If the 'finish' flag is + * set Z_FINISH will be passed to the final inflate() call and Z_STREAM_END must + * be returned or there has been a problem, otherwise Z_SYNC_FLUSH is used and + * Z_OK or Z_STREAM_END will be returned on success. + * + * The input and output sizes are updated to the actual amounts of data consumed + * or written, not the amount available (as in a z_stream). The data pointers + * are not changed, so the next input is (data+input_size) and the next + * available output is (output+output_size). + */ +static int +png_inflate(png_structrp png_ptr, png_uint_32 owner, int finish, + /* INPUT: */ png_const_bytep input, png_uint_32p input_size_ptr, + /* OUTPUT: */ png_bytep output, png_alloc_size_t *output_size_ptr) +{ + if (png_ptr->zowner == owner) /* Else not claimed */ + { + int ret; + png_alloc_size_t avail_out = *output_size_ptr; + png_uint_32 avail_in = *input_size_ptr; + + /* zlib can't necessarily handle more than 65535 bytes at once (i.e. it + * can't even necessarily handle 65536 bytes) because the type uInt is + * "16 bits or more". Consequently it is necessary to chunk the input to + * zlib. This code uses ZLIB_IO_MAX, from pngpriv.h, as the maximum (the + * maximum value that can be stored in a uInt.) It is possible to set + * ZLIB_IO_MAX to a lower value in pngpriv.h and this may sometimes have + * a performance advantage, because it reduces the amount of data accessed + * at each step and that may give the OS more time to page it in. + */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input); + /* avail_in and avail_out are set below from 'size' */ + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.avail_out = 0; + + /* Read directly into the output if it is available (this is set to + * a local buffer below if output is NULL). + */ + if (output != NULL) + png_ptr->zstream.next_out = output; + + do + { + uInt avail; + unsigned char local_buffer[PNG_INFLATE_BUF_SIZE]; + + /* zlib INPUT BUFFER */ + /* The setting of 'avail_in' used to be outside the loop; by setting it + * inside it is possible to chunk the input to zlib and simply rely on + * zlib to advance the 'next_in' pointer. This allows arbitrary + * amounts of data to be passed through zlib at the unavoidable cost of + * requiring a window save (memcpy of up to 32768 output bytes) + * every ZLIB_IO_MAX input bytes. + */ + avail_in += png_ptr->zstream.avail_in; /* not consumed last time */ + + avail = ZLIB_IO_MAX; + + if (avail_in < avail) + avail = (uInt)avail_in; /* safe: < than ZLIB_IO_MAX */ + + avail_in -= avail; + png_ptr->zstream.avail_in = avail; + + /* zlib OUTPUT BUFFER */ + avail_out += png_ptr->zstream.avail_out; /* not written last time */ + + avail = ZLIB_IO_MAX; /* maximum zlib can process */ + + if (output == NULL) + { + /* Reset the output buffer each time round if output is NULL and + * make available the full buffer, up to 'remaining_space' + */ + png_ptr->zstream.next_out = local_buffer; + if ((sizeof local_buffer) < avail) + avail = (sizeof local_buffer); + } + + if (avail_out < avail) + avail = (uInt)avail_out; /* safe: < ZLIB_IO_MAX */ + + png_ptr->zstream.avail_out = avail; + avail_out -= avail; + + /* zlib inflate call */ + /* In fact 'avail_out' may be 0 at this point, that happens at the end + * of the read when the final LZ end code was not passed at the end of + * the previous chunk of input data. Tell zlib if we have reached the + * end of the output buffer. + */ + ret = inflate(&png_ptr->zstream, avail_out > 0 ? Z_NO_FLUSH : + (finish ? Z_FINISH : Z_SYNC_FLUSH)); + } while (ret == Z_OK); + + /* For safety kill the local buffer pointer now */ + if (output == NULL) + png_ptr->zstream.next_out = NULL; + + /* Claw back the 'size' and 'remaining_space' byte counts. */ + avail_in += png_ptr->zstream.avail_in; + avail_out += png_ptr->zstream.avail_out; + + /* Update the input and output sizes; the updated values are the amount + * consumed or written, effectively the inverse of what zlib uses. + */ + if (avail_out > 0) + *output_size_ptr -= avail_out; + + if (avail_in > 0) + *input_size_ptr -= avail_in; + + /* Ensure png_ptr->zstream.msg is set (even in the success case!) */ + png_zstream_error(png_ptr, ret); + return ret; + } + + else + { + /* This is a bad internal error. The recovery assigns to the zstream msg + * pointer, which is not owned by the caller, but this is safe; it's only + * used on errors! + */ + png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); + return Z_STREAM_ERROR; + } +} + /* - * Decompress trailing data in a chunk. The assumption is that chunkdata + * Decompress trailing data in a chunk. The assumption is that read_buffer * points at an allocated area holding the contents of a chunk with a * trailing compressed part. What we get back is an allocated area * holding the original prefix part and an uncompressed version of the * trailing part (the malloc area passed in is freed). */ -png_charp /* PRIVATE */ -png_decompress_chunk(png_structp png_ptr, int comp_type, - png_charp chunkdata, png_size_t chunklength, - png_size_t prefix_size, png_size_t *newlength) +static int +png_decompress_chunk(png_structrp png_ptr, + png_uint_32 chunklength, png_uint_32 prefix_size, + png_alloc_size_t *newlength /* must be initialized to the maximum! */, + int terminate /*add a '\0' to the end of the uncompressed data*/) { - static PNG_CONST char msg[] = "Error decoding compressed text"; - png_charp text; - png_size_t text_size; + /* TODO: implement different limits for different types of chunk. + * + * The caller supplies *newlength set to the maximum length of the + * uncompressed data, but this routine allocates space for the prefix and + * maybe a '\0' terminator too. We have to assume that 'prefix_size' is + * limited only by the maximum chunk size. + */ + png_alloc_size_t limit = PNG_SIZE_MAX; - if (comp_type == PNG_COMPRESSION_TYPE_BASE) +# ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + + if (limit >= prefix_size + (terminate != 0)) { - int ret = Z_OK; - png_ptr->zstream.next_in = (png_bytep)(chunkdata + prefix_size); - png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + int ret; - text_size = 0; - text = NULL; + limit -= prefix_size + (terminate != 0); - while (png_ptr->zstream.avail_in) + if (limit < *newlength) + *newlength = limit; + + /* Now try to claim the stream; the 'warn' setting causes zlib to be told + * to use the maximum window size during inflate; this hides errors in the + * deflate header window bits value which is used if '0' is passed. In + * fact this only has an effect with zlib versions 1.2.4 and later - see + * the comments in png_inflate_claim above. + */ + ret = png_inflate_claim(png_ptr, png_ptr->chunk_name, + png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0); + + if (ret == Z_OK) { - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) + png_uint_32 lzsize = chunklength - prefix_size; + + ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, + /* input: */ png_ptr->read_buffer + prefix_size, &lzsize, + /* output: */ NULL, newlength); + + if (ret == Z_STREAM_END) { - if (png_ptr->zstream.msg != NULL) - png_warning(png_ptr, png_ptr->zstream.msg); - else - png_warning(png_ptr, msg); - inflateReset(&png_ptr->zstream); - png_ptr->zstream.avail_in = 0; - - if (text == NULL) + /* Use 'inflateReset' here, not 'inflateReset2' because this + * preserves the previously decided window size (otherwise it would + * be necessary to store the previous window size.) In practice + * this doesn't matter anyway, because png_inflate will call inflate + * with Z_FINISH in almost all cases, so the window will not be + * maintained. + */ + if (inflateReset(&png_ptr->zstream) == Z_OK) { - text_size = prefix_size + png_sizeof(msg) + 1; - text = (png_charp)png_malloc_warn(png_ptr, text_size); - if (text == NULL) - { - png_free(png_ptr,chunkdata); - png_error(png_ptr,"Not enough memory to decompress chunk"); - } - png_memcpy(text, chunkdata, prefix_size); - } + /* Because of the limit checks above we know that the new, + * expanded, size will fit in a size_t (let alone an + * png_alloc_size_t). Use png_malloc_base here to avoid an + * extra OOM message. + */ + png_alloc_size_t new_size = *newlength; + png_alloc_size_t buffer_size = prefix_size + new_size + + (terminate != 0); + png_bytep text = png_voidcast(png_bytep, png_malloc_base(png_ptr, + buffer_size)); - text[text_size - 1] = 0x00; - - /* Copy what we can of the error message into the text chunk */ - text_size = (png_size_t)(chunklength - (text - chunkdata) - 1); - text_size = png_sizeof(msg) > text_size ? text_size : - png_sizeof(msg); - png_memcpy(text + prefix_size, msg, text_size + 1); - break; - } - if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) - { - if (text == NULL) - { - text_size = prefix_size + - png_ptr->zbuf_size - png_ptr->zstream.avail_out; - text = (png_charp)png_malloc_warn(png_ptr, text_size + 1); - if (text == NULL) - { - png_free(png_ptr,chunkdata); - png_error(png_ptr,"Not enough memory to decompress chunk."); - } - png_memcpy(text + prefix_size, png_ptr->zbuf, - text_size - prefix_size); - png_memcpy(text, chunkdata, prefix_size); - *(text + text_size) = 0x00; - } - else - { - png_charp tmp; - - tmp = text; - text = (png_charp)png_malloc_warn(png_ptr, - (png_uint_32)(text_size + - png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); - if (text == NULL) + if (text != NULL) { - png_free(png_ptr, tmp); - png_free(png_ptr, chunkdata); - png_error(png_ptr,"Not enough memory to decompress chunk.."); + ret = png_inflate(png_ptr, png_ptr->chunk_name, 1/*finish*/, + png_ptr->read_buffer + prefix_size, &lzsize, + text + prefix_size, newlength); + + if (ret == Z_STREAM_END) + { + if (new_size == *newlength) + { + if (terminate) + text[prefix_size + *newlength] = 0; + + if (prefix_size > 0) + memcpy(text, png_ptr->read_buffer, prefix_size); + + { + png_bytep old_ptr = png_ptr->read_buffer; + + png_ptr->read_buffer = text; + png_ptr->read_buffer_size = buffer_size; + text = old_ptr; /* freed below */ + } + } + + else + { + /* The size changed on the second read, there can be no + * guarantee that anything is correct at this point. + * The 'msg' pointer has been set to "unexpected end of + * LZ stream", which is fine, but return an error code + * that the caller won't accept. + */ + ret = PNG_UNEXPECTED_ZLIB_RETURN; + } + } + + else if (ret == Z_OK) + ret = PNG_UNEXPECTED_ZLIB_RETURN; /* for safety */ + + /* Free the text pointer (this is the old read_buffer on + * success) + */ + png_free(png_ptr, text); + + /* This really is very benign, but it's still an error because + * the extra space may otherwise be used as a Trojan Horse. + */ + if (ret == Z_STREAM_END && + chunklength - prefix_size != lzsize) + png_chunk_benign_error(png_ptr, "extra compressed data"); + } + + else + { + /* Out of memory allocating the buffer */ + ret = Z_MEM_ERROR; + png_zstream_error(png_ptr, Z_MEM_ERROR); } - png_memcpy(text, tmp, text_size); - png_free(png_ptr, tmp); - png_memcpy(text + text_size, png_ptr->zbuf, - (png_ptr->zbuf_size - png_ptr->zstream.avail_out)); - text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; - *(text + text_size) = 0x00; } - if (ret == Z_STREAM_END) - break; + else { - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* inflateReset failed, store the error message */ + png_zstream_error(png_ptr, ret); + + if (ret == Z_STREAM_END) + ret = PNG_UNEXPECTED_ZLIB_RETURN; } } - } - if (ret != Z_STREAM_END) - { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char umsg[52]; - if (ret == Z_BUF_ERROR) - png_snprintf(umsg, 52, - "Buffer error in compressed datastream in %s chunk", - png_ptr->chunk_name); - else if (ret == Z_DATA_ERROR) - png_snprintf(umsg, 52, - "Data error in compressed datastream in %s chunk", - png_ptr->chunk_name); - else - png_snprintf(umsg, 52, - "Incomplete compressed datastream in %s chunk", - png_ptr->chunk_name); - png_warning(png_ptr, umsg); -#else - png_warning(png_ptr, - "Incomplete compressed datastream in chunk other than IDAT"); -#endif - text_size=prefix_size; - if (text == NULL) - { - text = (png_charp)png_malloc_warn(png_ptr, text_size+1); - if (text == NULL) - { - png_free(png_ptr, chunkdata); - png_error(png_ptr,"Not enough memory for text."); - } - png_memcpy(text, chunkdata, prefix_size); - } - *(text + text_size) = 0x00; + else if (ret == Z_OK) + ret = PNG_UNEXPECTED_ZLIB_RETURN; + + /* Release the claimed stream */ + png_ptr->zowner = 0; } - inflateReset(&png_ptr->zstream); - png_ptr->zstream.avail_in = 0; + else /* the claim failed */ if (ret == Z_STREAM_END) /* impossible! */ + ret = PNG_UNEXPECTED_ZLIB_RETURN; - png_free(png_ptr, chunkdata); - chunkdata = text; - *newlength=text_size; + return ret; } - else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + + else { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char umsg[50]; + /* Application/configuration limits exceeded */ + png_zstream_error(png_ptr, Z_MEM_ERROR); + return Z_MEM_ERROR; + } +} +#endif /* PNG_READ_COMPRESSED_TEXT_SUPPORTED */ - png_snprintf(umsg, 50, - "Unknown zTXt compression type %d", comp_type); - png_warning(png_ptr, umsg); -#else - png_warning(png_ptr, "Unknown zTXt compression type"); -#endif +#ifdef PNG_READ_iCCP_SUPPORTED +/* Perform a partial read and decompress, producing 'avail_out' bytes and + * reading from the current chunk as required. + */ +static int +png_inflate_read(png_structrp png_ptr, png_bytep read_buffer, uInt read_size, + png_uint_32p chunk_bytes, png_bytep next_out, png_alloc_size_t *out_size, + int finish) +{ + if (png_ptr->zowner == png_ptr->chunk_name) + { + int ret; - *(chunkdata + prefix_size) = 0x00; - *newlength=prefix_size; + /* next_in and avail_in must have been initialized by the caller. */ + png_ptr->zstream.next_out = next_out; + png_ptr->zstream.avail_out = 0; /* set in the loop */ + + do + { + if (png_ptr->zstream.avail_in == 0) + { + if (read_size > *chunk_bytes) + read_size = (uInt)*chunk_bytes; + *chunk_bytes -= read_size; + + if (read_size > 0) + png_crc_read(png_ptr, read_buffer, read_size); + + png_ptr->zstream.next_in = read_buffer; + png_ptr->zstream.avail_in = read_size; + } + + if (png_ptr->zstream.avail_out == 0) + { + uInt avail = ZLIB_IO_MAX; + if (avail > *out_size) + avail = (uInt)*out_size; + *out_size -= avail; + + png_ptr->zstream.avail_out = avail; + } + + /* Use Z_SYNC_FLUSH when there is no more chunk data to ensure that all + * the available output is produced; this allows reading of truncated + * streams. + */ + ret = inflate(&png_ptr->zstream, + *chunk_bytes > 0 ? Z_NO_FLUSH : (finish ? Z_FINISH : Z_SYNC_FLUSH)); + } + while (ret == Z_OK && (*out_size > 0 || png_ptr->zstream.avail_out > 0)); + + *out_size += png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; /* Should not be required, but is safe */ + + /* Ensure the error message pointer is always set: */ + png_zstream_error(png_ptr, ret); + return ret; } - return chunkdata; + else + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("zstream unclaimed"); + return Z_STREAM_ERROR; + } } #endif -/* read and check the IDHR chunk */ +/* Read and check the IDHR chunk */ void /* PRIVATE */ -png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_IHDR(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[13]; png_uint_32 width, height; int bit_depth, color_type, compression_type, filter_type; int interlace_type; - png_debug(1, "in png_handle_IHDR\n"); + png_debug(1, "in png_handle_IHDR"); if (png_ptr->mode & PNG_HAVE_IHDR) - png_error(png_ptr, "Out of place IHDR"); + png_chunk_error(png_ptr, "out of place"); - /* check the length */ + /* Check the length */ if (length != 13) - png_error(png_ptr, "Invalid IHDR chunk"); + png_chunk_error(png_ptr, "invalid"); png_ptr->mode |= PNG_HAVE_IHDR; @@ -374,79 +810,93 @@ 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 */ + /* Set internal variables */ png_ptr->width = width; png_ptr->height = height; png_ptr->bit_depth = (png_byte)bit_depth; png_ptr->interlaced = (png_byte)interlace_type; png_ptr->color_type = (png_byte)color_type; -#if defined(PNG_MNG_FEATURES_SUPPORTED) +#ifdef PNG_MNG_FEATURES_SUPPORTED png_ptr->filter_type = (png_byte)filter_type; #endif png_ptr->compression_type = (png_byte)compression_type; - /* find number of channels */ + /* Find number of channels */ switch (png_ptr->color_type) { + default: /* invalid, png_set_IHDR calls png_error */ case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_PALETTE: png_ptr->channels = 1; break; + case PNG_COLOR_TYPE_RGB: png_ptr->channels = 3; break; + case PNG_COLOR_TYPE_GRAY_ALPHA: png_ptr->channels = 2; break; + case PNG_COLOR_TYPE_RGB_ALPHA: png_ptr->channels = 4; break; } - /* set up other useful info */ + /* Set up other useful info */ png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * png_ptr->channels); - png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); - png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth); - png_debug1(3,"channels = %d\n", png_ptr->channels); - png_debug1(3,"rowbytes = %lu\n", png_ptr->rowbytes); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->width); + png_debug1(3, "bit_depth = %d", png_ptr->bit_depth); + png_debug1(3, "channels = %d", png_ptr->channels); + png_debug1(3, "rowbytes = %lu", (unsigned long)png_ptr->rowbytes); png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, - color_type, interlace_type, compression_type, filter_type); + color_type, interlace_type, compression_type, filter_type); } -/* read and check the palette */ +/* Read and check the palette */ void /* PRIVATE */ -png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_PLTE(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_color palette[PNG_MAX_PALETTE_LENGTH]; int num, i; -#ifndef PNG_NO_POINTER_INDEXING +#ifdef PNG_POINTER_INDEXING_SUPPORTED png_colorp pal_ptr; #endif - png_debug(1, "in png_handle_PLTE\n"); + png_debug(1, "in png_handle_PLTE"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before PLTE"); + png_chunk_error(png_ptr, "missing IHDR"); + + /* Moved to before the 'after IDAT' check below because otherwise duplicate + * PLTE chunks are potentially ignored (the spec says there shall not be more + * than one PLTE, the error is not treated as benign, so this check trumps + * the requirement that PLTE appears before IDAT.) + */ + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_chunk_error(png_ptr, "duplicate"); + else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid PLTE after IDAT"); + /* This is benign because the non-benign error happened before, when an + * IDAT was encountered in a color-mapped image with no PLTE. + */ png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } - else if (png_ptr->mode & PNG_HAVE_PLTE) - png_error(png_ptr, "Duplicate PLTE chunk"); png_ptr->mode |= PNG_HAVE_PLTE; - if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) { - png_warning(png_ptr, - "Ignoring PLTE chunk in grayscale PNG"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "ignored in grayscale PNG"); return; } -#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + +#ifndef PNG_READ_OPT_PLTE_SUPPORTED if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) { png_crc_finish(png_ptr, length); @@ -456,21 +906,21 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) { + png_crc_finish(png_ptr, length); + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) - { - png_warning(png_ptr, "Invalid palette chunk"); - png_crc_finish(png_ptr, length); - return; - } + png_chunk_benign_error(png_ptr, "invalid"); + else - { - png_error(png_ptr, "Invalid palette chunk"); - } + png_chunk_error(png_ptr, "invalid"); + + return; } + /* The cast is safe because 'length' is less than 3*PNG_MAX_PALETTE_LENGTH */ num = (int)length / 3; -#ifndef PNG_NO_POINTER_INDEXING +#ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) { png_byte buf[3]; @@ -486,42 +936,51 @@ 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 */ + /* 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 we actually need the PLTE chunk (ie for a paletted image), we do + * whatever the normal CRC configuration tells us. However, if we + * have an RGB image, the PLTE can be considered ancillary, so + * we will act as though it is. + */ +#ifndef PNG_READ_OPT_PLTE_SUPPORTED if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) #endif { png_crc_finish(png_ptr, 0); } -#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + +#ifndef PNG_READ_OPT_PLTE_SUPPORTED else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ { /* If we don't want to use the data from an ancillary chunk, - we have two options: an error abort, or a warning and we - ignore the data in this chunk (which should be OK, since - it's considered ancillary for a RGB or RGBA image). */ + * 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). + * + * IMPLEMENTATION NOTE: this is only here because png_crc_finish uses the + * chunk type to determine whether to check the ancillary or the critical + * flags. + */ if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) { if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) { - png_chunk_error(png_ptr, "CRC error"); + png_chunk_benign_error(png_ptr, "CRC error"); } + else { png_chunk_warning(png_ptr, "CRC error"); return; } } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) { @@ -530,175 +989,152 @@ png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } #endif + /* TODO: png_set_PLTE has the side effect of setting png_ptr->palette to its + * own copy of the palette. This has the side effect that when png_start_row + * is called (this happens after any call to png_read_update_info) the + * info_ptr palette gets changed. This is extremely unexpected and + * confusing. + * + * Fix this by not sharing the palette in this way. + */ png_set_PLTE(png_ptr, info_ptr, palette, num); -#if defined(PNG_READ_tRNS_SUPPORTED) - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + /* The three chunks, bKGD, hIST and tRNS *must* appear after PLTE and before + * IDAT. Prior to 1.6.0 this was not checked; instead the code merely + * checked the apparent validity of a tRNS chunk inserted before PLTE on a + * palette PNG. 1.6.0 attempts to rigorously follow the standard and + * therefore does a benign error if the erroneous condition is detected *and* + * cancels the tRNS if the benign error returns. The alternative is to + * amend the standard since it would be rather hypocritical of the standards + * maintainers to ignore it. + */ +#ifdef PNG_READ_tRNS_SUPPORTED + if (png_ptr->num_trans > 0 || + (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS) != 0)) { - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) - { - if (png_ptr->num_trans > (png_uint_16)num) - { - png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); - png_ptr->num_trans = (png_uint_16)num; - } - if (info_ptr->num_trans > (png_uint_16)num) - { - png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); - info_ptr->num_trans = (png_uint_16)num; - } - } + /* Cancel this because otherwise it would be used if the transforms + * require it. Don't cancel the 'valid' flag because this would prevent + * detection of duplicate chunks. + */ + png_ptr->num_trans = 0; + + if (info_ptr != NULL) + info_ptr->num_trans = 0; + + png_chunk_benign_error(png_ptr, "tRNS must be after"); } #endif +#ifdef PNG_READ_hIST_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) != 0) + png_chunk_benign_error(png_ptr, "hIST must be after"); +#endif + +#ifdef PNG_READ_bKGD_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) != 0) + png_chunk_benign_error(png_ptr, "bKGD must be after"); +#endif } void /* PRIVATE */ -png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_IEND(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_debug(1, "in png_handle_IEND\n"); + png_debug(1, "in png_handle_IEND"); if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) - { - png_error(png_ptr, "No image in file"); - } + png_chunk_error(png_ptr, "out of place"); png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); - if (length != 0) - { - png_warning(png_ptr, "Incorrect IEND chunk length"); - } png_crc_finish(png_ptr, length); - info_ptr =info_ptr; /* quiet compiler warnings about unused info_ptr */ + if (length != 0) + png_chunk_benign_error(png_ptr, "invalid"); + + PNG_UNUSED(info_ptr) } -#if defined(PNG_READ_gAMA_SUPPORTED) +#ifdef PNG_READ_gAMA_SUPPORTED void /* PRIVATE */ -png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_gAMA(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_fixed_point igamma; -#ifdef PNG_FLOATING_POINT_SUPPORTED - float file_gamma; -#endif png_byte buf[4]; - png_debug(1, "in png_handle_gAMA\n"); + png_debug(1, "in png_handle_gAMA"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before gAMA"); - else if (png_ptr->mode & PNG_HAVE_IDAT) - { - png_warning(png_ptr, "Invalid gAMA after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - else if (png_ptr->mode & PNG_HAVE_PLTE) - /* Should be an error, but we can cope with it */ - png_warning(png_ptr, "Out of place gAMA chunk"); + png_chunk_error(png_ptr, "missing IHDR"); - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) -#if defined(PNG_READ_sRGB_SUPPORTED) - && !(info_ptr->valid & PNG_INFO_sRGB) -#endif - ) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Duplicate gAMA chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } if (length != 4) { - png_warning(png_ptr, "Incorrect gAMA chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) return; - igamma = (png_fixed_point)png_get_uint_32(buf); - /* check for zero gamma */ - if (igamma == 0) - { - png_warning(png_ptr, - "Ignoring gAMA chunk with gamma=0"); - return; - } + igamma = png_get_fixed_point(NULL, buf); -#if defined(PNG_READ_sRGB_SUPPORTED) - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) - if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) - { - png_warning(png_ptr, - "Ignoring incorrect gAMA value when sRGB is also present"); -#ifndef PNG_NO_CONSOLE_IO - fprintf(stderr, "gamma = (%d/100000)\n", (int)igamma); -#endif - return; - } -#endif /* PNG_READ_sRGB_SUPPORTED */ - -#ifdef PNG_FLOATING_POINT_SUPPORTED - file_gamma = (float)igamma / (float)100000.0; -# ifdef PNG_READ_GAMMA_SUPPORTED - png_ptr->gamma = file_gamma; -# endif - png_set_gAMA(png_ptr, info_ptr, file_gamma); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED - png_set_gAMA_fixed(png_ptr, info_ptr, igamma); -#endif + png_colorspace_set_gamma(png_ptr, &png_ptr->colorspace, igamma); + png_colorspace_sync(png_ptr, info_ptr); } #endif -#if defined(PNG_READ_sBIT_SUPPORTED) +#ifdef PNG_READ_sBIT_SUPPORTED void /* PRIVATE */ -png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_sBIT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_size_t truelen; + unsigned int truelen; png_byte buf[4]; - png_debug(1, "in png_handle_sBIT\n"); + png_debug(1, "in png_handle_sBIT"); buf[0] = buf[1] = buf[2] = buf[3] = 0; if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before sBIT"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Invalid sBIT after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } - else if (png_ptr->mode & PNG_HAVE_PLTE) - { - /* Should be an error, but we can cope with it */ - png_warning(png_ptr, "Out of place sBIT chunk"); - } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) { - png_warning(png_ptr, "Duplicate sBIT chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) truelen = 3; + else - truelen = (png_size_t)png_ptr->channels; + truelen = png_ptr->channels; if (length != truelen || length > 4) { - png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_chunk_benign_error(png_ptr, "invalid"); png_crc_finish(png_ptr, length); return; } png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) return; @@ -709,6 +1145,7 @@ png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_ptr->sig_bit.blue = buf[2]; png_ptr->sig_bit.alpha = buf[3]; } + else { png_ptr->sig_bit.gray = buf[0]; @@ -717,551 +1154,606 @@ png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) png_ptr->sig_bit.blue = buf[0]; png_ptr->sig_bit.alpha = buf[1]; } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); } #endif -#if defined(PNG_READ_cHRM_SUPPORTED) +#ifdef PNG_READ_cHRM_SUPPORTED void /* PRIVATE */ -png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_cHRM(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_byte buf[4]; -#ifdef PNG_FLOATING_POINT_SUPPORTED - float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; -#endif - png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, - int_y_green, int_x_blue, int_y_blue; + png_byte buf[32]; + png_xy xy; - png_uint_32 uint_x, uint_y; - - png_debug(1, "in png_handle_cHRM\n"); + png_debug(1, "in png_handle_cHRM"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before cHRM"); - else if (png_ptr->mode & PNG_HAVE_IDAT) - { - png_warning(png_ptr, "Invalid cHRM after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - else if (png_ptr->mode & PNG_HAVE_PLTE) - /* Should be an error, but we can cope with it */ - png_warning(png_ptr, "Missing PLTE before cHRM"); + png_chunk_error(png_ptr, "missing IHDR"); - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) -#if defined(PNG_READ_sRGB_SUPPORTED) - && !(info_ptr->valid & PNG_INFO_sRGB) -#endif - ) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Duplicate cHRM chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } if (length != 32) { - png_warning(png_ptr, "Incorrect cHRM chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } - png_crc_read(png_ptr, buf, 4); - uint_x = png_get_uint_32(buf); + png_crc_read(png_ptr, buf, 32); - png_crc_read(png_ptr, buf, 4); - uint_y = png_get_uint_32(buf); - - if (uint_x > 80000L || uint_y > 80000L || - uint_x + uint_y > 100000L) - { - png_warning(png_ptr, "Invalid cHRM white point"); - png_crc_finish(png_ptr, 24); - return; - } - int_x_white = (png_fixed_point)uint_x; - int_y_white = (png_fixed_point)uint_y; - - png_crc_read(png_ptr, buf, 4); - uint_x = png_get_uint_32(buf); - - png_crc_read(png_ptr, buf, 4); - uint_y = png_get_uint_32(buf); - - if (uint_x + uint_y > 100000L) - { - png_warning(png_ptr, "Invalid cHRM red point"); - png_crc_finish(png_ptr, 16); - return; - } - int_x_red = (png_fixed_point)uint_x; - int_y_red = (png_fixed_point)uint_y; - - png_crc_read(png_ptr, buf, 4); - uint_x = png_get_uint_32(buf); - - png_crc_read(png_ptr, buf, 4); - uint_y = png_get_uint_32(buf); - - if (uint_x + uint_y > 100000L) - { - png_warning(png_ptr, "Invalid cHRM green point"); - png_crc_finish(png_ptr, 8); - return; - } - int_x_green = (png_fixed_point)uint_x; - int_y_green = (png_fixed_point)uint_y; - - png_crc_read(png_ptr, buf, 4); - uint_x = png_get_uint_32(buf); - - png_crc_read(png_ptr, buf, 4); - uint_y = png_get_uint_32(buf); - - if (uint_x + uint_y > 100000L) - { - png_warning(png_ptr, "Invalid cHRM blue point"); - png_crc_finish(png_ptr, 0); - return; - } - int_x_blue = (png_fixed_point)uint_x; - int_y_blue = (png_fixed_point)uint_y; - -#ifdef PNG_FLOATING_POINT_SUPPORTED - white_x = (float)int_x_white / (float)100000.0; - white_y = (float)int_y_white / (float)100000.0; - red_x = (float)int_x_red / (float)100000.0; - red_y = (float)int_y_red / (float)100000.0; - green_x = (float)int_x_green / (float)100000.0; - green_y = (float)int_y_green / (float)100000.0; - blue_x = (float)int_x_blue / (float)100000.0; - blue_y = (float)int_y_blue / (float)100000.0; -#endif - -#if defined(PNG_READ_sRGB_SUPPORTED) - if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) - { - if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || - PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || - PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || - PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || - PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || - PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || - PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || - PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) - { - png_warning(png_ptr, - "Ignoring incorrect cHRM value when sRGB is also present"); -#ifndef PNG_NO_CONSOLE_IO -#ifdef PNG_FLOATING_POINT_SUPPORTED - fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n", - white_x, white_y, red_x, red_y); - fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n", - green_x, green_y, blue_x, blue_y); -#else - fprintf(stderr,"wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", - int_x_white, int_y_white, int_x_red, int_y_red); - fprintf(stderr,"gx=%ld, gy=%ld, bx=%ld, by=%ld\n", - int_x_green, int_y_green, int_x_blue, int_y_blue); -#endif -#endif /* PNG_NO_CONSOLE_IO */ - } - png_crc_finish(png_ptr, 0); - return; - } -#endif /* PNG_READ_sRGB_SUPPORTED */ - -#ifdef PNG_FLOATING_POINT_SUPPORTED - png_set_cHRM(png_ptr, info_ptr, - white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED - png_set_cHRM_fixed(png_ptr, info_ptr, - int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, - int_y_green, int_x_blue, int_y_blue); -#endif if (png_crc_finish(png_ptr, 0)) return; + + xy.whitex = png_get_fixed_point(NULL, buf); + xy.whitey = png_get_fixed_point(NULL, buf + 4); + xy.redx = png_get_fixed_point(NULL, buf + 8); + xy.redy = png_get_fixed_point(NULL, buf + 12); + xy.greenx = png_get_fixed_point(NULL, buf + 16); + xy.greeny = png_get_fixed_point(NULL, buf + 20); + xy.bluex = png_get_fixed_point(NULL, buf + 24); + xy.bluey = png_get_fixed_point(NULL, buf + 28); + + if (xy.whitex == PNG_FIXED_ERROR || + xy.whitey == PNG_FIXED_ERROR || + xy.redx == PNG_FIXED_ERROR || + xy.redy == PNG_FIXED_ERROR || + xy.greenx == PNG_FIXED_ERROR || + xy.greeny == PNG_FIXED_ERROR || + xy.bluex == PNG_FIXED_ERROR || + xy.bluey == PNG_FIXED_ERROR) + { + png_chunk_benign_error(png_ptr, "invalid values"); + return; + } + + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + return; + + if (png_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) + { + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + png_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + (void)png_colorspace_set_chromaticities(png_ptr, &png_ptr->colorspace, &xy, + 1/*prefer cHRM values*/); + png_colorspace_sync(png_ptr, info_ptr); } #endif -#if defined(PNG_READ_sRGB_SUPPORTED) +#ifdef PNG_READ_sRGB_SUPPORTED void /* PRIVATE */ -png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_sRGB(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - int intent; - png_byte buf[1]; + png_byte intent; - png_debug(1, "in png_handle_sRGB\n"); + png_debug(1, "in png_handle_sRGB"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before sRGB"); - else if (png_ptr->mode & PNG_HAVE_IDAT) - { - png_warning(png_ptr, "Invalid sRGB after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - else if (png_ptr->mode & PNG_HAVE_PLTE) - /* Should be an error, but we can cope with it */ - png_warning(png_ptr, "Out of place sRGB chunk"); + png_chunk_error(png_ptr, "missing IHDR"); - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Duplicate sRGB chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } if (length != 1) { - png_warning(png_ptr, "Incorrect sRGB chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } - png_crc_read(png_ptr, buf, 1); + png_crc_read(png_ptr, &intent, 1); + if (png_crc_finish(png_ptr, 0)) return; - intent = buf[0]; - /* check for bad intent */ - if (intent >= PNG_sRGB_INTENT_LAST) + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) + return; + + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. + */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) { - png_warning(png_ptr, "Unknown sRGB intent"); + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + png_chunk_benign_error(png_ptr, "too many profiles"); return; } -#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) - { - png_fixed_point igamma; -#ifdef PNG_FIXED_POINT_SUPPORTED - igamma=info_ptr->int_gamma; -#else -# ifdef PNG_FLOATING_POINT_SUPPORTED - igamma=(png_fixed_point)(info_ptr->gamma * 100000.); -# endif -#endif - if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) - { - png_warning(png_ptr, - "Ignoring incorrect gAMA value when sRGB is also present"); -#ifndef PNG_NO_CONSOLE_IO -# ifdef PNG_FIXED_POINT_SUPPORTED - fprintf(stderr,"incorrect gamma=(%d/100000)\n",(int)png_ptr->int_gamma); -# else -# ifdef PNG_FLOATING_POINT_SUPPORTED - fprintf(stderr,"incorrect gamma=%f\n",png_ptr->gamma); -# endif -# endif -#endif - } - } -#endif /* PNG_READ_gAMA_SUPPORTED */ - -#ifdef PNG_READ_cHRM_SUPPORTED -#ifdef PNG_FIXED_POINT_SUPPORTED - if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) - if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || - PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || - PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || - PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || - PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || - PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) - { - png_warning(png_ptr, - "Ignoring incorrect cHRM value when sRGB is also present"); - } -#endif /* PNG_FIXED_POINT_SUPPORTED */ -#endif /* PNG_READ_cHRM_SUPPORTED */ - - png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); + (void)png_colorspace_set_sRGB(png_ptr, &png_ptr->colorspace, intent); + png_colorspace_sync(png_ptr, info_ptr); } #endif /* PNG_READ_sRGB_SUPPORTED */ -#if defined(PNG_READ_iCCP_SUPPORTED) +#ifdef PNG_READ_iCCP_SUPPORTED void /* PRIVATE */ -png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) -/* Note: this does not properly handle chunks that are > 64K under DOS */ +png_handle_iCCP(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) +/* Note: this does not properly handle profiles that are > 64K under DOS */ { - png_charp chunkdata; - png_byte compression_type; - png_bytep pC; - png_charp profile; - png_uint_32 skip = 0; - png_uint_32 profile_size, profile_length; - png_size_t slength, prefix_length, data_length; + png_const_charp errmsg = NULL; /* error message output, or no error */ + int finished = 0; /* crc checked */ - png_debug(1, "in png_handle_iCCP\n"); + png_debug(1, "in png_handle_iCCP"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before iCCP"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + png_chunk_error(png_ptr, "missing IHDR"); + + else if (png_ptr->mode & (PNG_HAVE_IDAT|PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Invalid iCCP after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); 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)) + /* Consistent with all the above colorspace handling an obviously *invalid* + * chunk is just ignored, so does not invalidate the color space. An + * alternative is to set the 'invalid' flags at the start of this routine + * and only clear them in they were not set before and all the tests pass. + * The minimum 'deflate' stream is assumed to be just the 2 byte header and 4 + * byte checksum. The keyword must be one character and there is a + * terminator (0) byte and the compression method. + */ + if (length < 9) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too short"); + return; + } + + /* If a colorspace error has already been output skip this chunk */ + if (png_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) { - png_warning(png_ptr, "Duplicate iCCP chunk"); png_crc_finish(png_ptr, length); return; } -#ifdef PNG_MAX_MALLOC_64K - if (length > (png_uint_32)65535L) + /* Only one sRGB or iCCP chunk is allowed, use the HAVE_INTENT flag to detect + * this. + */ + if ((png_ptr->colorspace.flags & PNG_COLORSPACE_HAVE_INTENT) == 0) { - png_warning(png_ptr, "iCCP chunk too large to fit in memory"); - skip = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; - } -#endif + uInt read_length, keyword_length; + char keyword[81]; - chunkdata = (png_charp)png_malloc(png_ptr, length + 1); - slength = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + /* Find the keyword; the keyword plus separator and compression method + * bytes can be at most 81 characters long. + */ + read_length = 81; /* maximum */ + if (read_length > length) + read_length = (uInt)length; - if (png_crc_finish(png_ptr, skip)) - { - png_free(png_ptr, chunkdata); - return; + png_crc_read(png_ptr, (png_bytep)keyword, read_length); + length -= read_length; + + keyword_length = 0; + while (keyword_length < 80 && keyword_length < read_length && + keyword[keyword_length] != 0) + ++keyword_length; + + /* TODO: make the keyword checking common */ + if (keyword_length >= 1 && keyword_length <= 79) + { + /* We only understand '0' compression - deflate - so if we get a + * different value we can't safely decode the chunk. + */ + if (keyword_length+1 < read_length && + keyword[keyword_length+1] == PNG_COMPRESSION_TYPE_BASE) + { + read_length -= keyword_length+2; + + if (png_inflate_claim(png_ptr, png_iCCP, + png_ptr->flags & PNG_FLAG_BENIGN_ERRORS_WARN ? 15 : 0) == Z_OK) + { + unsigned char profile_header[132]; + unsigned char local_buffer[PNG_INFLATE_BUF_SIZE]; + png_alloc_size_t size = (sizeof profile_header); + + png_ptr->zstream.next_in = (Bytef*)keyword + (keyword_length+2); + png_ptr->zstream.avail_in = read_length; + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, profile_header, &size, + 0/*finish: don't, because the output is too small*/); + + if (size == 0) + { + /* We have the ICC profile header; do the basic header checks. + */ + const png_uint_32 profile_length = + png_get_uint_32(profile_header); + + if (png_icc_check_length(png_ptr, &png_ptr->colorspace, + keyword, profile_length)) + { + /* The length is apparently ok, so we can check the 132 + * byte header. + */ + if (png_icc_check_header(png_ptr, &png_ptr->colorspace, + keyword, profile_length, profile_header, + png_ptr->color_type)) + { + /* Now read the tag table; a variable size buffer is + * needed at this point, allocate one for the whole + * profile. The header check has already validated + * that none of these stuff will overflow. + */ + const png_uint_32 tag_count = png_get_uint_32( + profile_header+128); + png_bytep profile = png_read_buffer(png_ptr, + profile_length, 2/*silent*/); + + if (profile != NULL) + { + memcpy(profile, profile_header, + (sizeof profile_header)); + + size = 12 * tag_count; + + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, + profile + (sizeof profile_header), &size, 0); + + /* Still expect a a buffer error because we expect + * there to be some tag data! + */ + if (size == 0) + { + if (png_icc_check_tag_table(png_ptr, + &png_ptr->colorspace, keyword, profile_length, + profile)) + { + /* The profile has been validated for basic + * security issues, so read the whole thing in. + */ + size = profile_length - (sizeof profile_header) + - 12 * tag_count; + + (void)png_inflate_read(png_ptr, local_buffer, + (sizeof local_buffer), &length, + profile + (sizeof profile_header) + + 12 * tag_count, &size, 1/*finish*/); + + if (length > 0 && !(png_ptr->flags & + PNG_FLAG_BENIGN_ERRORS_WARN)) + errmsg = "extra compressed data"; + + /* But otherwise allow extra data: */ + else if (size == 0) + { + if (length > 0) + { + /* This can be handled completely, so + * keep going. + */ + png_chunk_warning(png_ptr, + "extra compressed data"); + } + + png_crc_finish(png_ptr, length); + finished = 1; + +# ifdef PNG_sRGB_SUPPORTED + /* Check for a match against sRGB */ + png_icc_set_sRGB(png_ptr, + &png_ptr->colorspace, profile, + png_ptr->zstream.adler); +# endif + + /* Steal the profile for info_ptr. */ + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, + PNG_FREE_ICCP, 0); + + info_ptr->iccp_name = png_voidcast(char*, + png_malloc_base(png_ptr, + keyword_length+1)); + if (info_ptr->iccp_name != NULL) + { + memcpy(info_ptr->iccp_name, keyword, + keyword_length+1); + info_ptr->iccp_proflen = + profile_length; + info_ptr->iccp_profile = profile; + png_ptr->read_buffer = NULL; /*steal*/ + info_ptr->free_me |= PNG_FREE_ICCP; + info_ptr->valid |= PNG_INFO_iCCP; + } + + else + { + png_ptr->colorspace.flags |= + PNG_COLORSPACE_INVALID; + errmsg = "out of memory"; + } + } + + /* else the profile remains in the read + * buffer which gets reused for subsequent + * chunks. + */ + + if (info_ptr != NULL) + png_colorspace_sync(png_ptr, info_ptr); + + if (errmsg == NULL) + { + png_ptr->zowner = 0; + return; + } + } + + else if (size > 0) + errmsg = "truncated"; + + else + errmsg = png_ptr->zstream.msg; + } + + /* else png_icc_check_tag_table output an error */ + } + + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "out of memory"; + } + + /* else png_icc_check_header output an error */ + } + + /* else png_icc_check_length output an error */ + } + + else /* profile truncated */ + errmsg = png_ptr->zstream.msg; + + /* Release the stream */ + png_ptr->zowner = 0; + } + + else /* png_inflate_claim failed */ + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "bad compression method"; /* or missing */ + } + + else + errmsg = "bad keyword"; } - chunkdata[slength] = 0x00; + else + errmsg = "too many profiles"; - for (profile = chunkdata; *profile; profile++) - /* empty loop to find end of name */ ; + /* Failure: the reason is in 'errmsg' */ + if (!finished) + png_crc_finish(png_ptr, length); - ++profile; - - /* there should be at least one zero (the compression type byte) - following the separator, and we should be on it */ - if ( profile >= chunkdata + slength - 1) - { - png_free(png_ptr, chunkdata); - png_warning(png_ptr, "Malformed iCCP chunk"); - return; - } - - /* compression_type should always be zero */ - compression_type = *profile++; - if (compression_type) - { - png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); - compression_type=0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 - wrote nonzero) */ - } - - prefix_length = profile - chunkdata; - chunkdata = png_decompress_chunk(png_ptr, compression_type, chunkdata, - slength, prefix_length, &data_length); - - profile_length = data_length - prefix_length; - - if ( prefix_length > data_length || profile_length < 4) - { - png_free(png_ptr, chunkdata); - png_warning(png_ptr, "Profile size field missing from iCCP chunk"); - return; - } - - /* Check the profile_size recorded in the first 32 bits of the ICC profile */ - pC = (png_bytep)(chunkdata+prefix_length); - profile_size = ((*(pC ))<<24) | - ((*(pC+1))<<16) | - ((*(pC+2))<< 8) | - ((*(pC+3)) ); - - if(profile_size < profile_length) - profile_length = profile_size; - - if(profile_size > profile_length) - { - png_free(png_ptr, chunkdata); - png_warning(png_ptr, "Ignoring truncated iCCP profile."); - return; - } - - png_set_iCCP(png_ptr, info_ptr, chunkdata, compression_type, - chunkdata + prefix_length, profile_length); - png_free(png_ptr, chunkdata); + png_ptr->colorspace.flags |= PNG_COLORSPACE_INVALID; + png_colorspace_sync(png_ptr, info_ptr); + if (errmsg != NULL) /* else already output */ + png_chunk_benign_error(png_ptr, errmsg); } #endif /* PNG_READ_iCCP_SUPPORTED */ -#if defined(PNG_READ_sPLT_SUPPORTED) +#ifdef PNG_READ_sPLT_SUPPORTED void /* PRIVATE */ -png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_sPLT(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) /* Note: this does not properly handle chunks that are > 64K under DOS */ { - png_bytep chunkdata; - png_bytep entry_start; + png_bytep entry_start, buffer; png_sPLT_t new_palette; -#ifdef PNG_NO_POINTER_INDEXING png_sPLT_entryp pp; -#endif - int data_length, entry_size, i; + png_uint_32 data_length; + int entry_size, i; png_uint_32 skip = 0; - png_size_t slength; + png_uint_32 dl; + png_size_t max_dl; - png_debug(1, "in png_handle_sPLT\n"); + png_debug(1, "in png_handle_sPLT"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_warning(png_ptr, "No space in chunk cache for sPLT"); + png_crc_finish(png_ptr, length); + return; + } + } +#endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before sPLT"); + png_chunk_error(png_ptr, "missing IHDR"); + else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid sPLT after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } #ifdef PNG_MAX_MALLOC_64K - if (length > (png_uint_32)65535L) + if (length > 65535U) { - png_warning(png_ptr, "sPLT chunk too large to fit in memory"); - skip = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; } #endif - chunkdata = (png_bytep)png_malloc(png_ptr, length + 1); - slength = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)chunkdata, slength); - - if (png_crc_finish(png_ptr, skip)) + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + if (buffer == NULL) { - png_free(png_ptr, chunkdata); + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); return; } - chunkdata[slength] = 0x00; - for (entry_start = chunkdata; *entry_start; entry_start++) - /* empty loop to find end of name */ ; + /* WARNING: this may break if size_t is less than 32 bits; it is assumed + * that the PNG_MAX_MALLOC_64K test is enabled in this case, but this is a + * potential breakage point if the types in pngconf.h aren't exactly right. + */ + png_crc_read(png_ptr, buffer, length); + + if (png_crc_finish(png_ptr, skip)) + return; + + buffer[length] = 0; + + for (entry_start = buffer; *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) + /* A sample depth should follow the separator, and we should be on it */ + if (entry_start > buffer + length - 2) { - png_free(png_ptr, chunkdata); png_warning(png_ptr, "malformed sPLT chunk"); return; } new_palette.depth = *entry_start++; entry_size = (new_palette.depth == 8 ? 6 : 10); - data_length = (slength - (entry_start - chunkdata)); + /* This must fit in a png_uint_32 because it is derived from the original + * chunk data length. + */ + data_length = length - (png_uint_32)(entry_start - buffer); - /* integrity-check the data length */ + /* Integrity-check the data length */ if (data_length % entry_size) { - png_free(png_ptr, chunkdata); png_warning(png_ptr, "sPLT chunk has bad length"); return; } - new_palette.nentries = (png_int_32) ( data_length / entry_size); - if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX / - png_sizeof(png_sPLT_entry))) + dl = (png_int_32)(data_length / entry_size); + max_dl = PNG_SIZE_MAX / (sizeof (png_sPLT_entry)); + + if (dl > max_dl) { png_warning(png_ptr, "sPLT chunk too long"); return; } + + new_palette.nentries = (png_int_32)(data_length / entry_size); + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( - png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + png_ptr, new_palette.nentries * (sizeof (png_sPLT_entry))); + if (new_palette.entries == NULL) { png_warning(png_ptr, "sPLT chunk requires too much memory"); return; } -#ifndef PNG_NO_POINTER_INDEXING +#ifdef PNG_POINTER_INDEXING_SUPPORTED for (i = 0; i < new_palette.nentries; i++) { - png_sPLT_entryp pp = new_palette.entries + i; + pp = new_palette.entries + i; if (new_palette.depth == 8) { - pp->red = *entry_start++; - pp->green = *entry_start++; - pp->blue = *entry_start++; - pp->alpha = *entry_start++; + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; } + else { - pp->red = png_get_uint_16(entry_start); entry_start += 2; - pp->green = png_get_uint_16(entry_start); entry_start += 2; - pp->blue = png_get_uint_16(entry_start); entry_start += 2; - pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; } #else pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) { if (new_palette.depth == 8) { - pp[i].red = *entry_start++; - pp[i].green = *entry_start++; - pp[i].blue = *entry_start++; - pp[i].alpha = *entry_start++; + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; } + else { - pp[i].red = png_get_uint_16(entry_start); entry_start += 2; - pp[i].green = png_get_uint_16(entry_start); entry_start += 2; - pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; - pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; } - pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + + pp[i].frequency = png_get_uint_16(entry_start); entry_start += 2; } #endif - /* discard all chunk data except the name and stash that */ - new_palette.name = (png_charp)chunkdata; + /* Discard all chunk data except the name and stash that */ + new_palette.name = (png_charp)buffer; png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); - png_free(png_ptr, chunkdata); png_free(png_ptr, new_palette.entries); } #endif /* PNG_READ_sPLT_SUPPORTED */ -#if defined(PNG_READ_tRNS_SUPPORTED) +#ifdef PNG_READ_tRNS_SUPPORTED void /* PRIVATE */ -png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_tRNS(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; - int bit_mask; - png_debug(1, "in png_handle_tRNS\n"); - - /* For non-indexed color, mask off any bits in the tRNS value that - * exceed the bit depth. Some creators were writing extra bits there. - * This is not needed for indexed color. */ - bit_mask = (1 << png_ptr->bit_depth) - 1; + png_debug(1, "in png_handle_tRNS"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before tRNS"); + png_chunk_error(png_ptr, "missing IHDR"); + else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid tRNS after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) { - png_warning(png_ptr, "Duplicate tRNS chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } @@ -1271,58 +1763,60 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) if (length != 2) { - png_warning(png_ptr, "Incorrect tRNS chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } png_crc_read(png_ptr, buf, 2); png_ptr->num_trans = 1; - png_ptr->trans_values.gray = png_get_uint_16(buf) & bit_mask; + png_ptr->trans_color.gray = png_get_uint_16(buf); } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) { png_byte buf[6]; if (length != 6) { - png_warning(png_ptr, "Incorrect tRNS chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } - png_crc_read(png_ptr, buf, (png_size_t)length); + + png_crc_read(png_ptr, buf, length); png_ptr->num_trans = 1; - png_ptr->trans_values.red = png_get_uint_16(buf) & bit_mask; - png_ptr->trans_values.green = png_get_uint_16(buf + 2) & bit_mask; - png_ptr->trans_values.blue = png_get_uint_16(buf + 4) & bit_mask; + png_ptr->trans_color.red = png_get_uint_16(buf); + png_ptr->trans_color.green = png_get_uint_16(buf + 2); + png_ptr->trans_color.blue = png_get_uint_16(buf + 4); } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (!(png_ptr->mode & PNG_HAVE_PLTE)) { - /* Should be an error, but we can cope with it. */ - png_warning(png_ptr, "Missing PLTE before tRNS"); - } - if (length > (png_uint_32)png_ptr->num_palette || - length > PNG_MAX_PALETTE_LENGTH) - { - png_warning(png_ptr, "Incorrect tRNS chunk length"); + /* TODO: is this actually an error in the ISO spec? */ png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } - if (length == 0) + + if (length > png_ptr->num_palette || length > PNG_MAX_PALETTE_LENGTH || + length == 0) { - png_warning(png_ptr, "Zero length tRNS chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } - png_crc_read(png_ptr, readbuf, (png_size_t)length); + + png_crc_read(png_ptr, readbuf, length); png_ptr->num_trans = (png_uint_16)length; } + else { - png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid with alpha channel"); return; } @@ -1332,136 +1826,147 @@ png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) return; } + /* TODO: this is a horrible side effect in the palette case because the + * png_struct ends up with a pointer to the tRNS buffer owned by the + * png_info. Fix this. + */ png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, - &(png_ptr->trans_values)); + &(png_ptr->trans_color)); } #endif -#if defined(PNG_READ_bKGD_SUPPORTED) +#ifdef PNG_READ_bKGD_SUPPORTED void /* PRIVATE */ -png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_bKGD(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_size_t truelen; + unsigned int truelen; png_byte buf[6]; + png_color_16 background; - png_debug(1, "in png_handle_bKGD\n"); + png_debug(1, "in png_handle_bKGD"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before bKGD"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE))) { - png_warning(png_ptr, "Invalid bKGD after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && - !(png_ptr->mode & PNG_HAVE_PLTE)) - { - png_warning(png_ptr, "Missing PLTE before bKGD"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) { - png_warning(png_ptr, "Duplicate bKGD chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) truelen = 6; + else truelen = 2; if (length != truelen) { - png_warning(png_ptr, "Incorrect bKGD chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) return; /* We convert the index value into RGB components so that we can allow * arbitrary RGB values for background when we have transparency, and * so it is easy to determine the RGB values of the background color - * from the info_ptr struct. */ + * from the info_ptr struct. + */ if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { - png_ptr->background.index = buf[0]; - if(info_ptr->num_palette) + background.index = buf[0]; + + if (info_ptr && info_ptr->num_palette) { - if(buf[0] > info_ptr->num_palette) - { - png_warning(png_ptr, "Incorrect bKGD chunk index value"); - return; - } - png_ptr->background.red = - (png_uint_16)png_ptr->palette[buf[0]].red; - png_ptr->background.green = - (png_uint_16)png_ptr->palette[buf[0]].green; - png_ptr->background.blue = - (png_uint_16)png_ptr->palette[buf[0]].blue; + if (buf[0] >= info_ptr->num_palette) + { + png_chunk_benign_error(png_ptr, "invalid index"); + return; + } + + background.red = (png_uint_16)png_ptr->palette[buf[0]].red; + background.green = (png_uint_16)png_ptr->palette[buf[0]].green; + background.blue = (png_uint_16)png_ptr->palette[buf[0]].blue; } - } - else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ - { - png_ptr->background.red = - png_ptr->background.green = - png_ptr->background.blue = - png_ptr->background.gray = png_get_uint_16(buf); - } - else - { - png_ptr->background.red = png_get_uint_16(buf); - png_ptr->background.green = png_get_uint_16(buf + 2); - png_ptr->background.blue = png_get_uint_16(buf + 4); + + else + background.red = background.green = background.blue = 0; + + background.gray = 0; } - png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + background.index = 0; + background.red = + background.green = + background.blue = + background.gray = png_get_uint_16(buf); + } + + else + { + background.index = 0; + background.red = png_get_uint_16(buf); + background.green = png_get_uint_16(buf + 2); + background.blue = png_get_uint_16(buf + 4); + background.gray = 0; + } + + png_set_bKGD(png_ptr, info_ptr, &background); } #endif -#if defined(PNG_READ_hIST_SUPPORTED) +#ifdef PNG_READ_hIST_SUPPORTED void /* PRIVATE */ -png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_hIST(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { unsigned int num, i; png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; - png_debug(1, "in png_handle_hIST\n"); + png_debug(1, "in png_handle_hIST"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before hIST"); - else if (png_ptr->mode & PNG_HAVE_IDAT) + png_chunk_error(png_ptr, "missing IHDR"); + + else if ((png_ptr->mode & PNG_HAVE_IDAT) || !(png_ptr->mode & PNG_HAVE_PLTE)) { - png_warning(png_ptr, "Invalid hIST after IDAT"); - png_crc_finish(png_ptr, length); - return; - } - else if (!(png_ptr->mode & PNG_HAVE_PLTE)) - { - png_warning(png_ptr, "Missing PLTE before hIST"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) { - png_warning(png_ptr, "Duplicate hIST chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } num = length / 2 ; - if (num != (unsigned int) png_ptr->num_palette || num > - (unsigned int) PNG_MAX_PALETTE_LENGTH) + + if (num != png_ptr->num_palette || num > PNG_MAX_PALETTE_LENGTH) { - png_warning(png_ptr, "Incorrect hIST chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } @@ -1480,39 +1985,42 @@ png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } #endif -#if defined(PNG_READ_pHYs_SUPPORTED) +#ifdef PNG_READ_pHYs_SUPPORTED void /* PRIVATE */ -png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_pHYs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[9]; png_uint_32 res_x, res_y; int unit_type; - png_debug(1, "in png_handle_pHYs\n"); + png_debug(1, "in png_handle_pHYs"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before pHYs"); + png_chunk_error(png_ptr, "missing IHDR"); + else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid pHYs after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) { - png_warning(png_ptr, "Duplicate pHYs chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } if (length != 9) { - png_warning(png_ptr, "Incorrect pHYs chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) return; @@ -1523,39 +2031,42 @@ png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } #endif -#if defined(PNG_READ_oFFs_SUPPORTED) +#ifdef PNG_READ_oFFs_SUPPORTED void /* PRIVATE */ -png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_oFFs(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[9]; png_int_32 offset_x, offset_y; int unit_type; - png_debug(1, "in png_handle_oFFs\n"); + png_debug(1, "in png_handle_oFFs"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before oFFs"); + png_chunk_error(png_ptr, "missing IHDR"); + else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid oFFs after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) { - png_warning(png_ptr, "Duplicate oFFs chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } if (length != 9) { - png_warning(png_ptr, "Incorrect oFFs chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) return; @@ -1566,288 +2077,245 @@ 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) */ +#ifdef PNG_READ_pCAL_SUPPORTED +/* Read the pCAL chunk (described in the PNG Extensions document) */ void /* PRIVATE */ -png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_pCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_charp purpose; png_int_32 X0, X1; png_byte type, nparams; - png_charp buf, units, endptr; + png_bytep buffer, buf, units, endptr; png_charpp params; - png_size_t slength; int i; - png_debug(1, "in png_handle_pCAL\n"); + png_debug(1, "in png_handle_pCAL"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before pCAL"); + png_chunk_error(png_ptr, "missing IHDR"); + else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid pCAL after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) { - png_warning(png_ptr, "Duplicate pCAL chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } - png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)\n", - length + 1); - purpose = (png_charp)png_malloc_warn(png_ptr, length + 1); - if (purpose == NULL) - { - png_warning(png_ptr, "No memory for pCAL purpose."); - return; - } - slength = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)purpose, slength); + png_debug1(2, "Allocating and reading pCAL chunk data (%u bytes)", + length + 1); + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + + if (buffer == NULL) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } + + png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, 0)) - { - png_free(png_ptr, purpose); return; - } - purpose[slength] = 0x00; /* null terminate the last string */ + buffer[length] = 0; /* Null terminate the last string */ - png_debug(3, "Finding end of pCAL purpose string\n"); - for (buf = purpose; *buf; buf++) - /* empty loop */ ; + png_debug(3, "Finding end of pCAL purpose string"); + for (buf = buffer; *buf; buf++) + /* Empty loop */ ; - endptr = purpose + slength; + endptr = buffer + length; /* We need to have at least 12 bytes after the purpose string - in order to get the parameter information. */ + * in order to get the parameter information. + */ if (endptr <= buf + 12) { - png_warning(png_ptr, "Invalid pCAL data"); - png_free(png_ptr, purpose); + png_chunk_benign_error(png_ptr, "invalid"); return; } - png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n"); + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units"); X0 = png_get_int_32((png_bytep)buf+1); X1 = png_get_int_32((png_bytep)buf+5); type = buf[9]; nparams = buf[10]; units = buf + 11; - png_debug(3, "Checking pCAL equation type and number of parameters\n"); + png_debug(3, "Checking pCAL equation type and number of parameters"); /* Check that we have the right number of parameters for known - equation types. */ + * equation types. + */ if ((type == PNG_EQUATION_LINEAR && nparams != 2) || (type == PNG_EQUATION_BASE_E && nparams != 3) || (type == PNG_EQUATION_ARBITRARY && nparams != 3) || (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) { - png_warning(png_ptr, "Invalid pCAL parameters for equation type"); - png_free(png_ptr, purpose); + png_chunk_benign_error(png_ptr, "invalid parameter count"); return; } + else if (type >= PNG_EQUATION_LAST) { - png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + png_chunk_benign_error(png_ptr, "unrecognized equation type"); } for (buf = units; *buf; buf++) /* Empty loop to move past the units string. */ ; - png_debug(3, "Allocating pCAL parameters array\n"); - params = (png_charpp)png_malloc_warn(png_ptr, (png_uint_32)(nparams - *png_sizeof(png_charp))) ; + png_debug(3, "Allocating pCAL parameters array"); + + params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + nparams * (sizeof (png_charp)))); + if (params == NULL) - { - png_free(png_ptr, purpose); - png_warning(png_ptr, "No memory for pCAL params."); - return; - } + { + png_chunk_benign_error(png_ptr, "out of memory"); + return; + } /* Get pointers to the start of each parameter string. */ - for (i = 0; i < (int)nparams; i++) + for (i = 0; i < 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++) + png_debug1(3, "Reading pCAL parameter %d", i); + + for (params[i] = (png_charp)buf; buf <= endptr && *buf != 0; buf++) /* Empty loop to move past each parameter string */ ; /* Make sure we haven't run out of data yet */ if (buf > endptr) { - png_warning(png_ptr, "Invalid pCAL data"); - png_free(png_ptr, purpose); png_free(png_ptr, params); + png_chunk_benign_error(png_ptr, "invalid data"); return; } } - png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams, - units, params); + png_set_pCAL(png_ptr, info_ptr, (png_charp)buffer, X0, X1, type, nparams, + (png_charp)units, params); - png_free(png_ptr, purpose); png_free(png_ptr, params); } #endif -#if defined(PNG_READ_sCAL_SUPPORTED) -/* read the sCAL chunk */ +#ifdef PNG_READ_sCAL_SUPPORTED +/* Read the sCAL chunk */ void /* PRIVATE */ -png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_sCAL(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_charp buffer, ep; -#ifdef PNG_FLOATING_POINT_SUPPORTED - double width, height; - png_charp vp; -#else -#ifdef PNG_FIXED_POINT_SUPPORTED - png_charp swidth, sheight; -#endif -#endif - png_size_t slength; + png_bytep buffer; + png_size_t i; + int state; - png_debug(1, "in png_handle_sCAL\n"); + png_debug(1, "in png_handle_sCAL"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before sCAL"); + png_chunk_error(png_ptr, "missing IHDR"); + else if (png_ptr->mode & PNG_HAVE_IDAT) { - png_warning(png_ptr, "Invalid sCAL after IDAT"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of place"); return; } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) { - png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); + return; + } + + /* Need unit type, width, \0, height: minimum 4 bytes */ + else if (length < 4) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%u bytes)", + length + 1); + + buffer = png_read_buffer(png_ptr, length+1, 2/*silent*/); + + if (buffer == NULL) + { + png_chunk_benign_error(png_ptr, "out of memory"); png_crc_finish(png_ptr, length); return; } - png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)\n", - length + 1); - buffer = (png_charp)png_malloc_warn(png_ptr, length + 1); - if (buffer == NULL) - { - png_warning(png_ptr, "Out of memory while processing sCAL chunk"); - return; - } - slength = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)buffer, slength); + png_crc_read(png_ptr, buffer, length); + buffer[length] = 0; /* Null terminate the last string */ if (png_crc_finish(png_ptr, 0)) + return; + + /* Validate the unit. */ + if (buffer[0] != 1 && buffer[0] != 2) { - png_free(png_ptr, buffer); + png_chunk_benign_error(png_ptr, "invalid unit"); return; } - buffer[slength] = 0x00; /* null terminate the last string */ + /* Validate the ASCII numbers, need two ASCII numbers separated by + * a '\0' and they need to fit exactly in the chunk data. + */ + i = 1; + state = 0; - ep = buffer + 1; /* skip unit byte */ + if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) || + i >= length || buffer[i++] != 0) + png_chunk_benign_error(png_ptr, "bad width format"); -#ifdef PNG_FLOATING_POINT_SUPPORTED - width = png_strtod(png_ptr, ep, &vp); - if (*vp) + else if (!PNG_FP_IS_POSITIVE(state)) + png_chunk_benign_error(png_ptr, "non-positive width"); + + else { - png_warning(png_ptr, "malformed width string in sCAL chunk"); - return; + png_size_t heighti = i; + + state = 0; + if (!png_check_fp_number((png_const_charp)buffer, length, &state, &i) || + i != length) + png_chunk_benign_error(png_ptr, "bad height format"); + + else if (!PNG_FP_IS_POSITIVE(state)) + png_chunk_benign_error(png_ptr, "non-positive height"); + + else + /* This is the (only) success case. */ + png_set_sCAL_s(png_ptr, info_ptr, buffer[0], + (png_charp)buffer+1, (png_charp)buffer+heighti); } -#else -#ifdef PNG_FIXED_POINT_SUPPORTED - swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); - if (swidth == NULL) - { - png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); - return; - } - png_memcpy(swidth, ep, (png_size_t)png_strlen(ep)); -#endif -#endif - - for (ep = buffer; *ep; ep++) - /* empty loop */ ; - ep++; - - if (buffer + slength < ep) - { - png_warning(png_ptr, "Truncated sCAL chunk"); -#if defined(PNG_FIXED_POINT_SUPPORTED) && \ - !defined(PNG_FLOATING_POINT_SUPPORTED) - png_free(png_ptr, swidth); -#endif - png_free(png_ptr, buffer); - return; - } - -#ifdef PNG_FLOATING_POINT_SUPPORTED - height = png_strtod(png_ptr, ep, &vp); - if (*vp) - { - png_warning(png_ptr, "malformed height string in sCAL chunk"); - return; - } -#else -#ifdef PNG_FIXED_POINT_SUPPORTED - sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); - if (swidth == NULL) - { - png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); - return; - } - png_memcpy(sheight, ep, (png_size_t)png_strlen(ep)); -#endif -#endif - - if (buffer + slength < ep -#ifdef PNG_FLOATING_POINT_SUPPORTED - || width <= 0. || height <= 0. -#endif - ) - { - png_warning(png_ptr, "Invalid sCAL data"); - png_free(png_ptr, buffer); -#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) - png_free(png_ptr, swidth); - png_free(png_ptr, sheight); -#endif - return; - } - - -#ifdef PNG_FLOATING_POINT_SUPPORTED - png_set_sCAL(png_ptr, info_ptr, buffer[0], width, height); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED - png_set_sCAL_s(png_ptr, info_ptr, buffer[0], swidth, sheight); -#endif -#endif - - png_free(png_ptr, buffer); -#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) - png_free(png_ptr, swidth); - png_free(png_ptr, sheight); -#endif } #endif -#if defined(PNG_READ_tIME_SUPPORTED) +#ifdef PNG_READ_tIME_SUPPORTED void /* PRIVATE */ -png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_tIME(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { png_byte buf[7]; png_time mod_time; - png_debug(1, "in png_handle_tIME\n"); + png_debug(1, "in png_handle_tIME"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Out of place tIME chunk"); + png_chunk_error(png_ptr, "missing IHDR"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) { - png_warning(png_ptr, "Duplicate tIME chunk"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "duplicate"); return; } @@ -1856,12 +2324,13 @@ png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) if (length != 7) { - png_warning(png_ptr, "Incorrect tIME chunk length"); png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "invalid"); return; } png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) return; @@ -1876,644 +2345,1136 @@ png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) } #endif -#if defined(PNG_READ_tEXt_SUPPORTED) +#ifdef PNG_READ_tEXt_SUPPORTED /* Note: this does not properly handle chunks that are > 64K under DOS */ void /* PRIVATE */ -png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_tEXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_textp text_ptr; + png_text text_info; + png_bytep buffer; png_charp key; png_charp text; png_uint_32 skip = 0; - png_size_t slength; - int ret; - png_debug(1, "in png_handle_tEXt\n"); + png_debug(1, "in png_handle_tEXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before tEXt"); + png_chunk_error(png_ptr, "missing IHDR"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; #ifdef PNG_MAX_MALLOC_64K - if (length > (png_uint_32)65535L) + if (length > 65535U) { - png_warning(png_ptr, "tEXt chunk too large to fit in memory"); - skip = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "too large to fit in memory"); + return; } #endif - key = (png_charp)png_malloc_warn(png_ptr, length + 1); - if (key == NULL) + buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); + + if (buffer == NULL) { - png_warning(png_ptr, "No memory to process text chunk."); + png_chunk_benign_error(png_ptr, "out of memory"); return; } - slength = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)key, slength); + + png_crc_read(png_ptr, buffer, length); if (png_crc_finish(png_ptr, skip)) - { - png_free(png_ptr, key); return; - } - key[slength] = 0x00; + key = (png_charp)buffer; + key[length] = 0; for (text = key; *text; text++) - /* empty loop to find end of key */ ; + /* Empty loop to find end of key */ ; - if (text != key + slength) + if (text != key + length) text++; - text_ptr = (png_textp)png_malloc_warn(png_ptr, - (png_uint_32)png_sizeof(png_text)); - if (text_ptr == NULL) - { - png_warning(png_ptr, "Not enough memory to process text chunk."); - png_free(png_ptr, key); - return; - } - text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; - text_ptr->key = key; -#ifdef PNG_iTXt_SUPPORTED - text_ptr->lang = NULL; - text_ptr->lang_key = NULL; - text_ptr->itxt_length = 0; -#endif - text_ptr->text = text; - text_ptr->text_length = png_strlen(text); + text_info.compression = PNG_TEXT_COMPRESSION_NONE; + text_info.key = key; + text_info.lang = NULL; + text_info.lang_key = NULL; + text_info.itxt_length = 0; + text_info.text = text; + text_info.text_length = strlen(text); - ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); - - png_free(png_ptr, key); - png_free(png_ptr, text_ptr); - if (ret) - png_warning(png_ptr, "Insufficient memory to process text chunk."); + if (png_set_text_2(png_ptr, info_ptr, &text_info, 1)) + png_warning(png_ptr, "Insufficient memory to process text chunk"); } #endif -#if defined(PNG_READ_zTXt_SUPPORTED) -/* note: this does not correctly handle chunks that are > 64K under DOS */ +#ifdef PNG_READ_zTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ void /* PRIVATE */ -png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_zTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_textp text_ptr; - png_charp chunkdata; - png_charp text; - int comp_type; - int ret; - png_size_t slength, prefix_len, data_len; + png_const_charp errmsg = NULL; + png_bytep buffer; + png_uint_32 keyword_length; + + png_debug(1, "in png_handle_zTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif - png_debug(1, "in png_handle_zTXt\n"); if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before zTXt"); + png_chunk_error(png_ptr, "missing IHDR"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; -#ifdef PNG_MAX_MALLOC_64K - /* We will no doubt have problems with chunks even half this size, but - there is no hard and fast rule to tell us where to stop. */ - if (length > (png_uint_32)65535L) - { - png_warning(png_ptr,"zTXt chunk too large to fit in memory"); - png_crc_finish(png_ptr, length); - return; - } -#endif + buffer = png_read_buffer(png_ptr, length, 2/*silent*/); - chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); - if (chunkdata == NULL) + if (buffer == NULL) { - png_warning(png_ptr,"Out of memory processing zTXt chunk."); - return; + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; } - slength = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + png_crc_read(png_ptr, buffer, length); + if (png_crc_finish(png_ptr, 0)) - { - png_free(png_ptr, chunkdata); return; - } - chunkdata[slength] = 0x00; + /* TODO: also check that the keyword contents match the spec! */ + for (keyword_length = 0; + keyword_length < length && buffer[keyword_length] != 0; + ++keyword_length) + /* Empty loop to find end of name */ ; - for (text = chunkdata; *text; text++) - /* empty loop */ ; + if (keyword_length > 79 || keyword_length < 1) + errmsg = "bad keyword"; + + /* zTXt must have some LZ data after the keyword, although it may expand to + * zero bytes; we need a '\0' at the end of the keyword, the compression type + * then the LZ data: + */ + else if (keyword_length + 3 > length) + errmsg = "truncated"; + + else if (buffer[keyword_length+1] != PNG_COMPRESSION_TYPE_BASE) + errmsg = "unknown compression type"; - /* zTXt must have some text after the chunkdataword */ - if (text >= chunkdata + slength - 2) - { - png_warning(png_ptr, "Truncated zTXt chunk"); - png_free(png_ptr, chunkdata); - return; - } else { - comp_type = *(++text); - if (comp_type != PNG_TEXT_COMPRESSION_zTXt) - { - png_warning(png_ptr, "Unknown compression type in zTXt chunk"); - comp_type = PNG_TEXT_COMPRESSION_zTXt; - } - text++; /* skip the compression_method byte */ + png_alloc_size_t uncompressed_length = PNG_SIZE_MAX; + + /* TODO: at present png_decompress_chunk imposes a single application + * level memory limit, this should be split to different values for iCCP + * and text chunks. + */ + if (png_decompress_chunk(png_ptr, length, keyword_length+2, + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + { + png_text text; + + /* It worked; png_ptr->read_buffer now looks like a tEXt chunk except + * for the extra compression type byte and the fact that it isn't + * necessarily '\0' terminated. + */ + buffer = png_ptr->read_buffer; + buffer[uncompressed_length+(keyword_length+2)] = 0; + + text.compression = PNG_TEXT_COMPRESSION_zTXt; + text.key = (png_charp)buffer; + text.text = (png_charp)(buffer + keyword_length+2); + text.text_length = uncompressed_length; + text.itxt_length = 0; + text.lang = NULL; + text.lang_key = NULL; + + if (png_set_text_2(png_ptr, info_ptr, &text, 1)) + errmsg = "insufficient memory"; + } + + else + errmsg = png_ptr->zstream.msg; } - prefix_len = text - chunkdata; - chunkdata = (png_charp)png_decompress_chunk(png_ptr, comp_type, chunkdata, - (png_size_t)length, prefix_len, &data_len); - - text_ptr = (png_textp)png_malloc_warn(png_ptr, - (png_uint_32)png_sizeof(png_text)); - if (text_ptr == NULL) - { - png_warning(png_ptr,"Not enough memory to process zTXt chunk."); - png_free(png_ptr, chunkdata); - return; - } - text_ptr->compression = comp_type; - text_ptr->key = chunkdata; -#ifdef PNG_iTXt_SUPPORTED - text_ptr->lang = NULL; - text_ptr->lang_key = NULL; - text_ptr->itxt_length = 0; -#endif - text_ptr->text = chunkdata + prefix_len; - text_ptr->text_length = data_len; - - ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); - - png_free(png_ptr, text_ptr); - png_free(png_ptr, chunkdata); - if (ret) - png_error(png_ptr, "Insufficient memory to store zTXt chunk."); + if (errmsg != NULL) + png_chunk_benign_error(png_ptr, errmsg); } #endif -#if defined(PNG_READ_iTXt_SUPPORTED) -/* note: this does not correctly handle chunks that are > 64K under DOS */ +#ifdef PNG_READ_iTXt_SUPPORTED +/* Note: this does not correctly handle chunks that are > 64K under DOS */ void /* PRIVATE */ -png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +png_handle_iTXt(png_structrp png_ptr, png_inforp info_ptr, png_uint_32 length) { - png_textp text_ptr; - png_charp chunkdata; - png_charp key, lang, text, lang_key; - int comp_flag; - int comp_type = 0; - int ret; - png_size_t slength, prefix_len, data_len; + png_const_charp errmsg = NULL; + png_bytep buffer; + png_uint_32 prefix_length; - png_debug(1, "in png_handle_iTXt\n"); + png_debug(1, "in png_handle_iTXt"); + +#ifdef PNG_USER_LIMITS_SUPPORTED + if (png_ptr->user_chunk_cache_max != 0) + { + if (png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + return; + } + + if (--png_ptr->user_chunk_cache_max == 1) + { + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + return; + } + } +#endif if (!(png_ptr->mode & PNG_HAVE_IHDR)) - png_error(png_ptr, "Missing IHDR before iTXt"); + png_chunk_error(png_ptr, "missing IHDR"); if (png_ptr->mode & PNG_HAVE_IDAT) png_ptr->mode |= PNG_AFTER_IDAT; -#ifdef PNG_MAX_MALLOC_64K - /* We will no doubt have problems with chunks even half this size, but - there is no hard and fast rule to tell us where to stop. */ - if (length > (png_uint_32)65535L) - { - png_warning(png_ptr,"iTXt chunk too large to fit in memory"); - png_crc_finish(png_ptr, length); - return; - } -#endif + buffer = png_read_buffer(png_ptr, length+1, 1/*warn*/); - chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); - if (chunkdata == NULL) + if (buffer == NULL) { - png_warning(png_ptr, "No memory to process iTXt chunk."); - return; + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "out of memory"); + return; } - slength = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + png_crc_read(png_ptr, buffer, length); + if (png_crc_finish(png_ptr, 0)) - { - png_free(png_ptr, chunkdata); return; + + /* First the keyword. */ + for (prefix_length=0; + prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* Perform a basic check on the keyword length here. */ + if (prefix_length > 79 || prefix_length < 1) + errmsg = "bad keyword"; + + /* Expect keyword, compression flag, compression type, language, translated + * keyword (both may be empty but are 0 terminated) then the text, which may + * be empty. + */ + else if (prefix_length + 5 > length) + errmsg = "truncated"; + + else if (buffer[prefix_length+1] == 0 || + (buffer[prefix_length+1] == 1 && + buffer[prefix_length+2] == PNG_COMPRESSION_TYPE_BASE)) + { + int compressed = buffer[prefix_length+1] != 0; + png_uint_32 language_offset, translated_keyword_offset; + png_alloc_size_t uncompressed_length = 0; + + /* Now the language tag */ + prefix_length += 3; + language_offset = prefix_length; + + for (; prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* WARNING: the length may be invalid here, this is checked below. */ + translated_keyword_offset = ++prefix_length; + + for (; prefix_length < length && buffer[prefix_length] != 0; + ++prefix_length) + /* Empty loop */ ; + + /* prefix_length should now be at the trailing '\0' of the translated + * keyword, but it may already be over the end. None of this arithmetic + * can overflow because chunks are at most 2^31 bytes long, but on 16-bit + * systems the available allocaton may overflow. + */ + ++prefix_length; + + if (!compressed && prefix_length <= length) + uncompressed_length = length - prefix_length; + + else if (compressed && prefix_length < length) + { + uncompressed_length = PNG_SIZE_MAX; + + /* TODO: at present png_decompress_chunk imposes a single application + * level memory limit, this should be split to different values for + * iCCP and text chunks. + */ + if (png_decompress_chunk(png_ptr, length, prefix_length, + &uncompressed_length, 1/*terminate*/) == Z_STREAM_END) + buffer = png_ptr->read_buffer; + + else + errmsg = png_ptr->zstream.msg; + } + + else + errmsg = "truncated"; + + if (errmsg == NULL) + { + png_text text; + + buffer[uncompressed_length+prefix_length] = 0; + + if (compressed) + text.compression = PNG_ITXT_COMPRESSION_NONE; + + else + text.compression = PNG_ITXT_COMPRESSION_zTXt; + + text.key = (png_charp)buffer; + text.lang = (png_charp)buffer + language_offset; + text.lang_key = (png_charp)buffer + translated_keyword_offset; + text.text = (png_charp)buffer + prefix_length; + text.text_length = 0; + text.itxt_length = uncompressed_length; + + if (png_set_text_2(png_ptr, info_ptr, &text, 1)) + errmsg = "insufficient memory"; + } } - chunkdata[slength] = 0x00; - - for (lang = chunkdata; *lang; lang++) - /* empty loop */ ; - lang++; /* skip NUL separator */ - - /* iTXt must have a language tag (possibly empty), two compression bytes, - translated keyword (possibly empty), and possibly some text after the - keyword */ - - if (lang >= chunkdata + slength - 3) - { - png_warning(png_ptr, "Truncated iTXt chunk"); - png_free(png_ptr, chunkdata); - return; - } else - { - comp_flag = *lang++; - comp_type = *lang++; - } + errmsg = "bad compression info"; - for (lang_key = lang; *lang_key; lang_key++) - /* empty loop */ ; - lang_key++; /* skip NUL separator */ - - if (lang_key >= chunkdata + slength) - { - png_warning(png_ptr, "Truncated iTXt chunk"); - png_free(png_ptr, chunkdata); - return; - } - - for (text = lang_key; *text; text++) - /* empty loop */ ; - text++; /* skip NUL separator */ - if (text >= chunkdata + slength) - { - png_warning(png_ptr, "Malformed iTXt chunk"); - png_free(png_ptr, chunkdata); - return; - } - - prefix_len = text - chunkdata; - - key=chunkdata; - if (comp_flag) - chunkdata = png_decompress_chunk(png_ptr, comp_type, chunkdata, - (size_t)length, prefix_len, &data_len); - else - data_len=png_strlen(chunkdata + prefix_len); - text_ptr = (png_textp)png_malloc_warn(png_ptr, - (png_uint_32)png_sizeof(png_text)); - if (text_ptr == NULL) - { - png_warning(png_ptr,"Not enough memory to process iTXt chunk."); - png_free(png_ptr, chunkdata); - return; - } - text_ptr->compression = (int)comp_flag + 1; - text_ptr->lang_key = chunkdata+(lang_key-key); - text_ptr->lang = chunkdata+(lang-key); - text_ptr->itxt_length = data_len; - text_ptr->text_length = 0; - text_ptr->key = chunkdata; - text_ptr->text = chunkdata + prefix_len; - - ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); - - png_free(png_ptr, text_ptr); - png_free(png_ptr, chunkdata); - if (ret) - png_error(png_ptr, "Insufficient memory to store iTXt chunk."); + if (errmsg != NULL) + png_chunk_benign_error(png_ptr, errmsg); } #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) +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +/* Utility function for png_handle_unknown; set up png_ptr::unknown_chunk */ +static int +png_cache_unknown_chunk(png_structrp png_ptr, png_uint_32 length) { - png_uint_32 skip = 0; + png_alloc_size_t limit = PNG_SIZE_MAX; - png_debug(1, "in png_handle_unknown\n"); - - if (png_ptr->mode & PNG_HAVE_IDAT) + if (png_ptr->unknown_chunk.data != NULL) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST PNG_IDAT; -#endif - if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* not an IDAT */ - png_ptr->mode |= PNG_AFTER_IDAT; + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; } - png_check_chunk_name(png_ptr, png_ptr->chunk_name); +# ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED + if (png_ptr->user_chunk_malloc_max > 0 && + png_ptr->user_chunk_malloc_max < limit) + limit = png_ptr->user_chunk_malloc_max; - if (!(png_ptr->chunk_name[0] & 0x20)) +# elif PNG_USER_CHUNK_MALLOC_MAX > 0 + if (PNG_USER_CHUNK_MALLOC_MAX < limit) + limit = PNG_USER_CHUNK_MALLOC_MAX; +# endif + + if (length <= limit) { -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) - if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != - PNG_HANDLE_CHUNK_ALWAYS -#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) - && png_ptr->read_user_chunk_fn == NULL -#endif - ) -#endif - png_chunk_error(png_ptr, "unknown critical chunk"); + PNG_CSTRING_FROM_CHUNK(png_ptr->unknown_chunk.name, png_ptr->chunk_name); + /* The following is safe because of the PNG_SIZE_MAX init above */ + png_ptr->unknown_chunk.size = (png_size_t)length/*SAFE*/; + /* 'mode' is a flag array, only the bottom four bits matter here */ + png_ptr->unknown_chunk.location = (png_byte)png_ptr->mode/*SAFE*/; + + if (length == 0) + png_ptr->unknown_chunk.data = NULL; + + else + { + /* Do a 'warn' here - it is handled below. */ + png_ptr->unknown_chunk.data = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, length)); + } } -#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) - if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) || - (png_ptr->read_user_chunk_fn != NULL)) + if (png_ptr->unknown_chunk.data == NULL && length > 0) { -#ifdef PNG_MAX_MALLOC_64K - if (length > (png_uint_32)65535L) - { - png_warning(png_ptr, "unknown chunk too large to fit in memory"); - skip = length - (png_uint_32)65535L; - length = (png_uint_32)65535L; - } -#endif - png_strncpy((png_charp)png_ptr->unknown_chunk.name, - (png_charp)png_ptr->chunk_name, 5); - png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); - png_ptr->unknown_chunk.size = (png_size_t)length; - png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); -#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) - if(png_ptr->read_user_chunk_fn != NULL) - { - /* callback to user unknown chunk handler */ - int ret; - ret = (*(png_ptr->read_user_chunk_fn)) - (png_ptr, &png_ptr->unknown_chunk); - if (ret < 0) - png_chunk_error(png_ptr, "error in user chunk"); - if (ret == 0) - { - if (!(png_ptr->chunk_name[0] & 0x20)) - if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != - PNG_HANDLE_CHUNK_ALWAYS) - png_chunk_error(png_ptr, "unknown critical chunk"); - png_set_unknown_chunks(png_ptr, info_ptr, - &png_ptr->unknown_chunk, 1); - } - } -#else - png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); -#endif - png_free(png_ptr, png_ptr->unknown_chunk.data); - png_ptr->unknown_chunk.data = NULL; + /* This is benign because we clean up correctly */ + png_crc_finish(png_ptr, length); + png_chunk_benign_error(png_ptr, "unknown chunk exceeds memory limits"); + return 0; } + else -#endif - skip = length; + { + if (length > 0) + png_crc_read(png_ptr, png_ptr->unknown_chunk.data, length); + png_crc_finish(png_ptr, 0); + return 1; + } +} +#endif /* PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ - png_crc_finish(png_ptr, skip); +/* Handle an unknown, or known but disabled, chunk */ +void /* PRIVATE */ +png_handle_unknown(png_structrp png_ptr, png_inforp info_ptr, + png_uint_32 length, int keep) +{ + int handled = 0; /* the chunk was handled */ -#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED) - info_ptr = info_ptr; /* quiet compiler warnings about unused info_ptr */ -#endif + png_debug(1, "in png_handle_unknown"); + +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* NOTE: this code is based on the code in libpng-1.4.12 except for fixing + * the bug which meant that setting a non-default behavior for a specific + * chunk would be ignored (the default was always used unless a user + * callback was installed). + * + * 'keep' is the value from the png_chunk_unknown_handling, the setting for + * this specific chunk_name, if PNG_HANDLE_AS_UNKNOWN_SUPPORTED, if not it + * will always be PNG_HANDLE_CHUNK_AS_DEFAULT and it needs to be set here. + * This is just an optimization to avoid multiple calls to the lookup + * function. + */ +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + keep = png_chunk_unknown_handling(png_ptr, png_ptr->chunk_name); +# endif +# endif + + /* One of the following methods will read the chunk or skip it (at least one + * of these is always defined because this is the only way to switch on + * PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + */ +# ifdef PNG_READ_USER_CHUNKS_SUPPORTED + /* The user callback takes precedence over the chunk keep value, but the + * keep value is still required to validate a save of a critical chunk. + */ + if (png_ptr->read_user_chunk_fn != NULL) + { + if (png_cache_unknown_chunk(png_ptr, length)) + { + /* Callback to user unknown chunk handler */ + int ret = (*(png_ptr->read_user_chunk_fn))(png_ptr, + &png_ptr->unknown_chunk); + + /* ret is: + * negative: An error occured, png_chunk_error will be called. + * zero: The chunk was not handled, the chunk will be discarded + * unless png_set_keep_unknown_chunks has been used to set + * a 'keep' behavior for this particular chunk, in which + * case that will be used. A critical chunk will cause an + * error at this point unless it is to be saved. + * positive: The chunk was handled, libpng will ignore/discard it. + */ + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + + else if (ret == 0) + { + /* If the keep value is 'default' or 'never' override it, but + * still error out on critical chunks unless the keep value is + * 'always' While this is weird it is the behavior in 1.4.12. + * A possible improvement would be to obey the value set for the + * chunk, but this would be an API change that would probably + * damage some applications. + * + * The png_app_warning below catches the case that matters, where + * the application has not set specific save or ignore for this + * chunk or global save or ignore. + */ + if (keep < PNG_HANDLE_CHUNK_IF_SAFE) + { +# ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + if (png_ptr->unknown_default < PNG_HANDLE_CHUNK_IF_SAFE) + { + png_chunk_warning(png_ptr, "Saving unknown chunk:"); + png_app_warning(png_ptr, + "forcing save of an unhandled chunk;" + " please call png_set_keep_unknown_chunks"); + /* with keep = PNG_HANDLE_CHUNK_IF_SAFE */ + } +# endif + keep = PNG_HANDLE_CHUNK_IF_SAFE; + } + } + + else /* chunk was handled */ + { + handled = 1; + /* Critical chunks can be safely discarded at this point. */ + keep = PNG_HANDLE_CHUNK_NEVER; + } + } + + else + keep = PNG_HANDLE_CHUNK_NEVER; /* insufficient memory */ + } + + else + /* Use the SAVE_UNKNOWN_CHUNKS code or skip the chunk */ +# endif /* PNG_READ_USER_CHUNKS_SUPPORTED */ + +# ifdef PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED + { + /* keep is currently just the per-chunk setting, if there was no + * setting change it to the global default now (not that this may + * still be AS_DEFAULT) then obtain the cache of the chunk if required, + * if not simply skip the chunk. + */ + if (keep == PNG_HANDLE_CHUNK_AS_DEFAULT) + keep = png_ptr->unknown_default; + + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name))) + { + if (!png_cache_unknown_chunk(png_ptr, length)) + keep = PNG_HANDLE_CHUNK_NEVER; + } + + else + png_crc_finish(png_ptr, length); + } +# else +# ifndef PNG_READ_USER_CHUNKS_SUPPORTED +# error no method to support READ_UNKNOWN_CHUNKS +# endif + + { + /* If here there is no read callback pointer set and no support is + * compiled in to just save the unknown chunks, so simply skip this + * chunk. If 'keep' is something other than AS_DEFAULT or NEVER then + * the app has erroneously asked for unknown chunk saving when there + * is no support. + */ + if (keep > PNG_HANDLE_CHUNK_NEVER) + png_app_error(png_ptr, "no unknown chunk support available"); + + png_crc_finish(png_ptr, length); + } +# endif + +# ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED + /* Now store the chunk in the chunk list if appropriate, and if the limits + * permit it. + */ + if (keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_IF_SAFE && + PNG_CHUNK_ANCILLIARY(png_ptr->chunk_name))) + { +# ifdef PNG_USER_LIMITS_SUPPORTED + switch (png_ptr->user_chunk_cache_max) + { + case 2: + png_ptr->user_chunk_cache_max = 1; + png_chunk_benign_error(png_ptr, "no space in chunk cache"); + /* FALL THROUGH */ + case 1: + /* NOTE: prior to 1.6.0 this case resulted in an unknown critical + * chunk being skipped, now there will be a hard error below. + */ + break; + + default: /* not at limit */ + --(png_ptr->user_chunk_cache_max); + /* FALL THROUGH */ + case 0: /* no limit */ +# endif /* PNG_USER_LIMITS_SUPPORTED */ + /* Here when the limit isn't reached or when limits are compiled + * out; store the chunk. + */ + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + handled = 1; +# ifdef PNG_USER_LIMITS_SUPPORTED + break; + } +# endif + } +# else /* no store support! */ + PNG_UNUSED(info_ptr) +# error untested code (reading unknown chunks with no store support) +# endif + + /* Regardless of the error handling below the cached data (if any) can be + * freed now. Notice that the data is not freed if there is a png_error, but + * it will be freed by destroy_read_struct. + */ + if (png_ptr->unknown_chunk.data != NULL) + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + +#else /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ + /* There is no support to read an unknown chunk, so just skip it. */ + png_crc_finish(png_ptr, length); + PNG_UNUSED(info_ptr) + PNG_UNUSED(keep) +#endif /* !PNG_READ_UNKNOWN_CHUNKS_SUPPORTED */ + + /* Check for unhandled critical chunks */ + if (!handled && PNG_CHUNK_CRITICAL(png_ptr->chunk_name)) + png_chunk_error(png_ptr, "unhandled critical chunk"); } /* 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. */ + * 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)) +/* Bit hacking: the test for an invalid byte in the 4 byte chunk name is: + * + * ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + */ void /* PRIVATE */ -png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +png_check_chunk_name(png_structrp png_ptr, png_uint_32 chunk_name) { - png_debug(1, "in png_check_chunk_name\n"); - if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || - isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + int i; + + png_debug(1, "in png_check_chunk_name"); + + for (i=1; i<=4; ++i) { - png_chunk_error(png_ptr, "invalid chunk type"); + int c = chunk_name & 0xff; + + if (c < 65 || c > 122 || (c > 90 && c < 97)) + png_chunk_error(png_ptr, "invalid chunk type"); + + chunk_name >>= 8; } } -/* 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. */ - +/* 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 'display' value; if 'display' is true then the whole row + * (dp) is filled from the start by replicating the available pixels. If + * 'display' is false only those pixels present in the pass are filled in. + */ void /* PRIVATE */ -png_combine_row(png_structp png_ptr, png_bytep row, int mask) +png_combine_row(png_const_structrp png_ptr, png_bytep dp, int display) { - png_debug(1,"in png_combine_row\n"); - if (mask == 0xff) + unsigned int pixel_depth = png_ptr->transformed_pixel_depth; + png_const_bytep sp = png_ptr->row_buf + 1; + png_uint_32 row_width = png_ptr->width; + unsigned int pass = png_ptr->pass; + png_bytep end_ptr = 0; + png_byte end_byte = 0; + unsigned int end_mask; + + png_debug(1, "in png_combine_row"); + + /* Added in 1.5.6: it should not be possible to enter this routine until at + * least one row has been read from the PNG data and transformed. + */ + if (pixel_depth == 0) + png_error(png_ptr, "internal row logic error"); + + /* Added in 1.5.4: the pixel depth should match the information returned by + * any call to png_read_update_info at this point. Do not continue if we got + * this wrong. + */ + if (png_ptr->info_rowbytes != 0 && png_ptr->info_rowbytes != + PNG_ROWBYTES(pixel_depth, row_width)) + png_error(png_ptr, "internal row size calculation error"); + + /* Don't expect this to ever happen: */ + if (row_width == 0) + png_error(png_ptr, "internal row width error"); + + /* Preserve the last byte in cases where only part of it will be overwritten, + * the multiply below may overflow, we don't care because ANSI-C guarantees + * we get the low bits. + */ + end_mask = (pixel_depth * row_width) & 7; + if (end_mask != 0) { - png_memcpy(row, png_ptr->row_buf + 1, - PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + /* end_ptr == NULL is a flag to say do nothing */ + end_ptr = dp + PNG_ROWBYTES(pixel_depth, row_width) - 1; + end_byte = *end_ptr; +# ifdef PNG_READ_PACKSWAP_SUPPORTED + if (png_ptr->transformations & PNG_PACKSWAP) /* little-endian byte */ + end_mask = 0xff << end_mask; + + else /* big-endian byte */ +# endif + end_mask = 0xff >> end_mask; + /* end_mask is now the bits to *keep* from the destination row */ } - else + + /* For non-interlaced images this reduces to a memcpy(). A memcpy() + * will also happen if interlacing isn't supported or if the application + * does not call png_set_interlace_handling(). In the latter cases the + * caller just gets a sequence of the unexpanded rows from each interlace + * pass. + */ +#ifdef PNG_READ_INTERLACING_SUPPORTED + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE) && + pass < 6 && (display == 0 || + /* The following copies everything for 'display' on passes 0, 2 and 4. */ + (display == 1 && (pass & 1) != 0))) { - switch (png_ptr->row_info.pixel_depth) + /* Narrow images may have no bits in a pass; the caller should handle + * this, but this test is cheap: + */ + if (row_width <= PNG_PASS_START_COL(pass)) + return; + + if (pixel_depth < 8) { - case 1: - { - png_bytep sp = png_ptr->row_buf + 1; - png_bytep dp = row; - int s_inc, s_start, s_end; - int m = 0x80; - int shift; - png_uint_32 i; - png_uint_32 row_width = png_ptr->width; + /* For pixel depths up to 4 bpp the 8-pixel mask can be expanded to fit + * into 32 bits, then a single loop over the bytes using the four byte + * values in the 32-bit mask can be used. For the 'display' option the + * expanded mask may also not require any masking within a byte. To + * make this work the PACKSWAP option must be taken into account - it + * simply requires the pixels to be reversed in each byte. + * + * The 'regular' case requires a mask for each of the first 6 passes, + * the 'display' case does a copy for the even passes in the range + * 0..6. This has already been handled in the test above. + * + * The masks are arranged as four bytes with the first byte to use in + * the lowest bits (little-endian) regardless of the order (PACKSWAP or + * not) of the pixels in each byte. + * + * NOTE: the whole of this logic depends on the caller of this function + * only calling it on rows appropriate to the pass. This function only + * understands the 'x' logic; the 'y' logic is handled by the caller. + * + * The following defines allow generation of compile time constant bit + * masks for each pixel depth and each possibility of swapped or not + * swapped bytes. Pass 'p' is in the range 0..6; 'x', a pixel index, + * is in the range 0..7; and the result is 1 if the pixel is to be + * copied in the pass, 0 if not. 'S' is for the sparkle method, 'B' + * for the block method. + * + * With some compilers a compile time expression of the general form: + * + * (shift >= 32) ? (a >> (shift-32)) : (b >> shift) + * + * Produces warnings with values of 'shift' in the range 33 to 63 + * because the right hand side of the ?: expression is evaluated by + * the compiler even though it isn't used. Microsoft Visual C (various + * versions) and the Intel C compiler are known to do this. To avoid + * this the following macros are used in 1.5.6. This is a temporary + * solution to avoid destabilizing the code during the release process. + */ +# if PNG_USE_COMPILE_TIME_MASKS +# define PNG_LSR(x,s) ((x)>>((s) & 0x1f)) +# define PNG_LSL(x,s) ((x)<<((s) & 0x1f)) +# else +# define PNG_LSR(x,s) ((x)>>(s)) +# define PNG_LSL(x,s) ((x)<<(s)) +# endif +# define S_COPY(p,x) (((p)<4 ? PNG_LSR(0x80088822,(3-(p))*8+(7-(x))) :\ + PNG_LSR(0xaa55ff00,(7-(p))*8+(7-(x)))) & 1) +# define B_COPY(p,x) (((p)<4 ? PNG_LSR(0xff0fff33,(3-(p))*8+(7-(x))) :\ + PNG_LSR(0xff55ff00,(7-(p))*8+(7-(x)))) & 1) -#if defined(PNG_READ_PACKSWAP_SUPPORTED) + /* Return a mask for pass 'p' pixel 'x' at depth 'd'. The mask is + * little endian - the first pixel is at bit 0 - however the extra + * parameter 's' can be set to cause the mask position to be swapped + * within each byte, to match the PNG format. This is done by XOR of + * the shift with 7, 6 or 4 for bit depths 1, 2 and 4. + */ +# define PIXEL_MASK(p,x,d,s) \ + (PNG_LSL(((PNG_LSL(1U,(d)))-1),(((x)*(d))^((s)?8-(d):0)))) + + /* Hence generate the appropriate 'block' or 'sparkle' pixel copy mask. + */ +# define S_MASKx(p,x,d,s) (S_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) +# define B_MASKx(p,x,d,s) (B_COPY(p,x)?PIXEL_MASK(p,x,d,s):0) + + /* Combine 8 of these to get the full mask. For the 1-bpp and 2-bpp + * cases the result needs replicating, for the 4-bpp case the above + * generates a full 32 bits. + */ +# define MASK_EXPAND(m,d) ((m)*((d)==1?0x01010101:((d)==2?0x00010001:1))) + +# define S_MASK(p,d,s) MASK_EXPAND(S_MASKx(p,0,d,s) + S_MASKx(p,1,d,s) +\ + S_MASKx(p,2,d,s) + S_MASKx(p,3,d,s) + S_MASKx(p,4,d,s) +\ + S_MASKx(p,5,d,s) + S_MASKx(p,6,d,s) + S_MASKx(p,7,d,s), d) + +# define B_MASK(p,d,s) MASK_EXPAND(B_MASKx(p,0,d,s) + B_MASKx(p,1,d,s) +\ + B_MASKx(p,2,d,s) + B_MASKx(p,3,d,s) + B_MASKx(p,4,d,s) +\ + B_MASKx(p,5,d,s) + B_MASKx(p,6,d,s) + B_MASKx(p,7,d,s), d) + +#if PNG_USE_COMPILE_TIME_MASKS + /* Utility macros to construct all the masks for a depth/swap + * combination. The 's' parameter says whether the format is PNG + * (big endian bytes) or not. Only the three odd-numbered passes are + * required for the display/block algorithm. + */ +# define S_MASKS(d,s) { S_MASK(0,d,s), S_MASK(1,d,s), S_MASK(2,d,s),\ + S_MASK(3,d,s), S_MASK(4,d,s), S_MASK(5,d,s) } + +# define B_MASKS(d,s) { B_MASK(1,d,s), S_MASK(3,d,s), S_MASK(5,d,s) } + +# define DEPTH_INDEX(d) ((d)==1?0:((d)==2?1:2)) + + /* Hence the pre-compiled masks indexed by PACKSWAP (or not), depth and + * then pass: + */ + static PNG_CONST png_uint_32 row_mask[2/*PACKSWAP*/][3/*depth*/][6] = + { + /* Little-endian byte masks for PACKSWAP */ + { S_MASKS(1,0), S_MASKS(2,0), S_MASKS(4,0) }, + /* Normal (big-endian byte) masks - PNG format */ + { S_MASKS(1,1), S_MASKS(2,1), S_MASKS(4,1) } + }; + + /* display_mask has only three entries for the odd passes, so index by + * pass>>1. + */ + static PNG_CONST png_uint_32 display_mask[2][3][3] = + { + /* Little-endian byte masks for PACKSWAP */ + { B_MASKS(1,0), B_MASKS(2,0), B_MASKS(4,0) }, + /* Normal (big-endian byte) masks - PNG format */ + { B_MASKS(1,1), B_MASKS(2,1), B_MASKS(4,1) } + }; + +# define MASK(pass,depth,display,png)\ + ((display)?display_mask[png][DEPTH_INDEX(depth)][pass>>1]:\ + row_mask[png][DEPTH_INDEX(depth)][pass]) + +#else /* !PNG_USE_COMPILE_TIME_MASKS */ + /* This is the runtime alternative: it seems unlikely that this will + * ever be either smaller or faster than the compile time approach. + */ +# define MASK(pass,depth,display,png)\ + ((display)?B_MASK(pass,depth,png):S_MASK(pass,depth,png)) +#endif /* !PNG_USE_COMPILE_TIME_MASKS */ + + /* Use the appropriate mask to copy the required bits. In some cases + * the byte mask will be 0 or 0xff, optimize these cases. row_width is + * the number of pixels, but the code copies bytes, so it is necessary + * to special case the end. + */ + png_uint_32 pixels_per_byte = 8 / pixel_depth; + png_uint_32 mask; + +# ifdef PNG_READ_PACKSWAP_SUPPORTED if (png_ptr->transformations & PNG_PACKSWAP) - { - s_start = 0; - s_end = 7; - s_inc = 1; - } + mask = MASK(pass, pixel_depth, display, 0); + else -#endif - { - s_start = 7; - s_end = 0; - s_inc = -1; - } +# endif + mask = MASK(pass, pixel_depth, display, 1); - shift = s_start; - - for (i = 0; i < row_width; i++) - { - if (m & mask) - { - int value; - - value = (*sp >> shift) & 0x01; - *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); - *dp |= (png_byte)(value << shift); - } - - if (shift == s_end) - { - shift = s_start; - sp++; - dp++; - } - else - shift += s_inc; - - if (m == 1) - m = 0x80; - else - m >>= 1; - } - break; - } - case 2: + for (;;) { - png_bytep sp = png_ptr->row_buf + 1; - png_bytep dp = row; - int s_start, s_end, s_inc; - int m = 0x80; - int shift; - png_uint_32 i; - png_uint_32 row_width = png_ptr->width; - int value; + png_uint_32 m; -#if defined(PNG_READ_PACKSWAP_SUPPORTED) - if (png_ptr->transformations & PNG_PACKSWAP) + /* It doesn't matter in the following if png_uint_32 has more than + * 32 bits because the high bits always match those in m<<24; it is, + * however, essential to use OR here, not +, because of this. + */ + m = mask; + mask = (m >> 8) | (m << 24); /* rotate right to good compilers */ + m &= 0xff; + + if (m != 0) /* something to copy */ { - s_start = 0; - s_end = 6; - s_inc = 2; - } - else -#endif - { - s_start = 6; - s_end = 0; - s_inc = -2; - } - - shift = s_start; - - for (i = 0; i < row_width; i++) - { - if (m & mask) - { - value = (*sp >> shift) & 0x03; - *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); - *dp |= (png_byte)(value << shift); - } - - if (shift == s_end) - { - shift = s_start; - sp++; - dp++; - } + if (m != 0xff) + *dp = (png_byte)((*dp & ~m) | (*sp & m)); else - shift += s_inc; - if (m == 1) - m = 0x80; - else - m >>= 1; + *dp = *sp; } - break; - } - case 4: - { - png_bytep sp = png_ptr->row_buf + 1; - png_bytep dp = row; - int s_start, s_end, s_inc; - int m = 0x80; - int shift; - png_uint_32 i; - png_uint_32 row_width = png_ptr->width; - int value; -#if defined(PNG_READ_PACKSWAP_SUPPORTED) - if (png_ptr->transformations & PNG_PACKSWAP) - { - s_start = 0; - s_end = 4; - s_inc = 4; - } - else -#endif - { - s_start = 4; - s_end = 0; - s_inc = -4; - } - shift = s_start; + /* NOTE: this may overwrite the last byte with garbage if the image + * is not an exact number of bytes wide; libpng has always done + * this. + */ + if (row_width <= pixels_per_byte) + break; /* May need to restore part of the last byte */ - for (i = 0; i < row_width; i++) - { - if (m & mask) - { - value = (*sp >> shift) & 0xf; - *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); - *dp |= (png_byte)(value << shift); - } - - if (shift == s_end) - { - shift = s_start; - sp++; - dp++; - } - else - shift += s_inc; - if (m == 1) - m = 0x80; - else - m >>= 1; - } - break; - } - default: - { - png_bytep sp = png_ptr->row_buf + 1; - png_bytep dp = row; - png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); - png_uint_32 i; - png_uint_32 row_width = png_ptr->width; - png_byte m = 0x80; - - - for (i = 0; i < row_width; i++) - { - if (m & mask) - { - png_memcpy(dp, sp, pixel_bytes); - } - - sp += pixel_bytes; - dp += pixel_bytes; - - if (m == 1) - m = 0x80; - else - m >>= 1; - } - break; + row_width -= pixels_per_byte; + ++dp; + ++sp; } } + + else /* pixel_depth >= 8 */ + { + unsigned int bytes_to_copy, bytes_to_jump; + + /* Validate the depth - it must be a multiple of 8 */ + if (pixel_depth & 7) + png_error(png_ptr, "invalid user transform pixel depth"); + + pixel_depth >>= 3; /* now in bytes */ + row_width *= pixel_depth; + + /* Regardless of pass number the Adam 7 interlace always results in a + * fixed number of pixels to copy then to skip. There may be a + * different number of pixels to skip at the start though. + */ + { + unsigned int offset = PNG_PASS_START_COL(pass) * pixel_depth; + + row_width -= offset; + dp += offset; + sp += offset; + } + + /* Work out the bytes to copy. */ + if (display) + { + /* When doing the 'block' algorithm the pixel in the pass gets + * replicated to adjacent pixels. This is why the even (0,2,4,6) + * passes are skipped above - the entire expanded row is copied. + */ + bytes_to_copy = (1<<((6-pass)>>1)) * pixel_depth; + + /* But don't allow this number to exceed the actual row width. */ + if (bytes_to_copy > row_width) + bytes_to_copy = row_width; + } + + else /* normal row; Adam7 only ever gives us one pixel to copy. */ + bytes_to_copy = pixel_depth; + + /* In Adam7 there is a constant offset between where the pixels go. */ + bytes_to_jump = PNG_PASS_COL_OFFSET(pass) * pixel_depth; + + /* And simply copy these bytes. Some optimization is possible here, + * depending on the value of 'bytes_to_copy'. Special case the low + * byte counts, which we know to be frequent. + * + * Notice that these cases all 'return' rather than 'break' - this + * avoids an unnecessary test on whether to restore the last byte + * below. + */ + switch (bytes_to_copy) + { + case 1: + for (;;) + { + *dp = *sp; + + if (row_width <= bytes_to_jump) + return; + + dp += bytes_to_jump; + sp += bytes_to_jump; + row_width -= bytes_to_jump; + } + + case 2: + /* There is a possibility of a partial copy at the end here; this + * slows the code down somewhat. + */ + do + { + dp[0] = sp[0], dp[1] = sp[1]; + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + } + while (row_width > 1); + + /* And there can only be one byte left at this point: */ + *dp = *sp; + return; + + case 3: + /* This can only be the RGB case, so each copy is exactly one + * pixel and it is not necessary to check for a partial copy. + */ + for(;;) + { + dp[0] = sp[0], dp[1] = sp[1], dp[2] = sp[2]; + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + } + + default: +#if PNG_ALIGN_TYPE != PNG_ALIGN_NONE + /* Check for double byte alignment and, if possible, use a + * 16-bit copy. Don't attempt this for narrow images - ones that + * are less than an interlace panel wide. Don't attempt it for + * wide bytes_to_copy either - use the memcpy there. + */ + if (bytes_to_copy < 16 /*else use memcpy*/ && + png_isaligned(dp, png_uint_16) && + png_isaligned(sp, png_uint_16) && + bytes_to_copy % (sizeof (png_uint_16)) == 0 && + bytes_to_jump % (sizeof (png_uint_16)) == 0) + { + /* Everything is aligned for png_uint_16 copies, but try for + * png_uint_32 first. + */ + if (png_isaligned(dp, png_uint_32) && + png_isaligned(sp, png_uint_32) && + bytes_to_copy % (sizeof (png_uint_32)) == 0 && + bytes_to_jump % (sizeof (png_uint_32)) == 0) + { + png_uint_32p dp32 = png_aligncast(png_uint_32p,dp); + png_const_uint_32p sp32 = png_aligncastconst( + png_const_uint_32p, sp); + size_t skip = (bytes_to_jump-bytes_to_copy) / + (sizeof (png_uint_32)); + + do + { + size_t c = bytes_to_copy; + do + { + *dp32++ = *sp32++; + c -= (sizeof (png_uint_32)); + } + while (c > 0); + + if (row_width <= bytes_to_jump) + return; + + dp32 += skip; + sp32 += skip; + row_width -= bytes_to_jump; + } + while (bytes_to_copy <= row_width); + + /* Get to here when the row_width truncates the final copy. + * There will be 1-3 bytes left to copy, so don't try the + * 16-bit loop below. + */ + dp = (png_bytep)dp32; + sp = (png_const_bytep)sp32; + do + *dp++ = *sp++; + while (--row_width > 0); + return; + } + + /* Else do it in 16-bit quantities, but only if the size is + * not too large. + */ + else + { + png_uint_16p dp16 = png_aligncast(png_uint_16p, dp); + png_const_uint_16p sp16 = png_aligncastconst( + png_const_uint_16p, sp); + size_t skip = (bytes_to_jump-bytes_to_copy) / + (sizeof (png_uint_16)); + + do + { + size_t c = bytes_to_copy; + do + { + *dp16++ = *sp16++; + c -= (sizeof (png_uint_16)); + } + while (c > 0); + + if (row_width <= bytes_to_jump) + return; + + dp16 += skip; + sp16 += skip; + row_width -= bytes_to_jump; + } + while (bytes_to_copy <= row_width); + + /* End of row - 1 byte left, bytes_to_copy > row_width: */ + dp = (png_bytep)dp16; + sp = (png_const_bytep)sp16; + do + *dp++ = *sp++; + while (--row_width > 0); + return; + } + } +#endif /* PNG_ALIGN_ code */ + + /* The true default - use a memcpy: */ + for (;;) + { + memcpy(dp, sp, bytes_to_copy); + + if (row_width <= bytes_to_jump) + return; + + sp += bytes_to_jump; + dp += bytes_to_jump; + row_width -= bytes_to_jump; + if (bytes_to_copy > row_width) + bytes_to_copy = row_width; + } + } + + /* NOT REACHED*/ + } /* pixel_depth >= 8 */ + + /* Here if pixel_depth < 8 to check 'end_ptr' below. */ } + else +#endif + + /* If here then the switch above wasn't used so just memcpy the whole row + * from the temporary row buffer (notice that this overwrites the end of the + * destination row if it is a partial byte.) + */ + memcpy(dp, sp, PNG_ROWBYTES(pixel_depth, row_width)); + + /* Restore the overwritten bits from the last byte if necessary. */ + if (end_ptr != NULL) + *end_ptr = (png_byte)((end_byte & end_mask) | (*end_ptr & ~end_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) +png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations /* Because these may affect the byte layout */) { - png_row_infop row_info = &(png_ptr->row_info); - png_bytep row = png_ptr->row_buf + 1; - int pass = png_ptr->pass; - png_uint_32 transformations = png_ptr->transformations; -#ifdef PNG_USE_LOCAL_ARRAYS - /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - /* offset to next interlace block */ - PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; -#endif + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* Offset to next interlace block */ + static PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - png_debug(1,"in png_do_read_interlace\n"); + png_debug(1, "in png_do_read_interlace"); if (row != NULL && row_info != NULL) { png_uint_32 final_width; @@ -2533,7 +3494,7 @@ png_do_read_interlace(png_structp png_ptr) png_uint_32 i; int j; -#if defined(PNG_READ_PACKSWAP_SUPPORTED) +#ifdef PNG_READ_PACKSWAP_SUPPORTED if (transformations & PNG_PACKSWAP) { sshift = (int)((row_info->width + 7) & 0x07); @@ -2542,6 +3503,7 @@ png_do_read_interlace(png_structp png_ptr) s_end = 0; s_inc = -1; } + else #endif { @@ -2557,26 +3519,32 @@ png_do_read_interlace(png_structp png_ptr) v = (png_byte)((*sp >> sshift) & 0x01); for (j = 0; j < jstop; j++) { - *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); - *dp |= (png_byte)(v << dshift); + unsigned int tmp = *dp & (0x7f7f >> (7 - dshift)); + tmp |= v << dshift; + *dp = (png_byte)(tmp & 0xff); + if (dshift == s_end) { dshift = s_start; dp--; } + else dshift += s_inc; } + if (sshift == s_end) { sshift = s_start; sp--; } + else sshift += s_inc; } break; } + case 2: { png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); @@ -2586,7 +3554,7 @@ png_do_read_interlace(png_structp png_ptr) int jstop = png_pass_inc[pass]; png_uint_32 i; -#if defined(PNG_READ_PACKSWAP_SUPPORTED) +#ifdef PNG_READ_PACKSWAP_SUPPORTED if (transformations & PNG_PACKSWAP) { sshift = (int)(((row_info->width + 3) & 0x03) << 1); @@ -2595,6 +3563,7 @@ png_do_read_interlace(png_structp png_ptr) s_end = 0; s_inc = -2; } + else #endif { @@ -2613,26 +3582,32 @@ png_do_read_interlace(png_structp png_ptr) v = (png_byte)((*sp >> sshift) & 0x03); for (j = 0; j < jstop; j++) { - *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); - *dp |= (png_byte)(v << dshift); + unsigned int tmp = *dp & (0x3f3f >> (6 - dshift)); + tmp |= v << dshift; + *dp = (png_byte)(tmp & 0xff); + if (dshift == s_end) { dshift = s_start; dp--; } + else dshift += s_inc; } + if (sshift == s_end) { sshift = s_start; sp--; } + else sshift += s_inc; } break; } + case 4: { png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); @@ -2642,7 +3617,7 @@ png_do_read_interlace(png_structp png_ptr) png_uint_32 i; int jstop = png_pass_inc[pass]; -#if defined(PNG_READ_PACKSWAP_SUPPORTED) +#ifdef PNG_READ_PACKSWAP_SUPPORTED if (transformations & PNG_PACKSWAP) { sshift = (int)(((row_info->width + 1) & 0x01) << 2); @@ -2651,6 +3626,7 @@ png_do_read_interlace(png_structp png_ptr) s_end = 0; s_inc = -4; } + else #endif { @@ -2663,35 +3639,44 @@ png_do_read_interlace(png_structp png_ptr) for (i = 0; i < row_info->width; i++) { - png_byte v = (png_byte)((*sp >> sshift) & 0xf); + png_byte v = (png_byte)((*sp >> sshift) & 0x0f); int j; for (j = 0; j < jstop; j++) { - *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); - *dp |= (png_byte)(v << dshift); + unsigned int tmp = *dp & (0xf0f >> (4 - dshift)); + tmp |= v << dshift; + *dp = (png_byte)(tmp & 0xff); + if (dshift == s_end) { dshift = s_start; dp--; } + else dshift += s_inc; } + if (sshift == s_end) { sshift = s_start; sp--; } + else sshift += s_inc; } break; } + default: { png_size_t pixel_bytes = (row_info->pixel_depth >> 3); - png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; + + png_bytep sp = row + (png_size_t)(row_info->width - 1) + * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; int jstop = png_pass_inc[pass]; @@ -2702,351 +3687,569 @@ png_do_read_interlace(png_structp png_ptr) png_byte v[8]; int j; - png_memcpy(v, sp, pixel_bytes); + memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) { - png_memcpy(dp, v, pixel_bytes); + memcpy(dp, v, pixel_bytes); dp -= pixel_bytes; } + sp -= pixel_bytes; } break; } } + row_info->width = final_width; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, final_width); } -#if !defined(PNG_READ_PACKSWAP_SUPPORTED) - transformations = transformations; /* silence compiler warning */ +#ifndef PNG_READ_PACKSWAP_SUPPORTED + PNG_UNUSED(transformations) /* Silence compiler warning */ #endif } #endif /* PNG_READ_INTERLACING_SUPPORTED */ -void /* PRIVATE */ -png_read_filter_row(png_structp, png_row_infop row_info, png_bytep row, - png_bytep prev_row, int filter) +static void +png_read_filter_row_sub(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) { - png_debug(1, "in png_read_filter_row\n"); - png_debug2(2,"row = %lu, filter = %d\n", png_ptr->row_number, filter); - switch (filter) + png_size_t i; + png_size_t istop = row_info->rowbytes; + unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + + PNG_UNUSED(prev_row) + + for (i = bpp; i < istop; i++) { - case PNG_FILTER_VALUE_NONE: - break; - case PNG_FILTER_VALUE_SUB: - { - png_uint_32 i; - png_uint_32 istop = row_info->rowbytes; - png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; - png_bytep rp = row + bpp; - png_bytep lp = row; + *rp = (png_byte)(((int)(*rp) + (int)(*(rp-bpp))) & 0xff); + rp++; + } +} - for (i = bpp; i < istop; i++) - { - *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); - rp++; - } - break; - } - case PNG_FILTER_VALUE_UP: - { - png_uint_32 i; - png_uint_32 istop = row_info->rowbytes; - png_bytep rp = row; - png_bytep pp = prev_row; +static void +png_read_filter_row_up(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_size_t istop = row_info->rowbytes; + png_bytep rp = row; + png_const_bytep pp = prev_row; - for (i = 0; i < istop; i++) - { - *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); - rp++; - } - break; - } - case PNG_FILTER_VALUE_AVG: - { - png_uint_32 i; - png_bytep rp = row; - png_bytep pp = prev_row; - png_bytep lp = row; - png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; - png_uint_32 istop = row_info->rowbytes - bpp; + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } +} - for (i = 0; i < bpp; i++) - { - *rp = (png_byte)(((int)(*rp) + - ((int)(*pp++) / 2 )) & 0xff); - rp++; - } +static void +png_read_filter_row_avg(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_size_t i; + png_bytep rp = row; + png_const_bytep pp = prev_row; + unsigned int bpp = (row_info->pixel_depth + 7) >> 3; + png_size_t istop = row_info->rowbytes - bpp; - for (i = 0; i < istop; i++) - { - *rp = (png_byte)(((int)(*rp) + - (int)(*pp++ + *lp++) / 2 ) & 0xff); - rp++; - } - break; - } - case PNG_FILTER_VALUE_PAETH: - { - png_uint_32 i; - png_bytep rp = row; - png_bytep pp = prev_row; - png_bytep lp = row; - png_bytep cp = prev_row; - png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; - png_uint_32 istop=row_info->rowbytes - bpp; + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); - for (i = 0; i < bpp; i++) - { - *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); - rp++; - } + rp++; + } - for (i = 0; i < istop; i++) /* use leftover rp,pp */ - { - int a, b, c, pa, pb, pc, p; + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *(rp-bpp)) / 2 ) & 0xff); - a = *lp++; - b = *pp++; - c = *cp++; + rp++; + } +} - p = b - c; - pc = a - c; +static void +png_read_filter_row_paeth_1byte_pixel(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_bytep rp_end = row + row_info->rowbytes; + int a, c; -#ifdef PNG_USE_ABS - pa = abs(p); - pb = abs(pc); - pc = abs(p + pc); -#else - pa = p < 0 ? -p : p; - pb = pc < 0 ? -pc : pc; - pc = (p + pc) < 0 ? -(p + pc) : p + pc; + /* First pixel/byte */ + c = *prev_row++; + a = *row + c; + *row++ = (png_byte)a; + + /* Remainder */ + while (row < rp_end) + { + int b, pa, pb, pc, p; + + a &= 0xff; /* From previous iteration or start */ + b = *prev_row++; + + p = b - c; + pc = a - c; + +# ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +# else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +# endif + + /* Find the best predictor, the least of pa, pb, pc favoring the earlier + * ones in the case of a tie. + */ + if (pb < pa) pa = pb, a = b; + if (pc < pa) a = c; + + /* Calculate the current pixel in a, and move the previous row pixel to c + * for the next time round the loop + */ + c = b; + a += *row; + *row++ = (png_byte)a; + } +} + +static void +png_read_filter_row_paeth_multibyte_pixel(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + int bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp_end = row + bpp; + + /* Process the first pixel in the row completely (this is the same as 'up' + * because there is only one candidate predictor for the first row). + */ + while (row < rp_end) + { + int a = *row + *prev_row++; + *row++ = (png_byte)a; + } + + /* Remainder */ + rp_end += row_info->rowbytes - bpp; + + while (row < rp_end) + { + int a, b, c, pa, pb, pc, p; + + c = *(prev_row - bpp); + a = *(row - bpp); + b = *prev_row++; + + p = b - c; + pc = a - c; + +# ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +# else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +# endif + + if (pb < pa) pa = pb, a = b; + if (pc < pa) a = c; + + //c = b; + a += *row; + *row++ = (png_byte)a; + } +} + +static void +png_init_filter_functions(png_structrp pp) + /* This function is called once for every PNG image to set the + * implementations required to reverse the filtering of PNG rows. Reversing + * the filter is the first transformation performed on the row data. It is + * performed in place, therefore an implementation can be selected based on + * the image pixel format. If the implementation depends on image width then + * take care to ensure that it works corretly if the image is interlaced - + * interlacing causes the actual row width to vary. + */ +{ + unsigned int bpp = (pp->pixel_depth + 7) >> 3; + + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub; + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg; + if (bpp == 1) + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth_1byte_pixel; + else + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth_multibyte_pixel; + +#ifdef PNG_FILTER_OPTIMIZATIONS + /* To use this define PNG_FILTER_OPTIMIZATIONS as the name of a function to + * call to install hardware optimizations for the above functions; simply + * replace whatever elements of the pp->read_filter[] array with a hardware + * specific (or, for that matter, generic) optimization. + * + * To see an example of this examine what configure.ac does when + * --enable-arm-neon is specified on the command line. + */ + PNG_FILTER_OPTIMIZATIONS(pp, bpp); #endif +} - /* - if (pa <= pb && pa <= pc) - p = a; - else if (pb <= pc) - p = b; - else - p = c; +void /* PRIVATE */ +png_read_filter_row(png_structrp pp, png_row_infop row_info, png_bytep row, + png_const_bytep prev_row, int filter) +{ + /* OPTIMIZATION: DO NOT MODIFY THIS FUNCTION, instead #define + * PNG_FILTER_OPTIMIZATIONS to a function that overrides the generic + * implementations. See png_init_filter_functions above. + */ + if (pp->read_filter[0] == NULL) + png_init_filter_functions(pp); + if (filter > PNG_FILTER_VALUE_NONE && filter < PNG_FILTER_VALUE_LAST) + pp->read_filter[filter-1](row_info, row, prev_row); +} + +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED +void /* PRIVATE */ +png_read_IDAT_data(png_structrp png_ptr, png_bytep output, + png_alloc_size_t avail_out) +{ + /* Loop reading IDATs and decompressing the result into output[avail_out] */ + png_ptr->zstream.next_out = output; + png_ptr->zstream.avail_out = 0; /* safety: set below */ + + if (output == NULL) + avail_out = 0; + + do + { + int ret; + png_byte tmpbuf[PNG_INFLATE_BUF_SIZE]; + + if (png_ptr->zstream.avail_in == 0) + { + uInt avail_in; + png_bytep buffer; + + while (png_ptr->idat_size == 0) + { + png_crc_finish(png_ptr, 0); + + png_ptr->idat_size = png_read_chunk_header(png_ptr); + /* This is an error even in the 'check' case because the code just + * consumed a non-IDAT header. */ - - p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; - - *rp = (png_byte)(((int)(*rp) + p) & 0xff); - rp++; + if (png_ptr->chunk_name != png_IDAT) + png_error(png_ptr, "Not enough image data"); } + + avail_in = png_ptr->IDAT_read_size; + + if (avail_in > png_ptr->idat_size) + avail_in = (uInt)png_ptr->idat_size; + + /* A PNG with a gradually increasing IDAT size will defeat this attempt + * to minimize memory usage by causing lots of re-allocs, but + * realistically doing IDAT_read_size re-allocs is not likely to be a + * big problem. + */ + buffer = png_read_buffer(png_ptr, avail_in, 0/*error*/); + + png_crc_read(png_ptr, buffer, avail_in); + png_ptr->idat_size -= avail_in; + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = avail_in; + } + + /* And set up the output side. */ + if (output != NULL) /* standard read */ + { + uInt out = ZLIB_IO_MAX; + + if (out > avail_out) + out = (uInt)avail_out; + + avail_out -= out; + png_ptr->zstream.avail_out = out; + } + + else /* check for end */ + { + png_ptr->zstream.next_out = tmpbuf; + png_ptr->zstream.avail_out = (sizeof tmpbuf); + } + + /* Use NO_FLUSH; this gives zlib the maximum opportunity to optimize the + * process. If the LZ stream is truncated the sequential reader will + * terminally damage the stream, above, by reading the chunk header of the + * following chunk (it then exits with png_error). + * + * TODO: deal more elegantly with truncated IDAT lists. + */ + ret = inflate(&png_ptr->zstream, Z_NO_FLUSH); + + /* Take the unconsumed output back (so, in the 'check' case this just + * counts up). + */ + avail_out += png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; + + if (ret == Z_STREAM_END) + { + /* Do this for safety; we won't read any more into this row. */ + png_ptr->zstream.next_out = NULL; + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + + if (png_ptr->zstream.avail_in > 0 || png_ptr->idat_size > 0) + png_chunk_benign_error(png_ptr, "Extra compressed data"); break; } - default: - png_warning(png_ptr, "Ignoring bad adaptive filter type"); - *row=0; - break; + + if (ret != Z_OK) + { + png_zstream_error(png_ptr, ret); + + if (output != NULL) + png_chunk_error(png_ptr, png_ptr->zstream.msg); + + else /* checking */ + { + png_chunk_benign_error(png_ptr, png_ptr->zstream.msg); + return; + } + } + } while (avail_out > 0); + + if (avail_out > 0) + { + /* The stream ended before the image; this is the same as too few IDATs so + * should be handled the same way. + */ + if (output != NULL) + png_error(png_ptr, "Not enough image data"); + + else /* checking */ + png_chunk_benign_error(png_ptr, "Too much image data"); } } void /* PRIVATE */ -png_read_finish_row(png_structp png_ptr) +png_read_finish_IDAT(png_structrp png_ptr) { -#ifdef PNG_USE_LOCAL_ARRAYS - /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* We don't need any more data and the stream should have ended, however the + * LZ end code may actually not have been processed. In this case we must + * read it otherwise stray unread IDAT data or, more likely, an IDAT chunk + * may still remain to be consumed. + */ + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + { + /* The NULL causes png_read_IDAT_data to swallow any remaining bytes in + * the compressed stream, but the stream may be damaged too, so even after + * this call we may need to terminate the zstream ownership. + */ + png_read_IDAT_data(png_ptr, NULL, 0); + png_ptr->zstream.next_out = NULL; /* safety */ - /* start of interlace block */ - PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + /* Now clear everything out for safety; the following may not have been + * done. + */ + if (!(png_ptr->flags & PNG_FLAG_ZSTREAM_ENDED)) + { + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZSTREAM_ENDED; + } + } - /* offset to next interlace block */ - PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + /* If the zstream has not been released do it now *and* terminate the reading + * of the final IDAT chunk. + */ + if (png_ptr->zowner == png_IDAT) + { + /* Always do this; the pointers otherwise point into the read buffer. */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; - /* start of interlace block in the y direction */ - PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + /* Now we no longer own the zstream. */ + png_ptr->zowner = 0; - /* offset to next interlace block in the y direction */ - PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; -#endif + /* The slightly weird semantics of the sequential IDAT reading is that we + * are always in or at the end of an IDAT chunk, so we always need to do a + * crc_finish here. If idat_size is non-zero we also need to read the + * spurious bytes at the end of the chunk now. + */ + (void)png_crc_finish(png_ptr, png_ptr->idat_size); + } +} - png_debug(1, "in png_read_finish_row\n"); +void /* PRIVATE */ +png_read_finish_row(png_structrp png_ptr) +{ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* Start of interlace block */ + static PNG_CONST png_byte png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + + png_debug(1, "in png_read_finish_row"); png_ptr->row_number++; if (png_ptr->row_number < png_ptr->num_rows) return; +#ifdef PNG_READ_INTERLACING_SUPPORTED if (png_ptr->interlaced) { png_ptr->row_number = 0; - png_memset_check(png_ptr, png_ptr->prev_row, 0, - png_ptr->rowbytes + 1); + + /* TO DO: don't do this if prev_row isn't needed (requires + * read-ahead of the next row's filter byte. + */ + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + do { png_ptr->pass++; + if (png_ptr->pass >= 7) break; + png_ptr->iwidth = (png_ptr->width + png_pass_inc[png_ptr->pass] - 1 - png_pass_start[png_ptr->pass]) / png_pass_inc[png_ptr->pass]; - png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, - png_ptr->iwidth) + 1; - if (!(png_ptr->transformations & PNG_INTERLACE)) { png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; - if (!(png_ptr->num_rows)) - continue; + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ - break; - } while (png_ptr->iwidth == 0); + break; /* libpng deinterlacing sees every row */ + + } while (png_ptr->num_rows == 0 || png_ptr->iwidth == 0); if (png_ptr->pass < 7) return; } +#endif /* PNG_READ_INTERLACING_SUPPORTED */ - if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) - { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_CONST PNG_IDAT; -#endif - char extra; - int ret; - - png_ptr->zstream.next_out = (Bytef *)&extra; - png_ptr->zstream.avail_out = (uInt)1; - for(;;) - { - if (!(png_ptr->zstream.avail_in)) - { - while (!png_ptr->idat_size) - { - png_byte chunk_length[4]; - - png_crc_finish(png_ptr, 0); - - png_read_data(png_ptr, chunk_length, 4); - png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); - png_reset_crc(png_ptr); - png_crc_read(png_ptr, png_ptr->chunk_name, 4); - if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) - png_error(png_ptr, "Not enough image data"); - - } - png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; - png_ptr->zstream.next_in = png_ptr->zbuf; - if (png_ptr->zbuf_size > png_ptr->idat_size) - png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; - png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); - png_ptr->idat_size -= png_ptr->zstream.avail_in; - } - ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); - if (ret == Z_STREAM_END) - { - if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || - png_ptr->idat_size) - png_warning(png_ptr, "Extra compressed data"); - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; - break; - } - if (ret != Z_OK) - png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : - "Decompression Error"); - - if (!(png_ptr->zstream.avail_out)) - { - png_warning(png_ptr, "Extra compressed data."); - png_ptr->mode |= PNG_AFTER_IDAT; - png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; - break; - } - - } - png_ptr->zstream.avail_out = 0; - } - - if (png_ptr->idat_size || png_ptr->zstream.avail_in) - png_warning(png_ptr, "Extra compression data"); - - inflateReset(&png_ptr->zstream); - - png_ptr->mode |= PNG_AFTER_IDAT; + /* Here after at the end of the last row of the last pass. */ + png_read_finish_IDAT(png_ptr); } +#endif /* PNG_SEQUENTIAL_READ_SUPPORTED */ void /* PRIVATE */ -png_read_start_row(png_structp png_ptr) +png_read_start_row(png_structrp png_ptr) { -#ifdef PNG_USE_LOCAL_ARRAYS - /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ +#ifdef PNG_READ_INTERLACING_SUPPORTED + /* Arrays to facilitate easy interlacing - use pass (0 - 6) as index */ - /* start of interlace block */ - PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + /* Start of interlace block */ + static PNG_CONST png_byte 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}; + /* Offset to next interlace block */ + static PNG_CONST png_byte 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}; + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte 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}; + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif int max_pixel_depth; - png_uint_32 row_bytes; + png_size_t row_bytes; - png_debug(1, "in png_read_start_row\n"); - png_ptr->zstream.avail_in = 0; + png_debug(1, "in png_read_start_row"); + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED png_init_read_transformations(png_ptr); +#endif +#ifdef PNG_READ_INTERLACING_SUPPORTED if (png_ptr->interlaced) { if (!(png_ptr->transformations & PNG_INTERLACE)) png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - - png_pass_ystart[0]) / png_pass_yinc[0]; + png_pass_ystart[0]) / png_pass_yinc[0]; + else png_ptr->num_rows = png_ptr->height; png_ptr->iwidth = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; - - row_bytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->iwidth) + 1; - - png_ptr->irowbytes = (png_size_t)row_bytes; - if((png_uint_32)png_ptr->irowbytes != row_bytes) - png_error(png_ptr, "Rowbytes overflow in png_read_start_row"); + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; } + else +#endif /* PNG_READ_INTERLACING_SUPPORTED */ { png_ptr->num_rows = png_ptr->height; png_ptr->iwidth = png_ptr->width; - png_ptr->irowbytes = png_ptr->rowbytes + 1; } + max_pixel_depth = png_ptr->pixel_depth; -#if defined(PNG_READ_PACK_SUPPORTED) + /* WARNING: * png_read_transform_info (pngrtran.c) performs a simpliar set of + * calculations to calculate the final pixel depth, then + * png_do_read_transforms actually does the transforms. This means that the + * code which effectively calculates this value is actually repeated in three + * separate places. They must all match. Innocent changes to the order of + * transformations can and will break libpng in a way that causes memory + * overwrites. + * + * TODO: fix this. + */ +#ifdef PNG_READ_PACK_SUPPORTED if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) max_pixel_depth = 8; #endif -#if defined(PNG_READ_EXPAND_SUPPORTED) +#ifdef PNG_READ_EXPAND_SUPPORTED if (png_ptr->transformations & PNG_EXPAND) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (png_ptr->num_trans) max_pixel_depth = 32; + else max_pixel_depth = 24; } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) { if (max_pixel_depth < 8) max_pixel_depth = 8; + if (png_ptr->num_trans) max_pixel_depth *= 2; } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) { if (png_ptr->num_trans) @@ -3058,56 +4261,81 @@ png_read_start_row(png_structp png_ptr) } #endif -#if defined(PNG_READ_FILLER_SUPPORTED) +#ifdef PNG_READ_EXPAND_16_SUPPORTED + if (png_ptr->transformations & PNG_EXPAND_16) + { +# ifdef PNG_READ_EXPAND_SUPPORTED + /* In fact it is an error if it isn't supported, but checking is + * the safe way. + */ + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->bit_depth < 16) + max_pixel_depth *= 2; + } + else +# endif + png_ptr->transformations &= ~PNG_EXPAND_16; + } +#endif + +#ifdef PNG_READ_FILLER_SUPPORTED if (png_ptr->transformations & (PNG_FILLER)) { - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - max_pixel_depth = 32; - else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) { if (max_pixel_depth <= 8) max_pixel_depth = 16; + else max_pixel_depth = 32; } - else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB || + png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (max_pixel_depth <= 32) max_pixel_depth = 32; + else max_pixel_depth = 64; } } #endif -#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +#ifdef PNG_READ_GRAY_TO_RGB_SUPPORTED if (png_ptr->transformations & PNG_GRAY_TO_RGB) { if ( -#if defined(PNG_READ_EXPAND_SUPPORTED) - (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#ifdef PNG_READ_EXPAND_SUPPORTED + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || #endif -#if defined(PNG_READ_FILLER_SUPPORTED) - (png_ptr->transformations & (PNG_FILLER)) || +#ifdef PNG_READ_FILLER_SUPPORTED + (png_ptr->transformations & (PNG_FILLER)) || #endif - png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { if (max_pixel_depth <= 16) max_pixel_depth = 32; + else max_pixel_depth = 64; } + else { if (max_pixel_depth <= 8) - { - if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) max_pixel_depth = 32; - else + + else max_pixel_depth = 24; - } + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) max_pixel_depth = 64; + else max_pixel_depth = 48; } @@ -3116,46 +4344,115 @@ png_read_start_row(png_structp png_ptr) #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) - if(png_ptr->transformations & PNG_USER_TRANSFORM) - { - int user_pixel_depth=png_ptr->user_transform_depth* + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth = png_ptr->user_transform_depth * png_ptr->user_transform_channels; - if(user_pixel_depth > max_pixel_depth) - max_pixel_depth=user_pixel_depth; - } + + if (user_pixel_depth > max_pixel_depth) + max_pixel_depth = user_pixel_depth; + } #endif - /* align the width on the next larger 8 pixels. Mainly used - for interlacing */ + /* This value is stored in png_struct and double checked in the row read + * code. + */ + png_ptr->maximum_pixel_depth = (png_byte)max_pixel_depth; + png_ptr->transformed_pixel_depth = 0; /* calculated on demand */ + + /* 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); + /* Calculate the maximum bytes needed, adding a byte and a pixel + * for safety's sake + */ + row_bytes = PNG_ROWBYTES(max_pixel_depth, row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); + #ifdef PNG_MAX_MALLOC_64K if (row_bytes > (png_uint_32)65536L) png_error(png_ptr, "This image requires a row greater than 64KB"); #endif - png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64); - png_ptr->row_buf = png_ptr->big_row_buf+32; + + if (row_bytes + 48 > png_ptr->old_big_row_buf_size) + { + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->big_prev_row); + + if (png_ptr->interlaced) + png_ptr->big_row_buf = (png_bytep)png_calloc(png_ptr, + row_bytes + 48); + + else + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes + 48); + + png_ptr->big_prev_row = (png_bytep)png_malloc(png_ptr, row_bytes + 48); + +#ifdef PNG_ALIGNED_MEMORY_SUPPORTED + /* Use 16-byte aligned memory for row_buf with at least 16 bytes + * of padding before and after row_buf; treat prev_row similarly. + * NOTE: the alignment is to the start of the pixels, one beyond the start + * of the buffer, because of the filter byte. Prior to libpng 1.5.6 this + * was incorrect; the filter byte was aligned, which had the exact + * opposite effect of that intended. + */ + { + png_bytep temp = png_ptr->big_row_buf + 32; + int extra = (int)((temp - (png_bytep)0) & 0x0f); + png_ptr->row_buf = temp - extra - 1/*filter byte*/; + + temp = png_ptr->big_prev_row + 32; + extra = (int)((temp - (png_bytep)0) & 0x0f); + png_ptr->prev_row = temp - extra - 1/*filter byte*/; + } + +#else + /* Use 31 bytes of padding before and 17 bytes after row_buf. */ + png_ptr->row_buf = png_ptr->big_row_buf + 31; + png_ptr->prev_row = png_ptr->big_prev_row + 31; +#endif + png_ptr->old_big_row_buf_size = row_bytes + 48; + } #ifdef PNG_MAX_MALLOC_64K - if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) + if (png_ptr->rowbytes > 65535) png_error(png_ptr, "This image requires a row greater than 64KB"); + #endif - if ((png_uint_32)png_ptr->rowbytes > (png_uint_32) -2) - png_error(png_ptr, "Row has too many bytes to allocate in memory."); - png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( - png_ptr->rowbytes + 1)); + if (png_ptr->rowbytes > (PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory"); - png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + memset(png_ptr->prev_row, 0, png_ptr->rowbytes + 1); - png_debug1(3, "width = %lu,\n", png_ptr->width); - png_debug1(3, "height = %lu,\n", png_ptr->height); - png_debug1(3, "iwidth = %lu,\n", png_ptr->iwidth); - png_debug1(3, "num_rows = %lu\n", png_ptr->num_rows); - png_debug1(3, "rowbytes = %lu,\n", png_ptr->rowbytes); - png_debug1(3, "irowbytes = %lu,\n", png_ptr->irowbytes); + png_debug1(3, "width = %u,", png_ptr->width); + png_debug1(3, "height = %u,", png_ptr->height); + png_debug1(3, "iwidth = %u,", png_ptr->iwidth); + png_debug1(3, "num_rows = %u,", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,", (unsigned long)png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu", + (unsigned long)PNG_ROWBYTES(png_ptr->pixel_depth, png_ptr->iwidth) + 1); + + /* The sequential reader needs a buffer for IDAT, but the progressive reader + * does not, so free the read buffer now regardless; the sequential reader + * reallocates it on demand. + */ + if (png_ptr->read_buffer) + { + png_bytep buffer = png_ptr->read_buffer; + + png_ptr->read_buffer_size = 0; + png_ptr->read_buffer = NULL; + png_free(png_ptr, buffer); + } + + /* Finally claim the zstream for the inflate of the IDAT data, use the bits + * value from the stream (note that this will result in a fatal error if the + * IDAT stream has a bogus deflate header window_bits value, but this should + * not be happening any longer!) + */ + if (png_inflate_claim(png_ptr, png_IDAT, 0) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); png_ptr->flags |= PNG_FLAG_ROW_INIT; } diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngset.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngset.c index 2480a16..a1b9d49 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngset.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngset.c @@ -1,375 +1,262 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * The functions here are used during reads to store data from the file * into the info struct, and during writes to store application data * into the info struct for writing into the file. This abstracts the * info struct and allows us to change the structure in the future. */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" #if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) -#if defined(PNG_bKGD_SUPPORTED) +#ifdef PNG_bKGD_SUPPORTED void PNGAPI -png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +png_set_bKGD(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_color_16p background) { - png_debug1(1, "in %s storage function\n", "bKGD"); - if (png_ptr == NULL || info_ptr == NULL) + png_debug1(1, "in %s storage function", "bKGD"); + + if (png_ptr == NULL || info_ptr == NULL || background == NULL) return; - png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->background = *background; info_ptr->valid |= PNG_INFO_bKGD; } #endif -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED -void PNGAPI -png_set_cHRM(png_structp png_ptr, png_infop info_ptr, - double white_x, double white_y, double red_x, double red_y, - double green_x, double green_y, double blue_x, double blue_y) +#ifdef PNG_cHRM_SUPPORTED +void PNGFAPI +png_set_cHRM_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) { - png_debug1(1, "in %s storage function\n", "cHRM"); + png_xy xy; + + png_debug1(1, "in %s storage function", "cHRM fixed"); + if (png_ptr == NULL || info_ptr == NULL) return; - if (white_x < 0.0 || white_y < 0.0 || - red_x < 0.0 || red_y < 0.0 || - green_x < 0.0 || green_y < 0.0 || - blue_x < 0.0 || blue_y < 0.0) - { - png_warning(png_ptr, - "Ignoring attempt to set negative chromaticity value"); - return; - } - if (white_x > 21474.83 || white_y > 21474.83 || - red_x > 21474.83 || red_y > 21474.83 || - green_x > 21474.83 || green_y > 21474.83 || - blue_x > 21474.83 || blue_y > 21474.83) - { - png_warning(png_ptr, - "Ignoring attempt to set chromaticity value exceeding 21474.83"); - return; - } + xy.redx = red_x; + xy.redy = red_y; + xy.greenx = green_x; + xy.greeny = green_y; + xy.bluex = blue_x; + xy.bluey = blue_y; + xy.whitex = white_x; + xy.whitey = white_y; - info_ptr->x_white = (float)white_x; - info_ptr->y_white = (float)white_y; - info_ptr->x_red = (float)red_x; - info_ptr->y_red = (float)red_y; - info_ptr->x_green = (float)green_x; - info_ptr->y_green = (float)green_y; - info_ptr->x_blue = (float)blue_x; - info_ptr->y_blue = (float)blue_y; -#ifdef PNG_FIXED_POINT_SUPPORTED - info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); - info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); - info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); - info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); - info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); - info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); - info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); - info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); -#endif - info_ptr->valid |= PNG_INFO_cHRM; + if (png_colorspace_set_chromaticities(png_ptr, &info_ptr->colorspace, &xy, + 2/* override with app values*/)) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + + png_colorspace_sync_info(png_ptr, info_ptr); } -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -void PNGAPI -png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, - png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, - png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, - png_fixed_point blue_x, png_fixed_point blue_y) + +void PNGFAPI +png_set_cHRM_XYZ_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point int_red_X, png_fixed_point int_red_Y, + png_fixed_point int_red_Z, png_fixed_point int_green_X, + png_fixed_point int_green_Y, png_fixed_point int_green_Z, + png_fixed_point int_blue_X, png_fixed_point int_blue_Y, + png_fixed_point int_blue_Z) { - png_debug1(1, "in %s storage function\n", "cHRM"); + png_XYZ XYZ; + + png_debug1(1, "in %s storage function", "cHRM XYZ fixed"); + if (png_ptr == NULL || info_ptr == NULL) return; - if (white_x < 0 || white_y < 0 || - red_x < 0 || red_y < 0 || - green_x < 0 || green_y < 0 || - blue_x < 0 || blue_y < 0) - { - png_warning(png_ptr, - "Ignoring attempt to set negative chromaticity value"); - return; - } -#ifdef PNG_FLOATING_POINT_SUPPORTED - if (white_x > (double) PNG_UINT_31_MAX || - white_y > (double) PNG_UINT_31_MAX || - red_x > (double) PNG_UINT_31_MAX || - red_y > (double) PNG_UINT_31_MAX || - green_x > (double) PNG_UINT_31_MAX || - green_y > (double) PNG_UINT_31_MAX || - blue_x > (double) PNG_UINT_31_MAX || - blue_y > (double) PNG_UINT_31_MAX) -#else - if (white_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || - white_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || - red_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || - red_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || - green_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || - green_y > (png_fixed_point) PNG_UINT_31_MAX/100000L || - blue_x > (png_fixed_point) PNG_UINT_31_MAX/100000L || - blue_y > (png_fixed_point) PNG_UINT_31_MAX/100000L) -#endif - { - png_warning(png_ptr, - "Ignoring attempt to set chromaticity value exceeding 21474.83"); - return; - } - info_ptr->int_x_white = white_x; - info_ptr->int_y_white = white_y; - info_ptr->int_x_red = red_x; - info_ptr->int_y_red = red_y; - info_ptr->int_x_green = green_x; - info_ptr->int_y_green = green_y; - info_ptr->int_x_blue = blue_x; - info_ptr->int_y_blue = blue_y; -#ifdef PNG_FLOATING_POINT_SUPPORTED - info_ptr->x_white = (float)(white_x/100000.); - info_ptr->y_white = (float)(white_y/100000.); - info_ptr->x_red = (float)( red_x/100000.); - info_ptr->y_red = (float)( red_y/100000.); - info_ptr->x_green = (float)(green_x/100000.); - info_ptr->y_green = (float)(green_y/100000.); - info_ptr->x_blue = (float)( blue_x/100000.); - info_ptr->y_blue = (float)( blue_y/100000.); -#endif - info_ptr->valid |= PNG_INFO_cHRM; -} -#endif -#endif + XYZ.red_X = int_red_X; + XYZ.red_Y = int_red_Y; + XYZ.red_Z = int_red_Z; + XYZ.green_X = int_green_X; + XYZ.green_Y = int_green_Y; + XYZ.green_Z = int_green_Z; + XYZ.blue_X = int_blue_X; + XYZ.blue_Y = int_blue_Y; + XYZ.blue_Z = int_blue_Z; -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED + if (png_colorspace_set_endpoints(png_ptr, &info_ptr->colorspace, &XYZ, 2)) + info_ptr->colorspace.flags |= PNG_COLORSPACE_FROM_cHRM; + + png_colorspace_sync_info(png_ptr, info_ptr); +} + +# ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI -png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +png_set_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) { - double gamma; - png_debug1(1, "in %s storage function\n", "gAMA"); + png_set_cHRM_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, white_x, "cHRM White X"), + png_fixed(png_ptr, white_y, "cHRM White Y"), + png_fixed(png_ptr, red_x, "cHRM Red X"), + png_fixed(png_ptr, red_y, "cHRM Red Y"), + png_fixed(png_ptr, green_x, "cHRM Green X"), + png_fixed(png_ptr, green_y, "cHRM Green Y"), + png_fixed(png_ptr, blue_x, "cHRM Blue X"), + png_fixed(png_ptr, blue_y, "cHRM Blue Y")); +} + +void PNGAPI +png_set_cHRM_XYZ(png_const_structrp png_ptr, png_inforp info_ptr, double red_X, + double red_Y, double red_Z, double green_X, double green_Y, double green_Z, + double blue_X, double blue_Y, double blue_Z) +{ + png_set_cHRM_XYZ_fixed(png_ptr, info_ptr, + png_fixed(png_ptr, red_X, "cHRM Red X"), + png_fixed(png_ptr, red_Y, "cHRM Red Y"), + png_fixed(png_ptr, red_Z, "cHRM Red Z"), + png_fixed(png_ptr, green_X, "cHRM Red X"), + png_fixed(png_ptr, green_Y, "cHRM Red Y"), + png_fixed(png_ptr, green_Z, "cHRM Red Z"), + png_fixed(png_ptr, blue_X, "cHRM Red X"), + png_fixed(png_ptr, blue_Y, "cHRM Red Y"), + png_fixed(png_ptr, blue_Z, "cHRM Red Z")); +} +# endif /* PNG_FLOATING_POINT_SUPPORTED */ + +#endif /* PNG_cHRM_SUPPORTED */ + +#ifdef PNG_gAMA_SUPPORTED +void PNGFAPI +png_set_gAMA_fixed(png_const_structrp png_ptr, png_inforp info_ptr, + png_fixed_point file_gamma) +{ + png_debug1(1, "in %s storage function", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) return; - /* Check for overflow */ - if (file_gamma > 21474.83) - { - png_warning(png_ptr, "Limiting gamma to 21474.83"); - gamma=21474.83; - } - else - gamma=file_gamma; - info_ptr->gamma = (float)gamma; -#ifdef PNG_FIXED_POINT_SUPPORTED - info_ptr->int_gamma = (int)(gamma*100000.+.5); -#endif - info_ptr->valid |= PNG_INFO_gAMA; - if(gamma == 0.0) - png_warning(png_ptr, "Setting gamma=0"); + png_colorspace_set_gamma(png_ptr, &info_ptr->colorspace, file_gamma); + png_colorspace_sync_info(png_ptr, info_ptr); } -#endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED void PNGAPI -png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point - int_gamma) +png_set_gAMA(png_const_structrp png_ptr, png_inforp info_ptr, double file_gamma) { - png_fixed_point gamma; - - png_debug1(1, "in %s storage function\n", "gAMA"); - if (png_ptr == NULL || info_ptr == NULL) - return; - - if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX) - { - png_warning(png_ptr, "Limiting gamma to 21474.83"); - gamma=PNG_UINT_31_MAX; - } - else - { - if (int_gamma < 0) - { - png_warning(png_ptr, "Setting negative gamma to zero"); - gamma=0; - } - else - gamma=int_gamma; - } -#ifdef PNG_FLOATING_POINT_SUPPORTED - info_ptr->gamma = (float)(gamma/100000.); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED - info_ptr->int_gamma = gamma; -#endif - info_ptr->valid |= PNG_INFO_gAMA; - if(gamma == 0) - png_warning(png_ptr, "Setting gamma=0"); + png_set_gAMA_fixed(png_ptr, info_ptr, png_fixed(png_ptr, file_gamma, + "png_set_gAMA")); } +# endif #endif -#if defined(PNG_hIST_SUPPORTED) +#ifdef PNG_hIST_SUPPORTED void PNGAPI -png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +png_set_hIST(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_uint_16p hist) { int i; - png_debug1(1, "in %s storage function\n", "hIST"); + png_debug1(1, "in %s storage function", "hIST"); + if (png_ptr == NULL || info_ptr == NULL) return; + if (info_ptr->num_palette == 0 || info_ptr->num_palette > PNG_MAX_PALETTE_LENGTH) { - png_warning(png_ptr, - "Invalid palette size, hIST allocation skipped."); - return; + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped"); + + return; } -#ifdef PNG_FREE_ME_SUPPORTED png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); -#endif - /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in version - 1.2.1 */ - png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, - (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof (png_uint_16))); - if (png_ptr->hist == NULL) - { - png_warning(png_ptr, "Insufficient memory for hIST chunk data."); - return; - } + + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in + * version 1.2.1 + */ + info_ptr->hist = png_voidcast(png_uint_16p, png_malloc_warn(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_uint_16)))); + + if (info_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data"); + return; + } + + info_ptr->free_me |= PNG_FREE_HIST; for (i = 0; i < info_ptr->num_palette; i++) - png_ptr->hist[i] = hist[i]; - info_ptr->hist = png_ptr->hist; - info_ptr->valid |= PNG_INFO_hIST; + info_ptr->hist[i] = hist[i]; -#ifdef PNG_FREE_ME_SUPPORTED - info_ptr->free_me |= PNG_FREE_HIST; -#else - png_ptr->flags |= PNG_FLAG_FREE_HIST; -#endif + info_ptr->valid |= PNG_INFO_hIST; } #endif void PNGAPI -png_set_IHDR(png_structp png_ptr, png_infop info_ptr, - png_uint_32 width, png_uint_32 height, int bit_depth, - int color_type, int interlace_type, int compression_type, - int filter_type) +png_set_IHDR(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) { - png_debug1(1, "in %s storage function\n", "IHDR"); + png_debug1(1, "in %s storage function", "IHDR"); + if (png_ptr == NULL || info_ptr == NULL) return; - /* check for width and height valid values */ - if (width == 0 || height == 0) - png_error(png_ptr, "Image width or height is zero in IHDR"); -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - if (width > png_ptr->user_width_max || height > png_ptr->user_height_max) - png_error(png_ptr, "image size exceeds user limits in IHDR"); -#else - if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX) - png_error(png_ptr, "image size exceeds user limits in IHDR"); -#endif - if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX) - png_error(png_ptr, "Invalid image size in IHDR"); - if ( width > (PNG_UINT_32_MAX - >> 3) /* 8-byte RGBA pixels */ - - 64 /* bigrowbuf hack */ - - 1 /* filter byte */ - - 7*8 /* rounding of width to multiple of 8 pixels */ - - 8) /* extra max_pixel_depth pad */ - png_warning(png_ptr, "Width is too large for libpng to process pixels"); - - /* check other values */ - if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && - bit_depth != 8 && bit_depth != 16) - png_error(png_ptr, "Invalid bit depth in IHDR"); - - if (color_type < 0 || color_type == 1 || - color_type == 5 || color_type > 6) - png_error(png_ptr, "Invalid color type in IHDR"); - - if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || - ((color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_GRAY_ALPHA || - color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) - png_error(png_ptr, "Invalid color type/bit depth combination in IHDR"); - - if (interlace_type >= PNG_INTERLACE_LAST) - png_error(png_ptr, "Unknown interlace method in IHDR"); - - if (compression_type != PNG_COMPRESSION_TYPE_BASE) - png_error(png_ptr, "Unknown compression method in IHDR"); - -#if defined(PNG_MNG_FEATURES_SUPPORTED) - /* Accept filter_method 64 (intrapixel differencing) only if - * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and - * 2. Libpng did not read a PNG signature (this filter_method is only - * used in PNG datastreams that are embedded in MNG datastreams) and - * 3. The application called png_permit_mng_features with a mask that - * included PNG_FLAG_MNG_FILTER_64 and - * 4. The filter_method is 64 and - * 5. The color_type is RGB or RGBA - */ - if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted) - png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); - if(filter_type != PNG_FILTER_TYPE_BASE) - { - if(!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && - ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && - (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_RGB_ALPHA))) - png_error(png_ptr, "Unknown filter method in IHDR"); - if(png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) - png_warning(png_ptr, "Invalid filter method in IHDR"); - } -#else - if(filter_type != PNG_FILTER_TYPE_BASE) - png_error(png_ptr, "Unknown filter method in IHDR"); -#endif - info_ptr->width = width; info_ptr->height = height; info_ptr->bit_depth = (png_byte)bit_depth; - info_ptr->color_type =(png_byte) color_type; + info_ptr->color_type = (png_byte)color_type; info_ptr->compression_type = (png_byte)compression_type; info_ptr->filter_type = (png_byte)filter_type; info_ptr->interlace_type = (png_byte)interlace_type; + + png_check_IHDR (png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->interlace_type, + info_ptr->compression_type, info_ptr->filter_type); + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) info_ptr->channels = 3; + else info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); - /* check for potential overflow */ - if (width > (PNG_UINT_32_MAX - >> 3) /* 8-byte RGBA pixels */ - - 64 /* bigrowbuf hack */ - - 1 /* filter byte */ - - 7*8 /* rounding of width to multiple of 8 pixels */ - - 8) /* extra max_pixel_depth pad */ - info_ptr->rowbytes = (png_size_t)0; + /* Check for potential overflow */ + if (width > + (PNG_UINT_32_MAX >> 3) /* 8-byte RRGGBBAA pixels */ + - 48 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = 0; else - info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,width); + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth, width); } -#if defined(PNG_oFFs_SUPPORTED) +#ifdef PNG_oFFs_SUPPORTED void PNGAPI -png_set_oFFs(png_structp png_ptr, png_infop info_ptr, - png_int_32 offset_x, png_int_32 offset_y, int unit_type) +png_set_oFFs(png_const_structrp png_ptr, png_inforp info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) { - png_debug1(1, "in %s storage function\n", "oFFs"); + png_debug1(1, "in %s storage function", "oFFs"); + if (png_ptr == NULL || info_ptr == NULL) return; @@ -380,141 +267,236 @@ png_set_oFFs(png_structp png_ptr, png_infop info_ptr, } #endif -#if defined(PNG_pCAL_SUPPORTED) +#ifdef PNG_pCAL_SUPPORTED void PNGAPI -png_set_pCAL(png_structp png_ptr, png_infop info_ptr, - png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, - png_charp units, png_charpp params) +png_set_pCAL(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_charp purpose, png_int_32 X0, png_int_32 X1, int type, + int nparams, png_const_charp units, png_charpp params) { - png_uint_32 length; + png_size_t length; int i; - png_debug1(1, "in %s storage function\n", "pCAL"); - if (png_ptr == NULL || info_ptr == NULL) + png_debug1(1, "in %s storage function", "pCAL"); + + if (png_ptr == NULL || info_ptr == NULL || purpose == NULL || units == NULL + || (nparams > 0 && params == NULL)) return; - length = png_strlen(purpose) + 1; - png_debug1(3, "allocating purpose for info (%lu bytes)\n", length); - info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); - if (info_ptr->pcal_purpose == NULL) - { - png_warning(png_ptr, "Insufficient memory for pCAL purpose."); - return; - } - png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + length = strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)", + (unsigned long)length); - png_debug(3, "storing X0, X1, type, and nparams in info\n"); + /* TODO: validate format of calibration name and unit name */ + + /* Check that the type matches the specification. */ + if (type < 0 || type > 3) + png_error(png_ptr, "Invalid pCAL equation type"); + + if (nparams < 0 || nparams > 255) + png_error(png_ptr, "Invalid pCAL parameter count"); + + /* Validate params[nparams] */ + for (i=0; ipcal_purpose = png_voidcast(png_charp, + png_malloc_warn(png_ptr, length)); + + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose"); + return; + } + + memcpy(info_ptr->pcal_purpose, purpose, length); + + png_debug(3, "storing X0, X1, type, and nparams in info"); info_ptr->pcal_X0 = X0; info_ptr->pcal_X1 = X1; info_ptr->pcal_type = (png_byte)type; info_ptr->pcal_nparams = (png_byte)nparams; - length = png_strlen(units) + 1; - png_debug1(3, "allocating units for info (%lu bytes)\n", length); - info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + length = strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)", + (unsigned long)length); + + info_ptr->pcal_units = png_voidcast(png_charp, + png_malloc_warn(png_ptr, length)); + if (info_ptr->pcal_units == NULL) - { - png_warning(png_ptr, "Insufficient memory for pCAL units."); - return; - } - png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + { + png_warning(png_ptr, "Insufficient memory for pCAL units"); + return; + } + + memcpy(info_ptr->pcal_units, units, length); + + info_ptr->pcal_params = png_voidcast(png_charpp, png_malloc_warn(png_ptr, + (png_size_t)((nparams + 1) * (sizeof (png_charp))))); - info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, - (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); if (info_ptr->pcal_params == NULL) - { - png_warning(png_ptr, "Insufficient memory for pCAL params."); - return; - } + { + png_warning(png_ptr, "Insufficient memory for pCAL params"); + return; + } - info_ptr->pcal_params[nparams] = NULL; + memset(info_ptr->pcal_params, 0, (nparams + 1) * (sizeof (png_charp))); for (i = 0; i < nparams; i++) { - length = png_strlen(params[i]) + 1; - png_debug2(3, "allocating parameter %d for info (%lu bytes)\n", i, length); + length = strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)", i, + (unsigned long)length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) - { - png_warning(png_ptr, "Insufficient memory for pCAL parameter."); - return; - } - png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter"); + return; + } + + memcpy(info_ptr->pcal_params[i], params[i], length); } info_ptr->valid |= PNG_INFO_pCAL; -#ifdef PNG_FREE_ME_SUPPORTED info_ptr->free_me |= PNG_FREE_PCAL; -#endif } #endif -#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_sCAL_SUPPORTED void PNGAPI -png_set_sCAL(png_structp png_ptr, png_infop info_ptr, - int unit, double width, double height) +png_set_sCAL_s(png_const_structrp png_ptr, png_inforp info_ptr, + int unit, png_const_charp swidth, png_const_charp sheight) { - png_debug1(1, "in %s storage function\n", "sCAL"); + png_size_t lengthw = 0, lengthh = 0; + + png_debug1(1, "in %s storage function", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) return; - info_ptr->scal_unit = (png_byte)unit; - info_ptr->scal_pixel_width = width; - info_ptr->scal_pixel_height = height; + /* Double check the unit (should never get here with an invalid + * unit unless this is an API call.) + */ + if (unit != 1 && unit != 2) + png_error(png_ptr, "Invalid sCAL unit"); - info_ptr->valid |= PNG_INFO_sCAL; -} -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -void PNGAPI -png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, - int unit, png_charp swidth, png_charp sheight) -{ - png_uint_32 length; + if (swidth == NULL || (lengthw = strlen(swidth)) == 0 || + swidth[0] == 45 /* '-' */ || !png_check_fp_string(swidth, lengthw)) + png_error(png_ptr, "Invalid sCAL width"); - png_debug1(1, "in %s storage function\n", "sCAL"); - if (png_ptr == NULL || info_ptr == NULL) - return; + if (sheight == NULL || (lengthh = strlen(sheight)) == 0 || + sheight[0] == 45 /* '-' */ || !png_check_fp_string(sheight, lengthh)) + png_error(png_ptr, "Invalid sCAL height"); info_ptr->scal_unit = (png_byte)unit; - length = png_strlen(swidth) + 1; - png_debug1(3, "allocating unit for info (%d bytes)\n", length); - info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + ++lengthw; + + png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthw); + + info_ptr->scal_s_width = png_voidcast(png_charp, + png_malloc_warn(png_ptr, lengthw)); + if (info_ptr->scal_s_width == NULL) { - png_warning(png_ptr, - "Memory allocation failed while processing sCAL."); + png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + return; } - png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); - length = png_strlen(sheight) + 1; - png_debug1(3, "allocating unit for info (%d bytes)\n", length); - info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + memcpy(info_ptr->scal_s_width, swidth, lengthw); + + ++lengthh; + + png_debug1(3, "allocating unit for info (%u bytes)", (unsigned int)lengthh); + + info_ptr->scal_s_height = png_voidcast(png_charp, + png_malloc_warn(png_ptr, lengthh)); + if (info_ptr->scal_s_height == NULL) { png_free (png_ptr, info_ptr->scal_s_width); - png_warning(png_ptr, - "Memory allocation failed while processing sCAL."); + info_ptr->scal_s_width = NULL; + + png_warning(png_ptr, "Memory allocation failed while processing sCAL"); + return; } - png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + + memcpy(info_ptr->scal_s_height, sheight, lengthh); info_ptr->valid |= PNG_INFO_sCAL; -#ifdef PNG_FREE_ME_SUPPORTED info_ptr->free_me |= PNG_FREE_SCAL; -#endif } -#endif -#endif + +# ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_const_structrp png_ptr, png_inforp info_ptr, int unit, + double width, double height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + /* Check the arguments. */ + if (width <= 0) + png_warning(png_ptr, "Invalid sCAL width ignored"); + + else if (height <= 0) + png_warning(png_ptr, "Invalid sCAL height ignored"); + + else + { + /* Convert 'width' and 'height' to ASCII. */ + char swidth[PNG_sCAL_MAX_DIGITS+1]; + char sheight[PNG_sCAL_MAX_DIGITS+1]; + + png_ascii_from_fp(png_ptr, swidth, (sizeof swidth), width, + PNG_sCAL_PRECISION); + png_ascii_from_fp(png_ptr, sheight, (sizeof sheight), height, + PNG_sCAL_PRECISION); + + png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); + } +} +# endif + +# ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_fixed(png_const_structrp png_ptr, png_inforp info_ptr, int unit, + png_fixed_point width, png_fixed_point height) +{ + png_debug1(1, "in %s storage function", "sCAL"); + + /* Check the arguments. */ + if (width <= 0) + png_warning(png_ptr, "Invalid sCAL width ignored"); + + else if (height <= 0) + png_warning(png_ptr, "Invalid sCAL height ignored"); + + else + { + /* Convert 'width' and 'height' to ASCII. */ + char swidth[PNG_sCAL_MAX_DIGITS+1]; + char sheight[PNG_sCAL_MAX_DIGITS+1]; + + png_ascii_from_fixed(png_ptr, swidth, (sizeof swidth), width); + png_ascii_from_fixed(png_ptr, sheight, (sizeof sheight), height); + + png_set_sCAL_s(png_ptr, info_ptr, unit, swidth, sheight); + } +} +# endif #endif -#if defined(PNG_pHYs_SUPPORTED) +#ifdef PNG_pHYs_SUPPORTED void PNGAPI -png_set_pHYs(png_structp png_ptr, png_infop info_ptr, - png_uint_32 res_x, png_uint_32 res_y, int unit_type) +png_set_pHYs(png_const_structrp png_ptr, png_inforp info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) { - png_debug1(1, "in %s storage function\n", "pHYs"); + png_debug1(1, "in %s storage function", "pHYs"); + if (png_ptr == NULL || info_ptr == NULL) return; @@ -526,718 +508,1099 @@ png_set_pHYs(png_structp png_ptr, png_infop info_ptr, #endif void PNGAPI -png_set_PLTE(png_structp png_ptr, png_infop info_ptr, - png_colorp palette, int num_palette) +png_set_PLTE(png_structrp png_ptr, png_inforp info_ptr, + png_const_colorp palette, int num_palette) { - png_debug1(1, "in %s storage function\n", "PLTE"); + png_debug1(1, "in %s storage function", "PLTE"); + if (png_ptr == NULL || info_ptr == NULL) return; if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) - { - if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_error(png_ptr, "Invalid palette length"); - else - { + + else + { png_warning(png_ptr, "Invalid palette length"); return; - } - } + } + } - /* - * It may not actually be necessary to set png_ptr->palette here; + if ((num_palette > 0 && palette == NULL) || + (num_palette == 0 +# ifdef PNG_MNG_FEATURES_SUPPORTED + && (png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) == 0 +# endif + )) + { + png_chunk_report(png_ptr, "Invalid palette", PNG_CHUNK_ERROR); + return; + } + + /* It may not actually be necessary to set png_ptr->palette here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. + * + * 1.6.0: the above statement appears to be incorrect; something has to set + * the palette inside png_struct on read. */ -#ifdef PNG_FREE_ME_SUPPORTED png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); -#endif /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead - of num_palette entries, - in case of an invalid PNG file that has too-large sample values. */ - png_ptr->palette = (png_colorp)png_malloc(png_ptr, - PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); - png_memset(png_ptr->palette, 0, PNG_MAX_PALETTE_LENGTH * - png_sizeof(png_color)); - png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof (png_color)); + * of num_palette entries, in case of an invalid PNG file that has + * too-large sample values. + */ + png_ptr->palette = png_voidcast(png_colorp, png_calloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)))); + + if (num_palette > 0) + memcpy(png_ptr->palette, palette, num_palette * (sizeof (png_color))); info_ptr->palette = png_ptr->palette; info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; -#ifdef PNG_FREE_ME_SUPPORTED info_ptr->free_me |= PNG_FREE_PLTE; -#else - png_ptr->flags |= PNG_FLAG_FREE_PLTE; -#endif info_ptr->valid |= PNG_INFO_PLTE; } -#if defined(PNG_sBIT_SUPPORTED) +#ifdef PNG_sBIT_SUPPORTED void PNGAPI -png_set_sBIT(png_structp png_ptr, png_infop info_ptr, - png_color_8p sig_bit) +png_set_sBIT(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_color_8p sig_bit) { - png_debug1(1, "in %s storage function\n", "sBIT"); - if (png_ptr == NULL || info_ptr == NULL) + png_debug1(1, "in %s storage function", "sBIT"); + + if (png_ptr == NULL || info_ptr == NULL || sig_bit == NULL) return; - png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof (png_color_8)); + info_ptr->sig_bit = *sig_bit; info_ptr->valid |= PNG_INFO_sBIT; } #endif -#if defined(PNG_sRGB_SUPPORTED) +#ifdef PNG_sRGB_SUPPORTED void PNGAPI -png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +png_set_sRGB(png_const_structrp png_ptr, png_inforp info_ptr, int srgb_intent) { - png_debug1(1, "in %s storage function\n", "sRGB"); + png_debug1(1, "in %s storage function", "sRGB"); + if (png_ptr == NULL || info_ptr == NULL) return; - info_ptr->srgb_intent = (png_byte)intent; - info_ptr->valid |= PNG_INFO_sRGB; + (void)png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent); + png_colorspace_sync_info(png_ptr, info_ptr); } void PNGAPI -png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, - int intent) +png_set_sRGB_gAMA_and_cHRM(png_const_structrp png_ptr, png_inforp info_ptr, + int srgb_intent) { -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED - float file_gamma; -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED - png_fixed_point int_file_gamma; -#endif -#endif -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED - float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED - png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, - int_green_y, int_blue_x, int_blue_y; -#endif -#endif - png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM"); + png_debug1(1, "in %s storage function", "sRGB_gAMA_and_cHRM"); + if (png_ptr == NULL || info_ptr == NULL) return; - png_set_sRGB(png_ptr, info_ptr, intent); + if (png_colorspace_set_sRGB(png_ptr, &info_ptr->colorspace, srgb_intent)) + { + /* This causes the gAMA and cHRM to be written too */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } -#if defined(PNG_gAMA_SUPPORTED) -#ifdef PNG_FLOATING_POINT_SUPPORTED - file_gamma = (float).45455; - png_set_gAMA(png_ptr, info_ptr, file_gamma); -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED - int_file_gamma = 45455L; - png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); -#endif -#endif - -#if defined(PNG_cHRM_SUPPORTED) -#ifdef PNG_FIXED_POINT_SUPPORTED - int_white_x = 31270L; - int_white_y = 32900L; - int_red_x = 64000L; - int_red_y = 33000L; - int_green_x = 30000L; - int_green_y = 60000L; - int_blue_x = 15000L; - int_blue_y = 6000L; - - png_set_cHRM_fixed(png_ptr, info_ptr, - int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, - int_blue_x, int_blue_y); -#endif -#ifdef PNG_FLOATING_POINT_SUPPORTED - white_x = (float).3127; - white_y = (float).3290; - red_x = (float).64; - red_y = (float).33; - green_x = (float).30; - green_y = (float).60; - blue_x = (float).15; - blue_y = (float).06; - - png_set_cHRM(png_ptr, info_ptr, - white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); -#endif -#endif + png_colorspace_sync_info(png_ptr, info_ptr); } -#endif +#endif /* sRGB */ -#if defined(PNG_iCCP_SUPPORTED) +#ifdef PNG_iCCP_SUPPORTED void PNGAPI -png_set_iCCP(png_structp png_ptr, png_infop info_ptr, - png_charp name, int compression_type, - png_charp profile, png_uint_32 proflen) +png_set_iCCP(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_charp name, int compression_type, + png_const_bytep profile, png_uint_32 proflen) { png_charp new_iccp_name; - png_charp new_iccp_profile; + png_bytep new_iccp_profile; + png_size_t length; + + png_debug1(1, "in %s storage function", "iCCP"); - png_debug1(1, "in %s storage function\n", "iCCP"); if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) return; - new_iccp_name = (png_charp)png_malloc_warn(png_ptr, png_strlen(name)+1); + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_app_error(png_ptr, "Invalid iCCP compression method"); + + /* Set the colorspace first because this validates the profile; do not + * override previously set app cHRM or gAMA here (because likely as not the + * application knows better than libpng what the correct values are.) Pass + * the info_ptr color_type field to png_colorspace_set_ICC because in the + * write case it has not yet been stored in png_ptr. + */ + { + int result = png_colorspace_set_ICC(png_ptr, &info_ptr->colorspace, name, + proflen, profile, info_ptr->color_type); + + png_colorspace_sync_info(png_ptr, info_ptr); + + /* Don't do any of the copying if the profile was bad, or inconsistent. */ + if (!result) + return; + + /* But do write the gAMA and cHRM chunks from the profile. */ + info_ptr->colorspace.flags |= + PNG_COLORSPACE_FROM_gAMA|PNG_COLORSPACE_FROM_cHRM; + } + + length = strlen(name)+1; + new_iccp_name = png_voidcast(png_charp, png_malloc_warn(png_ptr, length)); + if (new_iccp_name == NULL) { - png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + png_benign_error(png_ptr, "Insufficient memory to process iCCP chunk"); return; } - png_strncpy(new_iccp_name, name, png_strlen(name)+1); - new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + + memcpy(new_iccp_name, name, length); + new_iccp_profile = png_voidcast(png_bytep, + png_malloc_warn(png_ptr, proflen)); + if (new_iccp_profile == NULL) { - png_free (png_ptr, new_iccp_name); - png_warning(png_ptr, "Insufficient memory to process iCCP profile."); + png_free(png_ptr, new_iccp_name); + png_benign_error(png_ptr, + "Insufficient memory to process iCCP profile"); return; } - png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + memcpy(new_iccp_profile, profile, proflen); png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); info_ptr->iccp_proflen = proflen; info_ptr->iccp_name = new_iccp_name; info_ptr->iccp_profile = new_iccp_profile; - /* Compression is always zero but is here so the API and info structure - * does not have to change if we introduce multiple compression types */ - info_ptr->iccp_compression = (png_byte)compression_type; -#ifdef PNG_FREE_ME_SUPPORTED info_ptr->free_me |= PNG_FREE_ICCP; -#endif info_ptr->valid |= PNG_INFO_iCCP; } #endif -#if defined(PNG_TEXT_SUPPORTED) +#ifdef PNG_TEXT_SUPPORTED void PNGAPI -png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, - int num_text) +png_set_text(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_textp text_ptr, int num_text) { int ret; - ret=png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) - png_error(png_ptr, "Insufficient memory to store text"); + png_error(png_ptr, "Insufficient memory to store text"); } int /* PRIVATE */ -png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, - int num_text) +png_set_text_2(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_textp text_ptr, int num_text) { int i; - png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ? - "text" : (png_const_charp)png_ptr->chunk_name)); + png_debug1(1, "in %lx storage function", png_ptr == NULL ? "unexpected" : + (unsigned long)png_ptr->chunk_name); - if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + if (png_ptr == NULL || info_ptr == NULL || num_text <= 0 || text_ptr == NULL) return(0); /* Make sure we have enough space in the "text" array in info_struct - * to hold all of the incoming text_ptr objects. + * to hold all of the incoming text_ptr objects. This compare can't overflow + * because max_text >= num_text (anyway, subtract of two positive integers + * can't overflow in any case.) */ - if (info_ptr->num_text + num_text > info_ptr->max_text) + if (num_text > info_ptr->max_text - info_ptr->num_text) { - if (info_ptr->text != NULL) - { - png_textp old_text; - int old_max; + int old_num_text = info_ptr->num_text; + int max_text; + png_textp new_text = NULL; - old_max = info_ptr->max_text; - info_ptr->max_text = info_ptr->num_text + num_text + 8; - old_text = info_ptr->text; - info_ptr->text = (png_textp)png_malloc_warn(png_ptr, - (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); - if (info_ptr->text == NULL) - { - png_free(png_ptr, old_text); - return(1); - } - png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * - png_sizeof(png_text))); - png_free(png_ptr, old_text); - } - else + /* Calculate an appropriate max_text, checking for overflow. */ + max_text = old_num_text; + if (num_text <= INT_MAX - max_text) { - info_ptr->max_text = num_text + 8; - info_ptr->num_text = 0; - info_ptr->text = (png_textp)png_malloc_warn(png_ptr, - (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); - if (info_ptr->text == NULL) - return(1); -#ifdef PNG_FREE_ME_SUPPORTED - info_ptr->free_me |= PNG_FREE_TEXT; -#endif + max_text += num_text; + + /* Round up to a multiple of 8 */ + if (max_text < INT_MAX-8) + max_text = (max_text + 8) & ~0x7; + + else + max_text = INT_MAX; + + /* Now allocate a new array and copy the old members in, this does all + * the overflow checks. + */ + new_text = png_voidcast(png_textp,png_realloc_array(png_ptr, + info_ptr->text, old_num_text, max_text-old_num_text, + sizeof *new_text)); } - png_debug1(3, "allocated %d entries for info_ptr->text\n", - info_ptr->max_text); + + if (new_text == NULL) + { + png_chunk_report(png_ptr, "too many text chunks", + PNG_CHUNK_WRITE_ERROR); + return 1; + } + + png_free(png_ptr, info_ptr->text); + + info_ptr->text = new_text; + info_ptr->free_me |= PNG_FREE_TEXT; + info_ptr->max_text = max_text; + /* num_text is adjusted below as the entries are copied in */ + + png_debug1(3, "allocated %d entries for info_ptr->text", max_text); } + for (i = 0; i < num_text; i++) { - png_size_t text_length,key_len; - png_size_t lang_len,lang_key_len; + size_t text_length, key_len; + size_t lang_len, lang_key_len; png_textp textp = &(info_ptr->text[info_ptr->num_text]); if (text_ptr[i].key == NULL) continue; - key_len = png_strlen(text_ptr[i].key); + if (text_ptr[i].compression < PNG_TEXT_COMPRESSION_NONE || + text_ptr[i].compression >= PNG_TEXT_COMPRESSION_LAST) + { + png_chunk_report(png_ptr, "text compression mode is out of range", + PNG_CHUNK_WRITE_ERROR); + continue; + } - if(text_ptr[i].compression <= 0) + key_len = strlen(text_ptr[i].key); + + if (text_ptr[i].compression <= 0) { - lang_len = 0; - lang_key_len = 0; + lang_len = 0; + lang_key_len = 0; } + else -#ifdef PNG_iTXt_SUPPORTED +# ifdef PNG_iTXt_SUPPORTED { - /* set iTXt data */ - if (text_ptr[i].lang != NULL) - lang_len = png_strlen(text_ptr[i].lang); - else - lang_len = 0; - if (text_ptr[i].lang_key != NULL) - lang_key_len = png_strlen(text_ptr[i].lang_key); - else - lang_key_len = 0; + /* Set iTXt data */ + + if (text_ptr[i].lang != NULL) + lang_len = strlen(text_ptr[i].lang); + + else + lang_len = 0; + + if (text_ptr[i].lang_key != NULL) + lang_key_len = strlen(text_ptr[i].lang_key); + + else + lang_key_len = 0; } -#else +# else /* PNG_iTXt_SUPPORTED */ { - png_warning(png_ptr, "iTXt chunk not supported."); - continue; + png_chunk_report(png_ptr, "iTXt chunk not supported", + PNG_CHUNK_WRITE_ERROR); + continue; } -#endif +# endif if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') { text_length = 0; -#ifdef PNG_iTXt_SUPPORTED - if(text_ptr[i].compression > 0) +# ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) textp->compression = PNG_ITXT_COMPRESSION_NONE; + else -#endif +# endif textp->compression = PNG_TEXT_COMPRESSION_NONE; } + else { - text_length = png_strlen(text_ptr[i].text); + text_length = strlen(text_ptr[i].text); textp->compression = text_ptr[i].compression; } - textp->key = (png_charp)png_malloc_warn(png_ptr, - (png_uint_32)(key_len + text_length + lang_len + lang_key_len + 4)); - if (textp->key == NULL) - return(1); - png_debug2(2, "Allocated %lu bytes at %x in png_set_text\n", - (png_uint_32)(key_len + lang_len + lang_key_len + text_length + 4), - (int)textp->key); + textp->key = png_voidcast(png_charp,png_malloc_base(png_ptr, + key_len + text_length + lang_len + lang_key_len + 4)); + + if (textp->key == NULL) + { + png_chunk_report(png_ptr, "text chunk: out of memory", + PNG_CHUNK_WRITE_ERROR); + return 1; + } + + png_debug2(2, "Allocated %lu bytes at %p in png_set_text", + (unsigned long)(png_uint_32) + (key_len + lang_len + lang_key_len + text_length + 4), + textp->key); + + memcpy(textp->key, text_ptr[i].key, key_len); + *(textp->key + key_len) = '\0'; - png_memcpy(textp->key, text_ptr[i].key, - (png_size_t)(key_len)); - *(textp->key+key_len) = '\0'; -#ifdef PNG_iTXt_SUPPORTED if (text_ptr[i].compression > 0) { - textp->lang=textp->key + key_len + 1; - png_memcpy(textp->lang, text_ptr[i].lang, lang_len); - *(textp->lang+lang_len) = '\0'; - textp->lang_key=textp->lang + lang_len + 1; - png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); - *(textp->lang_key+lang_key_len) = '\0'; - textp->text=textp->lang_key + lang_key_len + 1; + textp->lang = textp->key + key_len + 1; + memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang + lang_len) = '\0'; + textp->lang_key = textp->lang + lang_len + 1; + memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key + lang_key_len) = '\0'; + textp->text = textp->lang_key + lang_key_len + 1; } + else -#endif { -#ifdef PNG_iTXt_SUPPORTED textp->lang=NULL; textp->lang_key=NULL; -#endif - textp->text=textp->key + key_len + 1; + textp->text = textp->key + key_len + 1; } - if(text_length) - png_memcpy(textp->text, text_ptr[i].text, - (png_size_t)(text_length)); - *(textp->text+text_length) = '\0'; -#ifdef PNG_iTXt_SUPPORTED - if(textp->compression > 0) + if (text_length) + memcpy(textp->text, text_ptr[i].text, text_length); + + *(textp->text + text_length) = '\0'; + +# ifdef PNG_iTXt_SUPPORTED + if (textp->compression > 0) { textp->text_length = 0; textp->itxt_length = text_length; } + else -#endif +# endif { textp->text_length = text_length; -#ifdef PNG_iTXt_SUPPORTED textp->itxt_length = 0; -#endif } + info_ptr->num_text++; - png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text); + png_debug1(3, "transferred text chunk %d", info_ptr->num_text); } + return(0); } #endif -#if defined(PNG_tIME_SUPPORTED) +#ifdef PNG_tIME_SUPPORTED void PNGAPI -png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +png_set_tIME(png_const_structrp png_ptr, png_inforp info_ptr, + png_const_timep mod_time) { - png_debug1(1, "in %s storage function\n", "tIME"); - if (png_ptr == NULL || info_ptr == NULL || + png_debug1(1, "in %s storage function", "tIME"); + + if (png_ptr == NULL || info_ptr == NULL || mod_time == NULL || (png_ptr->mode & PNG_WROTE_tIME)) return; - png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof (png_time)); + if (mod_time->month == 0 || mod_time->month > 12 || + mod_time->day == 0 || mod_time->day > 31 || + mod_time->hour > 23 || mod_time->minute > 59 || + mod_time->second > 60) + { + png_warning(png_ptr, "Ignoring invalid time value"); + return; + } + + info_ptr->mod_time = *mod_time; info_ptr->valid |= PNG_INFO_tIME; } #endif -#if defined(PNG_tRNS_SUPPORTED) +#ifdef PNG_tRNS_SUPPORTED void PNGAPI -png_set_tRNS(png_structp png_ptr, png_infop info_ptr, - png_bytep trans, int num_trans, png_color_16p trans_values) +png_set_tRNS(png_structrp png_ptr, png_inforp info_ptr, + png_const_bytep trans_alpha, int num_trans, png_const_color_16p trans_color) { - png_debug1(1, "in %s storage function\n", "tRNS"); + png_debug1(1, "in %s storage function", "tRNS"); + if (png_ptr == NULL || info_ptr == NULL) return; - if (trans != NULL) + if (trans_alpha != NULL) { - /* - * It may not actually be necessary to set png_ptr->trans here; + /* It may not actually be necessary to set png_ptr->trans_alpha here; * we do it for backward compatibility with the way the png_handle_tRNS * function used to do the allocation. + * + * 1.6.0: The above statement is incorrect; png_handle_tRNS effectively + * relies on png_set_tRNS storing the information in png_struct + * (otherwise it won't be there for the code in pngrtran.c). */ -#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); -#endif + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ - png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, - (png_uint_32)PNG_MAX_PALETTE_LENGTH); - if (num_trans <= PNG_MAX_PALETTE_LENGTH) - png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); -#ifdef PNG_FREE_ME_SUPPORTED - info_ptr->free_me |= PNG_FREE_TRNS; -#else - png_ptr->flags |= PNG_FLAG_FREE_TRNS; -#endif + png_ptr->trans_alpha = info_ptr->trans_alpha = png_voidcast(png_bytep, + png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH)); + + if (num_trans > 0 && num_trans <= PNG_MAX_PALETTE_LENGTH) + memcpy(info_ptr->trans_alpha, trans_alpha, (png_size_t)num_trans); } - if (trans_values != NULL) + if (trans_color != NULL) { - png_memcpy(&(info_ptr->trans_values), trans_values, - png_sizeof(png_color_16)); + int sample_max = (1 << info_ptr->bit_depth); + + if ((info_ptr->color_type == PNG_COLOR_TYPE_GRAY && + trans_color->gray > sample_max) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB && + (trans_color->red > sample_max || + trans_color->green > sample_max || + trans_color->blue > sample_max))) + png_warning(png_ptr, + "tRNS chunk has out-of-range samples for bit_depth"); + + info_ptr->trans_color = *trans_color; + if (num_trans == 0) - num_trans = 1; + num_trans = 1; } + info_ptr->num_trans = (png_uint_16)num_trans; - info_ptr->valid |= PNG_INFO_tRNS; + + if (num_trans != 0) + { + info_ptr->valid |= PNG_INFO_tRNS; + info_ptr->free_me |= PNG_FREE_TRNS; + } } #endif -#if defined(PNG_sPLT_SUPPORTED) +#ifdef PNG_sPLT_SUPPORTED void PNGAPI -png_set_sPLT(png_structp png_ptr, - png_infop info_ptr, png_sPLT_tp entries, int nentries) +png_set_sPLT(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_sPLT_tp entries, int nentries) +/* + * entries - array of png_sPLT_t structures + * to be added to the list of palettes + * in the info structure. + * + * nentries - number of palette structures to be + * added. + */ { - png_sPLT_tp np; - int i; + png_sPLT_tp np; - if (png_ptr == NULL || info_ptr == NULL) - return; - - np = (png_sPLT_tp)png_malloc_warn(png_ptr, - (info_ptr->splt_palettes_num + nentries) * png_sizeof(png_sPLT_t)); - if (np == NULL) - { - png_warning(png_ptr, "No memory for sPLT palettes."); + if (png_ptr == NULL || info_ptr == NULL || nentries <= 0 || entries == NULL) return; - } - png_memcpy(np, info_ptr->splt_palettes, - info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); - png_free(png_ptr, info_ptr->splt_palettes); - info_ptr->splt_palettes=NULL; + /* Use the internal realloc function, which checks for all the possible + * overflows. Notice that the parameters are (int) and (size_t) + */ + np = png_voidcast(png_sPLT_tp,png_realloc_array(png_ptr, + info_ptr->splt_palettes, info_ptr->splt_palettes_num, nentries, + sizeof *np)); - for (i = 0; i < nentries; i++) - { - png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; - png_sPLT_tp from = entries + i; + if (np == NULL) + { + /* Out of memory or too many chunks */ + png_chunk_report(png_ptr, "too many sPLT chunks", PNG_CHUNK_WRITE_ERROR); + return; + } - to->name = (png_charp)png_malloc_warn(png_ptr, - png_strlen(from->name) + 1); - if (to->name == NULL) - { - png_warning(png_ptr, - "Out of memory while processing sPLT chunk"); - } - /* TODO: use png_malloc_warn */ - png_strncpy(to->name, from->name, png_strlen(from->name)+1); - to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, - from->nentries * png_sizeof(png_sPLT_entry)); - /* TODO: use png_malloc_warn */ - png_memcpy(to->entries, from->entries, - from->nentries * png_sizeof(png_sPLT_entry)); - if (to->entries == NULL) - { - png_warning(png_ptr, - "Out of memory while processing sPLT chunk"); - png_free(png_ptr,to->name); - to->name = NULL; - } - to->nentries = from->nentries; - to->depth = from->depth; - } + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = np; + info_ptr->free_me |= PNG_FREE_SPLT; - info_ptr->splt_palettes = np; - info_ptr->splt_palettes_num += nentries; - info_ptr->valid |= PNG_INFO_sPLT; -#ifdef PNG_FREE_ME_SUPPORTED - info_ptr->free_me |= PNG_FREE_SPLT; -#endif + np += info_ptr->splt_palettes_num; + + do + { + png_size_t length; + + /* Skip invalid input entries */ + if (entries->name == NULL || entries->entries == NULL) + { + /* png_handle_sPLT doesn't do this, so this is an app error */ + png_app_error(png_ptr, "png_set_sPLT: invalid sPLT"); + /* Just skip the invalid entry */ + continue; + } + + np->depth = entries->depth; + + /* In the even of out-of-memory just return - there's no point keeping on + * trying to add sPLT chunks. + */ + length = strlen(entries->name) + 1; + np->name = png_voidcast(png_charp, png_malloc_base(png_ptr, length)); + + if (np->name == NULL) + break; + + memcpy(np->name, entries->name, length); + + /* IMPORTANT: we have memory now that won't get freed if something else + * goes wrong, this code must free it. png_malloc_array produces no + * warnings, use a png_chunk_report (below) if there is an error. + */ + np->entries = png_voidcast(png_sPLT_entryp, png_malloc_array(png_ptr, + entries->nentries, sizeof (png_sPLT_entry))); + + if (np->entries == NULL) + { + png_free(png_ptr, np->name); + break; + } + + np->nentries = entries->nentries; + /* This multiply can't overflow because png_malloc_array has already + * checked it when doing the allocation. + */ + memcpy(np->entries, entries->entries, + entries->nentries * sizeof (png_sPLT_entry)); + + /* Note that 'continue' skips the advance of the out pointer and out + * count, so an invalid entry is not added. + */ + info_ptr->valid |= PNG_INFO_sPLT; + ++(info_ptr->splt_palettes_num); + ++np; + } + while (++entries, --nentries); + + if (nentries > 0) + png_chunk_report(png_ptr, "sPLT out of memory", PNG_CHUNK_WRITE_ERROR); } #endif /* PNG_sPLT_SUPPORTED */ -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) -void PNGAPI -png_set_unknown_chunks(png_structp png_ptr, - png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +#ifdef PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED +static png_byte +check_location(png_const_structrp png_ptr, int location) { - png_unknown_chunkp np; - int i; + location &= (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT); - if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) - return; + /* New in 1.6.0; copy the location and check it. This is an API + * change, previously the app had to use the + * png_set_unknown_chunk_location API below for each chunk. + */ + if (location == 0 && !(png_ptr->mode & PNG_IS_READ_STRUCT)) + { + /* Write struct, so unknown chunks come from the app */ + png_app_warning(png_ptr, + "png_set_unknown_chunks now expects a valid location"); + /* Use the old behavior */ + location = (png_byte)(png_ptr->mode & + (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)); + } - np = (png_unknown_chunkp)png_malloc_warn(png_ptr, - (info_ptr->unknown_chunks_num + num_unknowns) * - png_sizeof(png_unknown_chunk)); - if (np == NULL) - { - png_warning(png_ptr, - "Out of memory while processing unknown chunk."); - return; - } + /* This need not be an internal error - if the app calls + * png_set_unknown_chunks on a read pointer it must get the location right. + */ + if (location == 0) + png_error(png_ptr, "invalid location in png_set_unknown_chunks"); - png_memcpy(np, info_ptr->unknown_chunks, - info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); - png_free(png_ptr, info_ptr->unknown_chunks); - info_ptr->unknown_chunks=NULL; + /* Now reduce the location to the top-most set bit by removing each least + * significant bit in turn. + */ + while (location != (location & -location)) + location &= ~(location & -location); - for (i = 0; i < num_unknowns; i++) - { - png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; - png_unknown_chunkp from = unknowns + i; - - png_strncpy((png_charp)to->name, (png_charp)from->name, 5); - to->data = (png_bytep)png_malloc_warn(png_ptr, from->size); - if (to->data == NULL) - { - png_warning(png_ptr, - "Out of memory while processing unknown chunk."); - } - else - { - png_memcpy(to->data, from->data, from->size); - to->size = from->size; - - /* note our location in the read or write sequence */ - to->location = (png_byte)(png_ptr->mode & 0xff); - } - } - - info_ptr->unknown_chunks = np; - info_ptr->unknown_chunks_num += num_unknowns; -#ifdef PNG_FREE_ME_SUPPORTED - info_ptr->free_me |= PNG_FREE_UNKN; -#endif + /* The cast is safe because 'location' is a bit mask and only the low four + * bits are significant. + */ + return (png_byte)location; } -void PNGAPI -png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, - int chunk, int location) -{ - if(png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < - (int)info_ptr->unknown_chunks_num) - info_ptr->unknown_chunks[chunk].location = (png_byte)location; -} -#endif -#if defined(PNG_1_0_X) || defined(PNG_1_2_X) -#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ - defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) void PNGAPI -png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +png_set_unknown_chunks(png_const_structrp png_ptr, + png_inforp info_ptr, png_const_unknown_chunkp unknowns, int num_unknowns) { - /* 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) + png_unknown_chunkp np; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns <= 0 || + unknowns == NULL) return; - png_ptr->mng_features_permitted = (png_byte) - ((png_ptr->mng_features_permitted & (~(PNG_FLAG_MNG_EMPTY_PLTE))) | - ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); + + /* Check for the failure cases where support has been disabled at compile + * time. This code is hardly ever compiled - it's here because + * STORE_UNKNOWN_CHUNKS is set by both read and write code (compiling in this + * code) but may be meaningless if the read or write handling of unknown + * chunks is not compiled in. + */ +# if !defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) && \ + defined(PNG_READ_SUPPORTED) + if (png_ptr->mode & PNG_IS_READ_STRUCT) + { + png_app_error(png_ptr, "no unknown chunk support on read"); + return; + } +# endif +# if !defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) && \ + defined(PNG_WRITE_SUPPORTED) + if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) + { + png_app_error(png_ptr, "no unknown chunk support on write"); + return; + } +# endif + + /* Prior to 1.6.0 this code used png_malloc_warn; however, this meant that + * unknown critical chunks could be lost with just a warning resulting in + * undefined behavior. Now png_chunk_report is used to provide behavior + * appropriate to read or write. + */ + np = png_voidcast(png_unknown_chunkp, png_realloc_array(png_ptr, + info_ptr->unknown_chunks, info_ptr->unknown_chunks_num, num_unknowns, + sizeof *np)); + + if (np == NULL) + { + png_chunk_report(png_ptr, "too many unknown chunks", + PNG_CHUNK_WRITE_ERROR); + return; + } + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = np; /* safe because it is initialized */ + info_ptr->free_me |= PNG_FREE_UNKN; + + np += info_ptr->unknown_chunks_num; + + /* Increment unknown_chunks_num each time round the loop to protect the + * just-allocated chunk data. + */ + for (; num_unknowns > 0; --num_unknowns, ++unknowns) + { + memcpy(np->name, unknowns->name, (sizeof np->name)); + np->name[(sizeof np->name)-1] = '\0'; + np->location = check_location(png_ptr, unknowns->location); + + if (unknowns->size == 0) + { + np->data = NULL; + np->size = 0; + } + + else + { + np->data = png_voidcast(png_bytep, + png_malloc_base(png_ptr, unknowns->size)); + + if (np->data == NULL) + { + png_chunk_report(png_ptr, "unknown chunk: out of memory", + PNG_CHUNK_WRITE_ERROR); + /* But just skip storing the unknown chunk */ + continue; + } + + memcpy(np->data, unknowns->data, unknowns->size); + np->size = unknowns->size; + } + + /* These increments are skipped on out-of-memory for the data - the + * unknown chunk entry gets overwritten if the png_chunk_report returns. + * This is correct in the read case (the chunk is just dropped.) + */ + ++np; + ++(info_ptr->unknown_chunks_num); + } +} + +void PNGAPI +png_set_unknown_chunk_location(png_const_structrp png_ptr, png_inforp info_ptr, + int chunk, int location) +{ + /* This API is pretty pointless in 1.6.0 because the location can be set + * before the call to png_set_unknown_chunks. + * + * TODO: add a png_app_warning in 1.7 + */ + if (png_ptr != NULL && info_ptr != NULL && chunk >= 0 && + chunk < info_ptr->unknown_chunks_num) + { + if ((location & (PNG_HAVE_IHDR|PNG_HAVE_PLTE|PNG_AFTER_IDAT)) == 0) + { + png_app_error(png_ptr, "invalid unknown chunk location"); + /* Fake out the pre 1.6.0 behavior: */ + if ((location & PNG_HAVE_IDAT)) /* undocumented! */ + location = PNG_AFTER_IDAT; + + else + location = PNG_HAVE_IHDR; /* also undocumented */ + } + + info_ptr->unknown_chunks[chunk].location = + check_location(png_ptr, location); + } } #endif -#endif -#if defined(PNG_MNG_FEATURES_SUPPORTED) + +#ifdef PNG_MNG_FEATURES_SUPPORTED png_uint_32 PNGAPI -png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +png_permit_mng_features (png_structrp png_ptr, png_uint_32 mng_features) { - png_debug(1, "in png_permit_mng_features\n"); + png_debug(1, "in png_permit_mng_features"); + if (png_ptr == NULL) - return (png_uint_32)0; - png_ptr->mng_features_permitted = - (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); - return (png_uint_32)png_ptr->mng_features_permitted; + return 0; + + png_ptr->mng_features_permitted = mng_features & PNG_ALL_MNG_FEATURES; + + return png_ptr->mng_features_permitted; } #endif -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) -void PNGAPI -png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep - chunk_list, int num_chunks) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +static unsigned int +add_one_chunk(png_bytep list, unsigned int count, png_const_bytep add, int keep) { - png_bytep new_list, p; - int i, old_num_chunks; - if (png_ptr == NULL) - return; - if (num_chunks == 0) - { - if(keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) - png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; - else - png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + unsigned int i; - if(keep == PNG_HANDLE_CHUNK_ALWAYS) - png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; - else - png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + /* Utility function: update the 'keep' state of a chunk if it is already in + * the list, otherwise add it to the list. + */ + for (i=0; i= PNG_HANDLE_CHUNK_LAST) + { + png_app_error(png_ptr, "png_set_keep_unknown_chunks: invalid keep"); return; - old_num_chunks=png_ptr->num_chunk_list; - new_list=(png_bytep)png_malloc(png_ptr, - (png_uint_32)(5*(num_chunks+old_num_chunks))); - if(png_ptr->chunk_list != NULL) - { - png_memcpy(new_list, png_ptr->chunk_list, - (png_size_t)(5*old_num_chunks)); - png_free(png_ptr, png_ptr->chunk_list); - png_ptr->chunk_list=NULL; - } - png_memcpy(new_list+5*old_num_chunks, chunk_list, - (png_size_t)(5*num_chunks)); - for (p=new_list+5*old_num_chunks+4, i=0; inum_chunk_list=old_num_chunks+num_chunks; - png_ptr->chunk_list=new_list; -#ifdef PNG_FREE_ME_SUPPORTED - png_ptr->free_me |= PNG_FREE_LIST; -#endif + } + + if (num_chunks_in <= 0) + { + png_ptr->unknown_default = keep; + + /* '0' means just set the flags, so stop here */ + if (num_chunks_in == 0) + return; + } + + if (num_chunks_in < 0) + { + /* Ignore all unknown chunks and all chunks recognized by + * libpng except for IHDR, PLTE, tRNS, IDAT, and IEND + */ + static PNG_CONST png_byte chunks_to_ignore[] = { + 98, 75, 71, 68, '\0', /* bKGD */ + 99, 72, 82, 77, '\0', /* cHRM */ + 103, 65, 77, 65, '\0', /* gAMA */ + 104, 73, 83, 84, '\0', /* hIST */ + 105, 67, 67, 80, '\0', /* iCCP */ + 105, 84, 88, 116, '\0', /* iTXt */ + 111, 70, 70, 115, '\0', /* oFFs */ + 112, 67, 65, 76, '\0', /* pCAL */ + 112, 72, 89, 115, '\0', /* pHYs */ + 115, 66, 73, 84, '\0', /* sBIT */ + 115, 67, 65, 76, '\0', /* sCAL */ + 115, 80, 76, 84, '\0', /* sPLT */ + 115, 84, 69, 82, '\0', /* sTER */ + 115, 82, 71, 66, '\0', /* sRGB */ + 116, 69, 88, 116, '\0', /* tEXt */ + 116, 73, 77, 69, '\0', /* tIME */ + 122, 84, 88, 116, '\0' /* zTXt */ + }; + + chunk_list = chunks_to_ignore; + num_chunks = (sizeof chunks_to_ignore)/5; + } + + else /* num_chunks_in > 0 */ + { + if (chunk_list == NULL) + { + /* Prior to 1.6.0 this was silently ignored, now it is an app_error + * which can be switched off. + */ + png_app_error(png_ptr, "png_set_keep_unknown_chunks: no chunk list"); + return; + } + + num_chunks = num_chunks_in; + } + + old_num_chunks = png_ptr->num_chunk_list; + if (png_ptr->chunk_list == NULL) + old_num_chunks = 0; + + /* Since num_chunks is always restricted to UINT_MAX/5 this can't overflow. + */ + if (num_chunks + old_num_chunks > UINT_MAX/5) + { + png_app_error(png_ptr, "png_set_keep_unknown_chunks: too many chunks"); + return; + } + + /* If these chunks are being reset to the default then no more memory is + * required because add_one_chunk above doesn't extend the list if the 'keep' + * parameter is the default. + */ + if (keep) + { + new_list = png_voidcast(png_bytep, png_malloc(png_ptr, + 5 * (num_chunks + old_num_chunks))); + + if (old_num_chunks > 0) + memcpy(new_list, png_ptr->chunk_list, 5*old_num_chunks); + } + + else if (old_num_chunks > 0) + new_list = png_ptr->chunk_list; + + else + new_list = NULL; + + /* Add the new chunks together with each one's handling code. If the chunk + * already exists the code is updated, otherwise the chunk is added to the + * end. (In libpng 1.6.0 order no longer matters because this code enforces + * the earlier convention that the last setting is the one that is used.) + */ + if (new_list != NULL) + { + png_const_bytep inlist; + png_bytep outlist; + unsigned int i; + + for (i=0; ichunk_list != new_list) + png_free(png_ptr, new_list); + + new_list = NULL; + } + } + + else + num_chunks = 0; + + png_ptr->num_chunk_list = num_chunks; + + if (png_ptr->chunk_list != new_list) + { + if (png_ptr->chunk_list != NULL) + png_free(png_ptr, png_ptr->chunk_list); + + png_ptr->chunk_list = new_list; + } } #endif -#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED void PNGAPI -png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, - png_user_chunk_ptr read_user_chunk_fn) +png_set_read_user_chunk_fn(png_structrp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) { - png_debug(1, "in png_set_read_user_chunk_fn\n"); + png_debug(1, "in png_set_read_user_chunk_fn"); + if (png_ptr == NULL) return; + png_ptr->read_user_chunk_fn = read_user_chunk_fn; png_ptr->user_chunk_ptr = user_chunk_ptr; } #endif -#if defined(PNG_INFO_IMAGE_SUPPORTED) +#ifdef PNG_INFO_IMAGE_SUPPORTED void PNGAPI -png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +png_set_rows(png_const_structrp png_ptr, png_inforp info_ptr, + png_bytepp row_pointers) { - png_debug1(1, "in %s storage function\n", "rows"); + png_debug1(1, "in %s storage function", "rows"); if (png_ptr == NULL || info_ptr == NULL) return; - if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + if (info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; - if(row_pointers) + + if (row_pointers) info_ptr->valid |= PNG_INFO_IDAT; } #endif -#ifdef PNG_WRITE_SUPPORTED void PNGAPI -png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size) +png_set_compression_buffer_size(png_structrp png_ptr, png_size_t size) { if (png_ptr == NULL) return; - if(png_ptr->zbuf) - png_free(png_ptr, png_ptr->zbuf); - png_ptr->zbuf_size = (png_size_t)size; - png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + if (size == 0 || size > PNG_UINT_31_MAX) + png_error(png_ptr, "invalid compression buffer size"); + +# ifdef PNG_SEQUENTIAL_READ_SUPPORTED + if (png_ptr->mode & PNG_IS_READ_STRUCT) + { + png_ptr->IDAT_read_size = (png_uint_32)size; /* checked above */ + return; + } +# endif + +# ifdef PNG_WRITE_SUPPORTED + if (!(png_ptr->mode & PNG_IS_READ_STRUCT)) + { + if (png_ptr->zowner != 0) + { + png_warning(png_ptr, + "Compression buffer size cannot be changed because it is in use"); + return; + } + + if (size > ZLIB_IO_MAX) + { + png_warning(png_ptr, + "Compression buffer size limited to system maximum"); + size = ZLIB_IO_MAX; /* must fit */ + } + + else if (size < 6) + { + /* Deflate will potentially go into an infinite loop on a SYNC_FLUSH + * if this is permitted. + */ + png_warning(png_ptr, + "Compression buffer size cannot be reduced below 6"); + return; + } + + if (png_ptr->zbuffer_size != size) + { + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); + png_ptr->zbuffer_size = (uInt)size; + } + } +# endif } -#endif void PNGAPI -png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +png_set_invalid(png_const_structrp png_ptr, png_inforp info_ptr, int mask) { if (png_ptr && info_ptr) - info_ptr->valid &= ~(mask); + info_ptr->valid &= ~mask; } -#ifndef PNG_1_0_X -#ifdef PNG_ASSEMBLER_CODE_SUPPORTED -/* function was added to libpng 1.2.0 and should always exist by default */ -void PNGAPI -png_set_asm_flags (png_structp png_ptr, png_uint_32) -{ -/* 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, - png_uint_32) -{ -/* 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 */ +/* 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_set_user_limits (png_structrp 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; + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if (png_ptr == NULL) + return; + + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} + +/* This function was added to libpng 1.4.0 */ +void PNGAPI +png_set_chunk_cache_max (png_structrp png_ptr, png_uint_32 user_chunk_cache_max) +{ + if (png_ptr) + png_ptr->user_chunk_cache_max = user_chunk_cache_max; +} + +/* This function was added to libpng 1.4.1 */ +void PNGAPI +png_set_chunk_malloc_max (png_structrp png_ptr, + png_alloc_size_t user_chunk_malloc_max) +{ + if (png_ptr) + png_ptr->user_chunk_malloc_max = user_chunk_malloc_max; } #endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ -#endif /* ?PNG_1_0_X */ + +#ifdef PNG_BENIGN_ERRORS_SUPPORTED +void PNGAPI +png_set_benign_errors(png_structrp png_ptr, int allowed) +{ + png_debug(1, "in png_set_benign_errors"); + + /* If allowed is 1, png_benign_error() is treated as a warning. + * + * If allowed is 0, png_benign_error() is treated as an error (which + * is the default behavior if png_set_benign_errors() is not called). + */ + + if (allowed) + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN | + PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN; + + else + png_ptr->flags &= ~(PNG_FLAG_BENIGN_ERRORS_WARN | + PNG_FLAG_APP_WARNINGS_WARN | PNG_FLAG_APP_ERRORS_WARN); +} +#endif /* PNG_BENIGN_ERRORS_SUPPORTED */ + +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Whether to report invalid palette index; added at libng-1.5.10. + * It is possible for an indexed (color-type==3) PNG file to contain + * pixels with invalid (out-of-range) indexes if the PLTE chunk has + * fewer entries than the image's bit-depth would allow. We recover + * from this gracefully by filling any incomplete palette with zeroes + * (opaque black). By default, when this occurs libpng will issue + * a benign error. This API can be used to override that behavior. + */ +void PNGAPI +png_set_check_for_invalid_index(png_structrp png_ptr, int allowed) +{ + png_debug(1, "in png_set_check_for_invalid_index"); + + if (allowed > 0) + png_ptr->num_palette_max = 0; + + else + png_ptr->num_palette_max = -1; +} +#endif #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngstruct.h b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngstruct.h new file mode 100644 index 0000000..89605b1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngstruct.h @@ -0,0 +1,489 @@ + +/* pngstruct.h - header file for PNG reference library + * + * Copyright (c) 1998-2013 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.) + * + * Last changed in libpng 1.6.1 [March 28, 2013] + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* 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. + */ + +#ifndef PNGSTRUCT_H +#define PNGSTRUCT_H +/* zlib.h defines the structure z_stream, an instance of which is included + * in this structure and is required for decompressing the LZ compressed + * data in PNG files. + */ +#ifndef ZLIB_CONST + /* We must ensure that zlib uses 'const' in declarations. */ +# define ZLIB_CONST +#endif +#include "../../../juce_core/zip/zlib/zlib.h" +#ifdef const + /* zlib.h sometimes #defines const to nothing, undo this. */ +# undef const +#endif + +/* zlib.h has mediocre z_const use before 1.2.6, this stuff is for compatibility + * with older builds. + */ +#if ZLIB_VERNUM < 0x1260 +# define PNGZ_MSG_CAST(s) png_constcast(char*,s) +# define PNGZ_INPUT_CAST(b) png_constcast(png_bytep,b) +#else +# define PNGZ_MSG_CAST(s) (s) +# define PNGZ_INPUT_CAST(b) (b) +#endif + +/* zlib.h declares a magic type 'uInt' that limits the amount of data that zlib + * can handle at once. This type need be no larger than 16 bits (so maximum of + * 65535), this define allows us to discover how big it is, but limited by the + * maximuum for png_size_t. The value can be overriden in a library build + * (pngusr.h, or set it in CPPFLAGS) and it works to set it to a considerably + * lower value (e.g. 255 works). A lower value may help memory usage (slightly) + * and may even improve performance on some systems (and degrade it on others.) + */ +#ifndef ZLIB_IO_MAX +# define ZLIB_IO_MAX ((uInt)-1) +#endif + +#ifdef PNG_WRITE_SUPPORTED +/* The type of a compression buffer list used by the write code. */ +typedef struct png_compression_buffer +{ + struct png_compression_buffer *next; + png_byte output[1]; /* actually zbuf_size */ +} png_compression_buffer, *png_compression_bufferp; + +#define PNG_COMPRESSION_BUFFER_SIZE(pp)\ + (offsetof(png_compression_buffer, output) + (pp)->zbuffer_size) +#endif + +/* Colorspace support; structures used in png_struct, png_info and in internal + * functions to hold and communicate information about the color space. + * + * PNG_COLORSPACE_SUPPORTED is only required if the application will perform + * colorspace corrections, otherwise all the colorspace information can be + * skipped and the size of libpng can be reduced (significantly) by compiling + * out the colorspace support. + */ +#ifdef PNG_COLORSPACE_SUPPORTED +/* The chromaticities of the red, green and blue colorants and the chromaticity + * of the corresponding white point (i.e. of rgb(1.0,1.0,1.0)). + */ +typedef struct png_xy +{ + png_fixed_point redx, redy; + png_fixed_point greenx, greeny; + png_fixed_point bluex, bluey; + png_fixed_point whitex, whitey; +} png_xy; + +/* The same data as above but encoded as CIE XYZ values. When this data comes + * from chromaticities the sum of the Y values is assumed to be 1.0 + */ +typedef struct png_XYZ +{ + png_fixed_point red_X, red_Y, red_Z; + png_fixed_point green_X, green_Y, green_Z; + png_fixed_point blue_X, blue_Y, blue_Z; +} png_XYZ; +#endif /* COLORSPACE */ + +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) +/* A colorspace is all the above plus, potentially, profile information, + * however at present libpng does not use the profile internally so it is only + * stored in the png_info struct (if iCCP is supported.) The rendering intent + * is retained here and is checked. + * + * The file gamma encoding information is also stored here and gamma correction + * is done by libpng, whereas color correction must currently be done by the + * application. + */ +typedef struct png_colorspace +{ +#ifdef PNG_GAMMA_SUPPORTED + png_fixed_point gamma; /* File gamma */ +#endif + +#ifdef PNG_COLORSPACE_SUPPORTED + png_xy end_points_xy; /* End points as chromaticities */ + png_XYZ end_points_XYZ; /* End points as CIE XYZ colorant values */ + png_uint_16 rendering_intent; /* Rendering intent of a profile */ +#endif + + /* Flags are always defined to simplify the code. */ + png_uint_16 flags; /* As defined below */ +} png_colorspace, * PNG_RESTRICT png_colorspacerp; + +typedef const png_colorspace * PNG_RESTRICT png_const_colorspacerp; + +/* General flags for the 'flags' field */ +#define PNG_COLORSPACE_HAVE_GAMMA 0x0001 +#define PNG_COLORSPACE_HAVE_ENDPOINTS 0x0002 +#define PNG_COLORSPACE_HAVE_INTENT 0x0004 +#define PNG_COLORSPACE_FROM_gAMA 0x0008 +#define PNG_COLORSPACE_FROM_cHRM 0x0010 +#define PNG_COLORSPACE_FROM_sRGB 0x0020 +#define PNG_COLORSPACE_ENDPOINTS_MATCH_sRGB 0x0040 +#define PNG_COLORSPACE_MATCHES_sRGB 0x0080 /* exact match on profile */ +#define PNG_COLORSPACE_INVALID 0x8000 +#define PNG_COLORSPACE_CANCEL(flags) (0xffff ^ (flags)) +#endif /* COLORSPACE || GAMMA */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmp_buf_local; /* New name in 1.6.0 for jmp_buf in png_struct */ + png_longjmp_ptr longjmp_fn;/* setjmp non-local goto function. */ + jmp_buf *jmp_buf_ptr; /* passed to longjmp_fn */ + size_t jmp_buf_size; /* size of the above, if allocated */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ +#ifdef PNG_WARNINGS_SUPPORTED + png_error_ptr warning_fn; /* function for printing warnings */ +#endif + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#ifdef PNG_READ_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + png_uint_32 zowner; /* ID (chunk type) of zstream owner, 0 if none */ + z_stream zstream; /* decompression structure */ + +#ifdef PNG_WRITE_SUPPORTED + png_compression_bufferp zbuffer_list; /* Created on demand during write */ + uInt zbuffer_size; /* size of the actual buffer */ + + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ +#endif +/* Added at libpng 1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + int zlib_text_level; /* holds zlib compression level */ + int zlib_text_method; /* holds zlib compression method */ + int zlib_text_window_bits; /* holds zlib compression window bits */ + int zlib_text_mem_level; /* holds zlib compression memory level */ + int zlib_text_strategy; /* holds zlib compression strategy */ +#endif +/* End of material added at libpng 1.5.4 */ +/* Added at libpng 1.6.0 */ +#ifdef PNG_WRITE_SUPPORTED + int zlib_set_level; /* Actual values set into the zstream on write */ + int zlib_set_method; + int zlib_set_window_bits; + int zlib_set_mem_level; + int zlib_set_strategy; +#endif + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_size_t rowbytes; /* size of row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_uint_32 chunk_name; /* PNG_CHUNK() id of current chunk */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row. + * This is a pointer into big_prev_row + */ + png_bytep row_buf; /* buffer to save current (unfiltered) row. + * This is a pointer into big_row_buf + */ +#ifdef PNG_WRITE_SUPPORTED + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ +#endif + png_size_t info_rowbytes; /* Added in 1.5.4: cache of updated row bytes */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + +/* Added at libpng-1.5.10 */ +#ifdef PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED + int num_palette_max; /* maximum palette index found in IDAT */ +#endif + + png_uint_16 num_trans; /* number of transparency values */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row: write only */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ +#ifdef PNG_WRITE_SUPPORTED + png_byte usr_channels; /* channels at start of write: write only */ +#endif + png_byte sig_bytes; /* magic bytes read/written from start of file */ + png_byte maximum_pixel_depth; + /* pixel depth used for the row buffers */ + png_byte transformed_pixel_depth; + /* pixel depth after read/write transforms */ +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif + +#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) ||\ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) + png_byte background_gamma_type; + png_fixed_point background_gamma; + png_color_16 background; /* background color in screen gamma space */ +#ifdef PNG_READ_GAMMA_SUPPORTED + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#ifdef PNG_WRITE_FLUSH_SUPPORTED + png_flush_ptr output_flush_fn; /* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#ifdef PNG_READ_GAMMA_SUPPORTED + int gamma_shift; /* number of "insignificant" bits in 16-bit gamma */ + png_fixed_point screen_gamma; /* screen gamma value (display_exponent) */ + + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans_alpha; /* alpha values for paletted files */ + png_color_16 trans_color; /* transparent color for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after a prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* For the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#ifdef PNG_READ_QUANTIZE_SUPPORTED + png_bytep palette_lookup; /* lookup table for quantizing */ + png_bytep quantize_index; /* index translation for palette files */ +#endif + +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + + /* Options */ +#ifdef PNG_SET_OPTION_SUPPORTED + png_byte options; /* On/off state (up to 4 options) */ +#endif + +#if PNG_LIBPNG_VER < 10700 +/* To do: remove this from libpng-1.7 */ +#ifdef PNG_TIME_RFC1123_SUPPORTED + char time_buffer[29]; /* String to hold RFC 1123 time text */ +#endif +#endif + +/* New members added in libpng-1.0.6 */ + + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ + +#ifdef PNG_USER_CHUNKS_SUPPORTED + png_voidp user_chunk_ptr; +#ifdef PNG_READ_USER_CHUNKS_SUPPORTED + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif +#endif + +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + int unknown_default; /* As PNG_HANDLE_* */ + unsigned int num_chunk_list; /* Number of entries in the list */ + png_bytep chunk_list; /* List of png_byte[5]; the textual chunk name + * followed by a PNG_HANDLE_* byte */ +#endif + +/* New members added in libpng-1.0.3 */ +#ifdef PNG_READ_RGB_TO_GRAY_SUPPORTED + png_byte rgb_to_gray_status; + /* Added in libpng 1.5.5 to record setting of coefficients: */ + png_byte rgb_to_gray_coefficients_set; + /* 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; + /* deleted in 1.5.5: 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) +/* Changed from png_byte to png_uint_32 at version 1.2.0 */ + png_uint_32 mng_features_permitted; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#ifdef PNG_MNG_FEATURES_SUPPORTED + png_byte filter_type; +#endif + +/* New members added in libpng-1.2.0 */ + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* 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 */ + +#ifdef PNG_READ_QUANTIZE_SUPPORTED +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep quantize_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_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; + + /* Added in libpng-1.4.0: Total number of sPLT, text, and unknown + * chunks that can be stored (0 means unlimited). + */ + png_uint_32 user_chunk_cache_max; + + /* Total memory that a zTXt, sPLT, iTXt, iCCP, or unknown chunk + * can occupy when decompressed. 0 means unlimited. + */ + png_alloc_size_t user_chunk_malloc_max; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#ifdef PNG_READ_UNKNOWN_CHUNKS_SUPPORTED + /* Temporary storage for unknown chunk that the library doesn't recognize, + * used while reading the chunk. + */ + png_unknown_chunk unknown_chunk; +#endif + +/* New member added in libpng-1.2.26 */ + png_size_t old_big_row_buf_size; + +#ifdef PNG_READ_SUPPORTED +/* New member added in libpng-1.2.30 */ + png_bytep read_buffer; /* buffer for reading chunk data */ + png_alloc_size_t read_buffer_size; /* current size of the buffer */ +#endif +#ifdef PNG_SEQUENTIAL_READ_SUPPORTED + uInt IDAT_read_size; /* limit on read buffer size for IDAT */ +#endif + +#ifdef PNG_IO_STATE_SUPPORTED +/* New member added in libpng-1.4.0 */ + png_uint_32 io_state; +#endif + +/* New member added in libpng-1.5.6 */ + png_bytep big_prev_row; + +/* New member added in libpng-1.5.7 */ + void (*read_filter[PNG_FILTER_VALUE_LAST-1])(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row); + +#ifdef PNG_READ_SUPPORTED +#if defined(PNG_COLORSPACE_SUPPORTED) || defined(PNG_GAMMA_SUPPORTED) + png_colorspace colorspace; +#endif +#endif +}; +#endif /* PNGSTRUCT_H */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngtrans.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngtrans.c index 8c8faa1..20558af 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngtrans.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngtrans.c @@ -1,47 +1,59 @@ /* 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 + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" #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 */ +/* Turn on BGR-to-RGB mapping */ void PNGAPI -png_set_bgr(png_structp png_ptr) +png_set_bgr(png_structrp png_ptr) { - png_debug(1, "in png_set_bgr\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_bgr"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_BGR; } #endif #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -/* turn on 16 bit byte swapping */ +/* Turn on 16 bit byte swapping */ void PNGAPI -png_set_swap(png_structp png_ptr) +png_set_swap(png_structrp png_ptr) { - png_debug(1, "in png_set_swap\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_swap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth == 16) png_ptr->transformations |= PNG_SWAP_BYTES; } #endif #if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) -/* turn on pixel packing */ +/* Turn on pixel packing */ void PNGAPI -png_set_packing(png_structp png_ptr) +png_set_packing(png_structrp png_ptr) { - png_debug(1, "in png_set_packing\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_packing"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) { png_ptr->transformations |= PNG_PACK; @@ -51,12 +63,15 @@ png_set_packing(png_structp png_ptr) #endif #if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) -/* turn on packed pixel swapping */ +/* Turn on packed pixel swapping */ void PNGAPI -png_set_packswap(png_structp png_ptr) +png_set_packswap(png_structrp png_ptr) { - png_debug(1, "in png_set_packswap\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_packswap"); + + if (png_ptr == NULL) + return; + if (png_ptr->bit_depth < 8) png_ptr->transformations |= PNG_PACKSWAP; } @@ -64,10 +79,13 @@ png_set_packswap(png_structp png_ptr) #if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) void PNGAPI -png_set_shift(png_structp png_ptr, png_color_8p true_bits) +png_set_shift(png_structrp png_ptr, png_const_color_8p true_bits) { - png_debug(1, "in png_set_shift\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_shift"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SHIFT; png_ptr->shift = *true_bits; } @@ -76,9 +94,10 @@ png_set_shift(png_structp png_ptr, png_color_8p true_bits) #if defined(PNG_READ_INTERLACING_SUPPORTED) || \ defined(PNG_WRITE_INTERLACING_SUPPORTED) int PNGAPI -png_set_interlace_handling(png_structp png_ptr) +png_set_interlace_handling(png_structrp png_ptr) { - png_debug(1, "in png_set_interlace handling\n"); + png_debug(1, "in png_set_interlace handling"); + if (png_ptr && png_ptr->interlaced) { png_ptr->transformations |= PNG_INTERLACE; @@ -96,57 +115,115 @@ png_set_interlace_handling(png_structp png_ptr) * that don't like bytes as parameters. */ void PNGAPI -png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +png_set_filler(png_structrp png_ptr, png_uint_32 filler, int filler_loc) { - png_debug(1, "in png_set_filler\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_filler"); + + if (png_ptr == NULL) + return; + + /* In libpng 1.6 it is possible to determine whether this is a read or write + * operation and therefore to do more checking here for a valid call. + */ + if (png_ptr->mode & PNG_IS_READ_STRUCT) + { +# ifdef PNG_READ_FILLER_SUPPORTED + /* On read png_set_filler is always valid, regardless of the base PNG + * format, because other transformations can give a format where the + * filler code can execute (basically an 8 or 16-bit component RGB or G + * format.) + * + * NOTE: usr_channels is not used by the read code! (This has led to + * confusion in the past.) The filler is only used in the read code. + */ + png_ptr->filler = (png_uint_16)filler; +# else + png_app_error(png_ptr, "png_set_filler not supported on read"); + PNG_UNUSED(filler) /* not used in the write case */ + return; +# endif + } + + else /* write */ + { +# ifdef PNG_WRITE_FILLER_SUPPORTED + /* On write the usr_channels parameter must be set correctly at the + * start to record the number of channels in the app-supplied data. + */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_RGB: + png_ptr->usr_channels = 4; + break; + + case PNG_COLOR_TYPE_GRAY: + if (png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + break; + } + + else + { + /* There simply isn't any code in libpng to strip out bits + * from bytes when the components are less than a byte in + * size! + */ + png_app_error(png_ptr, + "png_set_filler is invalid for low bit depth gray output"); + return; + } + + default: + png_app_error(png_ptr, + "png_set_filler: inappropriate color type"); + return; + } +# else + png_app_error(png_ptr, "png_set_filler not supported on write"); + return; +# endif + } + + /* Here on success - libpng supports the operation, set the transformation + * and the flag to say where the filler channel is. + */ png_ptr->transformations |= PNG_FILLER; - png_ptr->filler = (png_byte)filler; + if (filler_loc == PNG_FILLER_AFTER) png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; - - /* This should probably go in the "do_read_filler" routine. - * I attempted to do that in libpng-1.0.1a but that caused problems - * so I restored it in libpng-1.0.2a - */ - - if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) - { - png_ptr->usr_channels = 4; - } - - /* Also I added this in libpng-1.0.2a (what happens when we expand - * a less-than-8-bit grayscale to GA? */ - - if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) - { - png_ptr->usr_channels = 2; - } } -#if !defined(PNG_1_0_X) /* Added to libpng-1.2.7 */ void PNGAPI -png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +png_set_add_alpha(png_structrp png_ptr, png_uint_32 filler, int filler_loc) { - png_debug(1, "in png_set_add_alpha\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_add_alpha"); + + if (png_ptr == NULL) + return; + png_set_filler(png_ptr, filler, filler_loc); - png_ptr->transformations |= PNG_ADD_ALPHA; + /* The above may fail to do anything. */ + if (png_ptr->transformations & PNG_FILLER) + png_ptr->transformations |= PNG_ADD_ALPHA; } -#endif #endif #if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) void PNGAPI -png_set_swap_alpha(png_structp png_ptr) +png_set_swap_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_swap_alpha\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_swap_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_SWAP_ALPHA; } #endif @@ -154,40 +231,43 @@ png_set_swap_alpha(png_structp png_ptr) #if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) void PNGAPI -png_set_invert_alpha(png_structp png_ptr) +png_set_invert_alpha(png_structrp png_ptr) { - png_debug(1, "in png_set_invert_alpha\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_invert_alpha"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_ALPHA; } #endif #if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) void PNGAPI -png_set_invert_mono(png_structp png_ptr) +png_set_invert_mono(png_structrp png_ptr) { - png_debug(1, "in png_set_invert_mono\n"); - if(png_ptr == NULL) return; + png_debug(1, "in png_set_invert_mono"); + + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_INVERT_MONO; } -/* invert monochrome grayscale data */ +/* 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"); + png_debug(1, "in png_do_invert"); + /* This test removed from libpng version 1.0.13 and 1.2.0: * if (row_info->bit_depth == 1 && */ -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row == NULL || row_info == NULL) - return; -#endif if (row_info->color_type == PNG_COLOR_TYPE_GRAY) { png_bytep rp = row; - png_uint_32 i; - png_uint_32 istop = row_info->rowbytes; + png_size_t i; + png_size_t istop = row_info->rowbytes; for (i = 0; i < istop; i++) { @@ -195,47 +275,49 @@ png_do_invert(png_row_infop row_info, png_bytep row) rp++; } } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && row_info->bit_depth == 8) { png_bytep rp = row; - png_uint_32 i; - png_uint_32 istop = row_info->rowbytes; + png_size_t i; + png_size_t istop = row_info->rowbytes; - for (i = 0; i < istop; i+=2) + for (i = 0; i < istop; i += 2) { *rp = (png_byte)(~(*rp)); - rp+=2; + rp += 2; } } + +#ifdef PNG_16BIT_SUPPORTED else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && row_info->bit_depth == 16) { png_bytep rp = row; - png_uint_32 i; - png_uint_32 istop = row_info->rowbytes; + png_size_t i; + png_size_t istop = row_info->rowbytes; - for (i = 0; i < istop; i+=4) + for (i = 0; i < istop; i += 4) { *rp = (png_byte)(~(*rp)); - *(rp+1) = (png_byte)(~(*(rp+1))); - rp+=4; + *(rp + 1) = (png_byte)(~(*(rp + 1))); + rp += 4; } } +#endif } #endif +#ifdef PNG_16BIT_SUPPORTED #if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) -/* swaps byte order on 16 bit depth images */ +/* Swaps byte order on 16 bit depth images */ void /* PRIVATE */ png_do_swap(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_swap\n"); - if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - row_info->bit_depth == 16) + png_debug(1, "in png_do_swap"); + + if (row_info->bit_depth == 16) { png_bytep rp = row; png_uint_32 i; @@ -250,6 +332,7 @@ png_do_swap(png_row_infop row_info, png_bytep row) } } #endif +#endif #if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) static PNG_CONST png_byte onebppswaptable[256] = { @@ -357,27 +440,28 @@ static PNG_CONST png_byte fourbppswaptable[256] = { 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF }; -/* swaps pixel packing order within bytes */ +/* Swaps pixel packing order within bytes */ void /* PRIVATE */ png_do_packswap(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_packswap\n"); - if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - row_info->bit_depth < 8) + png_debug(1, "in png_do_packswap"); + + if (row_info->bit_depth < 8) { - png_bytep rp, end, table; + png_bytep rp; + png_const_bytep end, table; end = row + row_info->rowbytes; if (row_info->bit_depth == 1) - table = (png_bytep)onebppswaptable; + table = onebppswaptable; + else if (row_info->bit_depth == 2) - table = (png_bytep)twobppswaptable; + table = twobppswaptable; + else if (row_info->bit_depth == 4) - table = (png_bytep)fourbppswaptable; + table = fourbppswaptable; + else return; @@ -389,174 +473,130 @@ 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) */ +/* Remove a channel - this used to be 'png_do_strip_filler' but it used a + * somewhat weird combination of flags to determine what to do. All the calls + * to png_do_strip_filler are changed in 1.5.2 to call this instead with the + * correct arguments. + * + * The routine isn't general - the channel must be the channel at the start or + * end (not in the middle) of each pixel. + */ void /* PRIVATE */ -png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +png_do_strip_channel(png_row_infop row_info, png_bytep row, int at_start) { - png_debug(1, "in png_do_strip_filler\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL) -#endif + png_bytep sp = row; /* source pointer */ + png_bytep dp = row; /* destination pointer */ + png_bytep ep = row + row_info->rowbytes; /* One beyond end of row */ + + /* At the start sp will point to the first byte to copy and dp to where + * it is copied to. ep always points just beyond the end of the row, so + * the loop simply copies (channels-1) channels until sp reaches ep. + * + * at_start: 0 -- convert AG, XG, ARGB, XRGB, AAGG, XXGG, etc. + * nonzero -- convert GA, GX, RGBA, RGBX, GGAA, RRGGBBXX, etc. + */ + + /* GA, GX, XG cases */ + if (row_info->channels == 2) { - png_bytep sp=row; - png_bytep dp=row; - png_uint_32 row_width=row_info->width; - png_uint_32 i; - - if ((row_info->color_type == PNG_COLOR_TYPE_RGB || - (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && - (flags & PNG_FLAG_STRIP_ALPHA))) && - row_info->channels == 4) + if (row_info->bit_depth == 8) { - if (row_info->bit_depth == 8) - { - /* This converts from RGBX or RGBA to RGB */ - if (flags & PNG_FLAG_FILLER_AFTER) - { - dp+=3; sp+=4; - for (i = 1; i < row_width; i++) - { - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - sp++; - } - } - /* This converts from XRGB or ARGB to RGB */ - else - { - for (i = 0; i < row_width; i++) - { - sp++; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - } - } - row_info->pixel_depth = 24; - row_info->rowbytes = row_width * 3; - } - else /* if (row_info->bit_depth == 16) */ - { - if (flags & PNG_FLAG_FILLER_AFTER) - { - /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ - sp += 8; dp += 6; - for (i = 1; i < row_width; i++) - { - /* This could be (although png_memcpy is probably slower): - png_memcpy(dp, sp, 6); - sp += 8; - dp += 6; - */ + if (at_start) /* Skip initial filler */ + ++sp; + else /* Skip initial channel and, for sp, the filler */ + sp += 2, ++dp; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - sp += 2; - } - } - else - { - /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ - for (i = 0; i < row_width; i++) - { - /* This could be (although png_memcpy is probably slower): - png_memcpy(dp, sp, 6); - sp += 8; - dp += 6; - */ + /* For a 1 pixel wide image there is nothing to do */ + while (sp < ep) + *dp++ = *sp, sp += 2; - sp+=2; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - *dp++ = *sp++; - } - } - row_info->pixel_depth = 48; - row_info->rowbytes = row_width * 6; - } - row_info->channels = 3; + row_info->pixel_depth = 8; } - else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || - (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && - (flags & PNG_FLAG_STRIP_ALPHA))) && - row_info->channels == 2) + + else if (row_info->bit_depth == 16) { - if (row_info->bit_depth == 8) - { - /* This converts from GX or GA to G */ - if (flags & PNG_FLAG_FILLER_AFTER) - { - for (i = 0; i < row_width; i++) - { - *dp++ = *sp++; - sp++; - } - } - /* This converts from XG or AG to G */ - else - { - for (i = 0; i < row_width; i++) - { - sp++; - *dp++ = *sp++; - } - } - row_info->pixel_depth = 8; - row_info->rowbytes = row_width; - } - else /* if (row_info->bit_depth == 16) */ - { - if (flags & PNG_FLAG_FILLER_AFTER) - { - /* This converts from GGXX or GGAA to GG */ - sp += 4; dp += 2; - for (i = 1; i < row_width; i++) - { - *dp++ = *sp++; - *dp++ = *sp++; - sp += 2; - } - } - else - { - /* This converts from XXGG or AAGG to GG */ - for (i = 0; i < row_width; i++) - { - sp += 2; - *dp++ = *sp++; - *dp++ = *sp++; - } - } - row_info->pixel_depth = 16; - row_info->rowbytes = row_width * 2; - } - row_info->channels = 1; + if (at_start) /* Skip initial filler */ + sp += 2; + else /* Skip initial channel and, for sp, the filler */ + sp += 4, dp += 2; + + while (sp < ep) + *dp++ = *sp++, *dp++ = *sp, sp += 3; + + row_info->pixel_depth = 16; } - if (flags & PNG_FLAG_STRIP_ALPHA) - row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + + else + return; /* bad bit depth */ + + row_info->channels = 1; + + /* Finally fix the color type if it records an alpha channel */ + if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + row_info->color_type = PNG_COLOR_TYPE_GRAY; } + + /* RGBA, RGBX, XRGB cases */ + else if (row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + if (at_start) /* Skip initial filler */ + ++sp; + else /* Skip initial channels and, for sp, the filler */ + sp += 4, dp += 3; + + /* Note that the loop adds 3 to dp and 4 to sp each time. */ + while (sp < ep) + *dp++ = *sp++, *dp++ = *sp++, *dp++ = *sp, sp += 2; + + row_info->pixel_depth = 24; + } + + else if (row_info->bit_depth == 16) + { + if (at_start) /* Skip initial filler */ + sp += 2; + else /* Skip initial channels and, for sp, the filler */ + sp += 8, dp += 6; + + while (sp < ep) + { + /* Copy 6 bytes, skip 2 */ + *dp++ = *sp++, *dp++ = *sp++; + *dp++ = *sp++, *dp++ = *sp++; + *dp++ = *sp++, *dp++ = *sp, sp += 3; + } + + row_info->pixel_depth = 48; + } + + else + return; /* bad bit depth */ + + row_info->channels = 3; + + /* Finally fix the color type if it records an alpha channel */ + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + row_info->color_type = PNG_COLOR_TYPE_RGB; + } + + else + return; /* The filler channel has gone already */ + + /* Fix the rowbytes value. */ + row_info->rowbytes = dp-row; } #endif #if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) -/* swaps red and blue bytes within a pixel */ +/* Swaps red and blue bytes within a pixel */ void /* PRIVATE */ png_do_bgr(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_bgr\n"); - if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - (row_info->color_type & PNG_COLOR_MASK_COLOR)) + png_debug(1, "in png_do_bgr"); + + if ((row_info->color_type & PNG_COLOR_MASK_COLOR)) { png_uint_32 row_width = row_info->width; if (row_info->bit_depth == 8) @@ -573,6 +613,7 @@ png_do_bgr(png_row_infop row_info, png_bytep row) *(rp + 2) = save; } } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { png_bytep rp; @@ -586,6 +627,8 @@ png_do_bgr(png_row_infop row_info, png_bytep row) } } } + +#ifdef PNG_16BIT_SUPPORTED else if (row_info->bit_depth == 16) { if (row_info->color_type == PNG_COLOR_TYPE_RGB) @@ -603,6 +646,7 @@ png_do_bgr(png_row_infop row_info, png_bytep row) *(rp + 5) = save; } } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { png_bytep rp; @@ -619,28 +663,128 @@ png_do_bgr(png_row_infop row_info, png_bytep row) } } } +#endif } } #endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ +#if defined(PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED) || \ + defined(PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED) +/* Added at libpng-1.5.10 */ +void /* PRIVATE */ +png_do_check_palette_indexes(png_structrp png_ptr, png_row_infop row_info) +{ + if (png_ptr->num_palette < (1 << row_info->bit_depth) && + png_ptr->num_palette > 0) /* num_palette can be 0 in MNG files */ + { + /* Calculations moved outside switch in an attempt to stop different + * compiler warnings. 'padding' is in *bits* within the last byte, it is + * an 'int' because pixel_depth becomes an 'int' in the expression below, + * and this calculation is used because it avoids warnings that other + * forms produced on either GCC or MSVC. + */ + int padding = (-row_info->pixel_depth * row_info->width) & 7; + png_bytep rp = png_ptr->row_buf + row_info->rowbytes; + + switch (row_info->bit_depth) + { + case 1: + { + /* in this case, all bytes must be 0 so we don't need + * to unpack the pixels except for the rightmost one. + */ + for (; rp > png_ptr->row_buf; rp--) + { + if (*rp >> padding != 0) + png_ptr->num_palette_max = 1; + padding = 0; + } + + break; + } + + case 2: + { + for (; rp > png_ptr->row_buf; rp--) + { + int i = ((*rp >> padding) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 2) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 4) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 6) & 0x03); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + padding = 0; + } + + break; + } + + case 4: + { + for (; rp > png_ptr->row_buf; rp--) + { + int i = ((*rp >> padding) & 0x0f); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + i = (((*rp >> padding) >> 4) & 0x0f); + + if (i > png_ptr->num_palette_max) + png_ptr->num_palette_max = i; + + padding = 0; + } + + break; + } + + case 8: + { + for (; rp > png_ptr->row_buf; rp--) + { + if (*rp > png_ptr->num_palette_max) + png_ptr->num_palette_max = (int) *rp; + } + + break; + } + + default: + break; + } + } +} +#endif /* PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED */ + #if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ - defined(PNG_LEGACY_SUPPORTED) + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED void PNGAPI -png_set_user_transform_info(png_structp png_ptr, png_voidp +png_set_user_transform_info(png_structrp png_ptr, png_voidp user_transform_ptr, int user_transform_depth, int user_transform_channels) { - png_debug(1, "in png_set_user_transform_info\n"); - if(png_ptr == NULL) return; -#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + png_debug(1, "in png_set_user_transform_info"); + + if (png_ptr == NULL) + return; png_ptr->user_transform_ptr = user_transform_ptr; png_ptr->user_transform_depth = (png_byte)user_transform_depth; png_ptr->user_transform_channels = (png_byte)user_transform_channels; -#else - if(user_transform_ptr || user_transform_depth || user_transform_channels) - png_warning(png_ptr, - "This version of libpng does not support user transform info"); -#endif } #endif @@ -649,14 +793,38 @@ png_set_user_transform_info(png_structp png_ptr, png_voidp * associated with this pointer before png_write_destroy and png_read_destroy * are called. */ +#ifdef PNG_USER_TRANSFORM_PTR_SUPPORTED png_voidp PNGAPI -png_get_user_transform_ptr(png_structp png_ptr) +png_get_user_transform_ptr(png_const_structrp png_ptr) { -#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) - if (png_ptr == NULL) return (NULL); - return ((png_voidp)png_ptr->user_transform_ptr); -#else - return (NULL); -#endif + if (png_ptr == NULL) + return (NULL); + + return png_ptr->user_transform_ptr; } +#endif + +#ifdef PNG_USER_TRANSFORM_INFO_SUPPORTED +png_uint_32 PNGAPI +png_get_current_row_number(png_const_structrp png_ptr) +{ + /* See the comments in png.h - this is the sub-image row when reading and + * interlaced image. + */ + if (png_ptr != NULL) + return png_ptr->row_number; + + return PNG_UINT_32_MAX; /* help the app not to fail silently */ +} + +png_byte PNGAPI +png_get_current_pass_number(png_const_structrp png_ptr) +{ + if (png_ptr != NULL) + return png_ptr->pass; + return 8; /* invalid */ +} +#endif /* PNG_USER_TRANSFORM_INFO_SUPPORTED */ +#endif /* PNG_READ_USER_TRANSFORM_SUPPORTED || + PNG_WRITE_USER_TRANSFORM_SUPPORTED */ #endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwio.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwio.c index 34da2b2..c5fca98 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwio.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwio.c @@ -1,12 +1,15 @@ /* 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 + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * * This file provides a location for all output. Users who need * special handling are expected to write functions that have the same * arguments as these and perform similar functions, but that possibly @@ -15,220 +18,147 @@ * them at run time with png_set_write_fn(...). */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" + #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. */ + * 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) +png_write_data(png_structrp png_ptr, png_const_bytep data, png_size_t length) { + /* NOTE: write_data_fn must not change the buffer! */ if (png_ptr->write_data_fn != NULL ) - (*(png_ptr->write_data_fn))(png_ptr, data, length); + (*(png_ptr->write_data_fn))(png_ptr, png_constcast(png_bytep,data), + length); + else png_error(png_ptr, "Call to NULL write function"); } -#if !defined(PNG_NO_STDIO) +#ifdef PNG_STDIO_SUPPORTED /* This is the function that does the actual writing of data. If you are - not writing to a standard C stream, you should create a replacement - write_data function and use it at run time with png_set_write_fn(), rather - than changing the library. */ -#ifndef USE_FAR_KEYWORD -void PNGAPI + * 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. + */ +void PNGCBAPI png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { - png_uint_32 check; + png_size_t check; + + if (png_ptr == NULL) + return; - if(png_ptr == NULL) return; -#if defined(_WIN32_WCE) - if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) - check = 0; -#else check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); -#endif + if (check != length) png_error(png_ptr, "Write Error"); } -#else -/* this is the model-independent version. Since the standard I/O library - can't handle far buffers in the medium and small models, we have to copy - the data. -*/ - -#define NEAR_BUF_SIZE 1024 -#define MIN(a,b) (a <= b ? a : b) - -void PNGAPI -png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) -{ - png_uint_32 check; - png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ - png_FILE_p io_ptr; - - if(png_ptr == NULL) return; - /* Check if data really is near. If so, use usual code. */ - near_data = (png_byte *)CVT_PTR_NOCHECK(data); - io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); - if ((png_bytep)near_data == data) - { -#if defined(_WIN32_WCE) - if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) - check = 0; -#else - check = fwrite(near_data, 1, length, io_ptr); -#endif - } - else - { - png_byte buf[NEAR_BUF_SIZE]; - png_size_t written, remaining, err; - check = 0; - remaining = length; - do - { - written = MIN(NEAR_BUF_SIZE, remaining); - png_memcpy(buf, data, written); /* copy far buffer to near buffer */ -#if defined(_WIN32_WCE) - if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) - err = 0; -#else - err = fwrite(buf, 1, written, io_ptr); -#endif - if (err != written) - break; - else - check += err; - data += written; - remaining -= written; - } - while (remaining != 0); - } - if (check != length) - png_error(png_ptr, "Write Error"); -} - -#endif #endif /* This function is called to output any data pending writing (normally - to disk). After png_flush is called, there should be no data pending - writing in any buffers. */ -#if defined(PNG_WRITE_FLUSH_SUPPORTED) + * to disk). After png_flush is called, there should be no data pending + * writing in any buffers. + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED void /* PRIVATE */ -png_flush(png_structp png_ptr) +png_flush(png_structrp png_ptr) { if (png_ptr->output_flush_fn != NULL) (*(png_ptr->output_flush_fn))(png_ptr); } -#if !defined(PNG_NO_STDIO) -void PNGAPI +# ifdef PNG_STDIO_SUPPORTED +void PNGCBAPI png_default_flush(png_structp png_ptr) { -#if !defined(_WIN32_WCE) png_FILE_p io_ptr; -#endif - if(png_ptr == NULL) return; -#if !defined(_WIN32_WCE) - io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); - if (io_ptr != NULL) - fflush(io_ptr); -#endif + + if (png_ptr == NULL) + return; + + io_ptr = png_voidcast(png_FILE_p, (png_ptr->io_ptr)); + fflush(io_ptr); } -#endif +# endif #endif /* This function allows the application to supply new output functions for - libpng if standard C streams aren't being used. - - This function takes as its arguments: - png_ptr - pointer to a png output data structure - io_ptr - pointer to user supplied structure containing info about - the output functions. May be NULL. - write_data_fn - pointer to a new output function that takes as its - arguments a pointer to a png_struct, a pointer to - data to be written, and a 32-bit unsigned int that is - the number of bytes to be written. The new write - function should call png_error(png_ptr, "Error msg") - to exit and output any fatal error messages. - flush_data_fn - pointer to a new flush function that takes as its - arguments a pointer to a png_struct. After a call to - the flush function, there should be no data in any buffers - or pending transmission. If the output method doesn't do - any buffering of ouput, a function prototype must still be - supplied although it doesn't have to do anything. If - PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile - time, output_flush_fn will be ignored, although it must be - supplied for compatibility. */ + * libpng if standard C streams aren't being used. + * + * This function takes as its arguments: + * png_ptr - pointer to a png output data structure + * io_ptr - pointer to user supplied structure containing info about + * the output functions. May be NULL. + * write_data_fn - pointer to a new output function that takes as its + * arguments a pointer to a png_struct, a pointer to + * data to be written, and a 32-bit unsigned int that is + * the number of bytes to be written. The new write + * function should call png_error(png_ptr, "Error msg") + * to exit and output any fatal error messages. May be + * NULL, in which case libpng's default function will + * be used. + * flush_data_fn - pointer to a new flush function that takes as its + * arguments a pointer to a png_struct. After a call to + * the flush function, there should be no data in any buffers + * or pending transmission. If the output method doesn't do + * any buffering of output, a function prototype must still be + * supplied although it doesn't have to do anything. If + * PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + * time, output_flush_fn will be ignored, although it must be + * supplied for compatibility. May be NULL, in which case + * libpng's default function will be used, if + * PNG_WRITE_FLUSH_SUPPORTED is defined. This is not + * a good idea if io_ptr does not point to a standard + * *FILE structure. + */ void PNGAPI -png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, - png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +png_set_write_fn(png_structrp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) { - if(png_ptr == NULL) return; + if (png_ptr == NULL) + return; + png_ptr->io_ptr = io_ptr; -#if !defined(PNG_NO_STDIO) +#ifdef PNG_STDIO_SUPPORTED if (write_data_fn != NULL) png_ptr->write_data_fn = write_data_fn; + else png_ptr->write_data_fn = png_default_write_data; #else png_ptr->write_data_fn = write_data_fn; #endif -#if defined(PNG_WRITE_FLUSH_SUPPORTED) -#if !defined(PNG_NO_STDIO) +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_STDIO_SUPPORTED + if (output_flush_fn != NULL) png_ptr->output_flush_fn = output_flush_fn; + else png_ptr->output_flush_fn = png_default_flush; -#else + +# else png_ptr->output_flush_fn = output_flush_fn; -#endif +# endif #endif /* PNG_WRITE_FLUSH_SUPPORTED */ /* It is an error to read while writing a png file */ if (png_ptr->read_data_fn != NULL) { png_ptr->read_data_fn = NULL; + png_warning(png_ptr, - "Attempted to set both read_data_fn and write_data_fn in"); - png_warning(png_ptr, - "the same structure. Resetting read_data_fn to NULL."); + "Can't set both read_data_fn and write_data_fn in the" + " same structure"); } } - -#if defined(USE_FAR_KEYWORD) -#if defined(_MSC_VER) -void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) -{ - void *near_ptr; - void FAR *far_ptr; - FP_OFF(near_ptr) = FP_OFF(ptr); - far_ptr = (void FAR *)near_ptr; - if(check != 0) - if(FP_SEG(ptr) != FP_SEG(far_ptr)) - png_error(png_ptr,"segment lost in conversion"); - return(near_ptr); -} -# else -void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) -{ - void *near_ptr; - void FAR *far_ptr; - near_ptr = (void FAR *)ptr; - far_ptr = (void FAR *)near_ptr; - if(check != 0) - if(far_ptr != ptr) - png_error(png_ptr,"segment lost in conversion"); - return(near_ptr); -} -# endif -# endif #endif /* PNG_WRITE_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwrite.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwrite.c index 8f9b537..4c68682 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwrite.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwrite.c @@ -1,18 +1,76 @@ /* 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 + * Last changed in libpng 1.6.1 [March 28, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h */ -/* get internal access to png.h */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" +#if defined(PNG_SIMPLIFIED_WRITE_SUPPORTED) && defined(PNG_STDIO_SUPPORTED) +# include +#endif + #ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +/* Write out all the unknown chunks for the current given location */ +static void +write_unknown_chunks(png_structrp png_ptr, png_const_inforp info_ptr, + unsigned int where) +{ + if (info_ptr->unknown_chunks_num) + { + png_const_unknown_chunkp up; + + png_debug(5, "writing extra chunks"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + ++up) + if (up->location & where) + { + /* If per-chunk unknown chunk handling is enabled use it, otherwise + * just write the chunks the application has set. + */ +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + int keep = png_handle_as_unknown(png_ptr, up->name); + + /* NOTE: this code is radically different from the read side in the + * matter of handling an ancillary unknown chunk. In the read side + * the default behavior is to discard it, in the code below the default + * behavior is to write it. Critical chunks are, however, only + * written if explicitly listed or if the default is set to write all + * unknown chunks. + * + * The default handling is also slightly weird - it is not possible to + * stop the writing of all unsafe-to-copy chunks! + * + * TODO: REVIEW: this would seem to be a bug. + */ + if (keep != PNG_HANDLE_CHUNK_NEVER && + ((up->name[3] & 0x20) /* safe-to-copy overrides everything */ || + keep == PNG_HANDLE_CHUNK_ALWAYS || + (keep == PNG_HANDLE_CHUNK_AS_DEFAULT && + png_ptr->unknown_default == PNG_HANDLE_CHUNK_ALWAYS))) +#endif + { + /* TODO: review, what is wrong with a zero length unknown chunk? */ + if (up->size == 0) + png_warning(png_ptr, "Writing zero-length unknown chunk"); + + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +} +#endif /* PNG_WRITE_UNKNOWN_CHUNKS_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 @@ -23,112 +81,120 @@ * them in png_write_end(), and compressing them. */ void PNGAPI -png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +png_write_info_before_PLTE(png_structrp png_ptr, png_const_inforp info_ptr) { - png_debug(1, "in png_write_info_before_PLTE\n"); + png_debug(1, "in png_write_info_before_PLTE"); + if (png_ptr == NULL || info_ptr == NULL) return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) { - png_write_sig(png_ptr); /* write PNG signature */ -#if defined(PNG_MNG_FEATURES_SUPPORTED) - if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted)) + /* Write PNG signature */ + png_write_sig(png_ptr); + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) && \ + (png_ptr->mng_features_permitted)) { - png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); - png_ptr->mng_features_permitted=0; + png_warning(png_ptr, "MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted = 0; } #endif - /* write IHDR information. */ + + /* Write IHDR information. */ png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, - info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, - info_ptr->filter_type, -#if defined(PNG_WRITE_INTERLACING_SUPPORTED) - info_ptr->interlace_type); + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + info_ptr->interlace_type #else - 0); + 0 #endif - /* the rest of these check to see if the valid field has the appropriate - flag set, and if it does, writes the chunk. */ -#if defined(PNG_WRITE_gAMA_SUPPORTED) - if (info_ptr->valid & PNG_INFO_gAMA) - { -# ifdef PNG_FLOATING_POINT_SUPPORTED - png_write_gAMA(png_ptr, info_ptr->gamma); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED - png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); + ); + + /* The rest of these check to see if the valid field has the appropriate + * flag set, and if it does, writes the chunk. + * + * 1.6.0: COLORSPACE support controls the writing of these chunks too, and + * the chunks will be written if the WRITE routine is there and information + * is available in the COLORSPACE. (See png_colorspace_sync_info in png.c + * for where the valid flags get set.) + * + * Under certain circumstances the colorspace can be invalidated without + * syncing the info_struct 'valid' flags; this happens if libpng detects and + * error and calls png_error while the color space is being set, yet the + * application continues writing the PNG. So check the 'invalid' flag here + * too. + */ +#ifdef PNG_GAMMA_SUPPORTED +# ifdef PNG_WRITE_gAMA_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_gAMA) && + (info_ptr->valid & PNG_INFO_gAMA)) + png_write_gAMA_fixed(png_ptr, info_ptr->colorspace.gamma); # endif #endif - } -#endif -#if defined(PNG_WRITE_sRGB_SUPPORTED) - if (info_ptr->valid & PNG_INFO_sRGB) - png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); -#endif -#if defined(PNG_WRITE_iCCP_SUPPORTED) - if (info_ptr->valid & PNG_INFO_iCCP) - png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, - info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); -#endif -#if defined(PNG_WRITE_sBIT_SUPPORTED) + +#ifdef PNG_COLORSPACE_SUPPORTED + /* Write only one of sRGB or an ICC profile. If a profile was supplied + * and it matches one of the known sRGB ones issue a warning. + */ +# ifdef PNG_WRITE_iCCP_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->valid & PNG_INFO_iCCP)) + { +# ifdef PNG_WRITE_sRGB_SUPPORTED + if (info_ptr->valid & PNG_INFO_sRGB) + png_app_warning(png_ptr, + "profile matches sRGB but writing iCCP instead"); +# endif + + png_write_iCCP(png_ptr, info_ptr->iccp_name, + info_ptr->iccp_profile); + } +# ifdef PNG_WRITE_sRGB_SUPPORTED + else +# endif +# endif + +# ifdef PNG_WRITE_sRGB_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->valid & PNG_INFO_sRGB)) + png_write_sRGB(png_ptr, info_ptr->colorspace.rendering_intent); +# endif /* WRITE_sRGB */ +#endif /* COLORSPACE */ + +#ifdef PNG_WRITE_sBIT_SUPPORTED if (info_ptr->valid & PNG_INFO_sBIT) png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); #endif -#if defined(PNG_WRITE_cHRM_SUPPORTED) - if (info_ptr->valid & PNG_INFO_cHRM) - { -#ifdef PNG_FLOATING_POINT_SUPPORTED - png_write_cHRM(png_ptr, - info_ptr->x_white, info_ptr->y_white, - info_ptr->x_red, info_ptr->y_red, - info_ptr->x_green, info_ptr->y_green, - info_ptr->x_blue, info_ptr->y_blue); -#else -# ifdef PNG_FIXED_POINT_SUPPORTED - png_write_cHRM_fixed(png_ptr, - info_ptr->int_x_white, info_ptr->int_y_white, - info_ptr->int_x_red, info_ptr->int_y_red, - info_ptr->int_x_green, info_ptr->int_y_green, - info_ptr->int_x_blue, info_ptr->int_y_blue); + +#ifdef PNG_COLORSPACE_SUPPORTED +# ifdef PNG_WRITE_cHRM_SUPPORTED + if (!(info_ptr->colorspace.flags & PNG_COLORSPACE_INVALID) && + (info_ptr->colorspace.flags & PNG_COLORSPACE_FROM_cHRM) && + (info_ptr->valid & PNG_INFO_cHRM)) + png_write_cHRM_fixed(png_ptr, &info_ptr->colorspace.end_points_xy); # endif #endif - } -#endif -#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) - if (info_ptr->unknown_chunks_num) - { - png_unknown_chunk *up; - png_debug(5, "writing extra chunks\n"); - - for (up = info_ptr->unknown_chunks; - up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; - up++) - { - int keep=png_handle_as_unknown(png_ptr, up->name); - if (keep != PNG_HANDLE_CHUNK_NEVER && - up->location && !(up->location & PNG_HAVE_PLTE) && - !(up->location & PNG_HAVE_IDAT) && - ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || - (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) - { - png_write_chunk(png_ptr, up->name, up->data, up->size); - } - } - } +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_IHDR); #endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; } } void PNGAPI -png_write_info(png_structp png_ptr, png_infop info_ptr) +png_write_info(png_structrp png_ptr, png_const_inforp info_ptr) { #if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) int i; #endif - png_debug(1, "in png_write_info\n"); + png_debug(1, "in png_write_info"); if (png_ptr == NULL || info_ptr == NULL) return; @@ -137,152 +203,136 @@ png_write_info(png_structp png_ptr, png_infop info_ptr) if (info_ptr->valid & PNG_INFO_PLTE) png_write_PLTE(png_ptr, info_ptr->palette, - (png_uint_32)info_ptr->num_palette); + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) png_error(png_ptr, "Valid palette required for paletted images"); -#if defined(PNG_WRITE_tRNS_SUPPORTED) +#ifdef PNG_WRITE_tRNS_SUPPORTED if (info_ptr->valid & PNG_INFO_tRNS) + { +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { -#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) - /* invert the alpha channel (in tRNS) */ - if ((png_ptr->transformations & PNG_INVERT_ALPHA) && - info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - int j; - for (j=0; j<(int)info_ptr->num_trans; j++) - info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); - } -#endif - png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), - info_ptr->num_trans, info_ptr->color_type); + int j; + for (j = 0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans_alpha[j] = + (png_byte)(255 - info_ptr->trans_alpha[j]); } #endif -#if defined(PNG_WRITE_bKGD_SUPPORTED) + png_write_tRNS(png_ptr, info_ptr->trans_alpha, &(info_ptr->trans_color), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#ifdef PNG_WRITE_bKGD_SUPPORTED if (info_ptr->valid & PNG_INFO_bKGD) png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); #endif -#if defined(PNG_WRITE_hIST_SUPPORTED) + +#ifdef PNG_WRITE_hIST_SUPPORTED if (info_ptr->valid & PNG_INFO_hIST) png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); #endif -#if defined(PNG_WRITE_oFFs_SUPPORTED) + +#ifdef PNG_WRITE_oFFs_SUPPORTED if (info_ptr->valid & PNG_INFO_oFFs) png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, - info_ptr->offset_unit_type); + info_ptr->offset_unit_type); #endif -#if defined(PNG_WRITE_pCAL_SUPPORTED) + +#ifdef PNG_WRITE_pCAL_SUPPORTED if (info_ptr->valid & PNG_INFO_pCAL) png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, - info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, - info_ptr->pcal_units, info_ptr->pcal_params); + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); #endif -#if defined(PNG_WRITE_sCAL_SUPPORTED) + +#ifdef PNG_WRITE_sCAL_SUPPORTED if (info_ptr->valid & PNG_INFO_sCAL) -#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) - png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, - info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); -#else -#ifdef PNG_FIXED_POINT_SUPPORTED png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, info_ptr->scal_s_width, info_ptr->scal_s_height); -#else - png_warning(png_ptr, - "png_write_sCAL not supported; sCAL chunk not written."); -#endif -#endif -#endif -#if defined(PNG_WRITE_pHYs_SUPPORTED) +#endif /* sCAL */ + +#ifdef PNG_WRITE_pHYs_SUPPORTED if (info_ptr->valid & PNG_INFO_pHYs) png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, - info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); -#endif -#if defined(PNG_WRITE_tIME_SUPPORTED) + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif /* pHYs */ + +#ifdef PNG_WRITE_tIME_SUPPORTED if (info_ptr->valid & PNG_INFO_tIME) { png_write_tIME(png_ptr, &(info_ptr->mod_time)); png_ptr->mode |= PNG_WROTE_tIME; } -#endif -#if defined(PNG_WRITE_sPLT_SUPPORTED) +#endif /* tIME */ + +#ifdef PNG_WRITE_sPLT_SUPPORTED if (info_ptr->valid & PNG_INFO_sPLT) - for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) - png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); -#endif -#if defined(PNG_WRITE_TEXT_SUPPORTED) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif /* sPLT */ + +#ifdef PNG_WRITE_TEXT_SUPPORTED /* Check to see if we need to write text chunks */ for (i = 0; i < info_ptr->num_text; i++) { - png_debug2(2, "Writing header text chunk %d, type %d\n", i, - info_ptr->text[i].compression); - /* an internationalized chunk? */ + png_debug2(2, "Writing header text chunk %d, type %d", i, + info_ptr->text[i].compression); + /* An internationalized chunk? */ if (info_ptr->text[i].compression > 0) { -#if defined(PNG_WRITE_iTXt_SUPPORTED) - /* write international chunk */ - png_write_iTXt(png_ptr, - info_ptr->text[i].compression, - info_ptr->text[i].key, - info_ptr->text[i].lang, - info_ptr->text[i].lang_key, - info_ptr->text[i].text); +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); #else png_warning(png_ptr, "Unable to write international text"); #endif /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; } + /* If we want a compressed text chunk */ else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) { -#if defined(PNG_WRITE_zTXt_SUPPORTED) - /* write compressed chunk */ +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ png_write_zTXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, 0, - info_ptr->text[i].compression); + 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 */ +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ png_write_tEXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, - 0); -#else - png_warning(png_ptr, "Unable to write uncompressed text"); -#endif + info_ptr->text[i].text, + 0); /* Mark this chunk as written */ info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; +#else + /* Can't get here */ + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif } } -#endif -#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) - if (info_ptr->unknown_chunks_num) - { - png_unknown_chunk *up; +#endif /* tEXt */ - png_debug(5, "writing extra chunks\n"); - - for (up = info_ptr->unknown_chunks; - up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; - up++) - { - int keep=png_handle_as_unknown(png_ptr, up->name); - if (keep != PNG_HANDLE_CHUNK_NEVER && - up->location && (up->location & PNG_HAVE_PLTE) && - !(up->location & PNG_HAVE_IDAT) && - ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || - (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) - { - png_write_chunk(png_ptr, up->name, up->data, up->size); - } - } - } +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_HAVE_PLTE); #endif } @@ -292,68 +342,78 @@ png_write_info(png_structp png_ptr, png_infop info_ptr) * comments, I suggest writing them here, and compressing them. */ void PNGAPI -png_write_end(png_structp png_ptr, png_infop info_ptr) +png_write_end(png_structrp png_ptr, png_inforp info_ptr) { - png_debug(1, "in png_write_end\n"); + png_debug(1, "in png_write_end"); + if (png_ptr == NULL) return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) png_error(png_ptr, "No IDATs written into file"); - /* see if user wants us to write information chunks */ +#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + if (png_ptr->num_palette_max > png_ptr->num_palette) + png_benign_error(png_ptr, "Wrote palette index exceeding num_palette"); +#endif + + /* See if user wants us to write information chunks */ if (info_ptr != NULL) { -#if defined(PNG_WRITE_TEXT_SUPPORTED) +#ifdef 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 */ +#ifdef PNG_WRITE_tIME_SUPPORTED + /* Check to see if user has supplied a time chunk */ if ((info_ptr->valid & PNG_INFO_tIME) && - !(png_ptr->mode & PNG_WROTE_tIME)) + !(png_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 */ +#ifdef PNG_WRITE_TEXT_SUPPORTED + /* Loop through comment chunks */ for (i = 0; i < info_ptr->num_text; i++) { - png_debug2(2, "Writing trailer text chunk %d, type %d\n", i, + png_debug2(2, "Writing trailer text chunk %d, type %d", i, info_ptr->text[i].compression); - /* an internationalized chunk? */ + /* An internationalized chunk? */ if (info_ptr->text[i].compression > 0) { -#if defined(PNG_WRITE_iTXt_SUPPORTED) - /* write international chunk */ - png_write_iTXt(png_ptr, - info_ptr->text[i].compression, - info_ptr->text[i].key, - info_ptr->text[i].lang, - info_ptr->text[i].lang_key, - info_ptr->text[i].text); +#ifdef PNG_WRITE_iTXt_SUPPORTED + /* Write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); #else - png_warning(png_ptr, "Unable to write international text"); + 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; + /* 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 */ +#ifdef PNG_WRITE_zTXt_SUPPORTED + /* Write compressed chunk */ png_write_zTXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, 0, - info_ptr->text[i].compression); + 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 */ +#ifdef PNG_WRITE_tEXt_SUPPORTED + /* Write uncompressed chunk */ png_write_tEXt(png_ptr, info_ptr->text[i].key, - info_ptr->text[i].text, 0); + info_ptr->text[i].text, 0); #else png_warning(png_ptr, "Unable to write uncompressed text"); #endif @@ -363,43 +423,35 @@ png_write_end(png_structp png_ptr, png_infop info_ptr) } } #endif -#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) - if (info_ptr->unknown_chunks_num) - { - png_unknown_chunk *up; - - png_debug(5, "writing extra chunks\n"); - - for (up = info_ptr->unknown_chunks; - up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; - up++) - { - int keep=png_handle_as_unknown(png_ptr, up->name); - if (keep != PNG_HANDLE_CHUNK_NEVER && - up->location && (up->location & PNG_AFTER_IDAT) && - ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || - (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) - { - png_write_chunk(png_ptr, up->name, up->data, up->size); - } - } - } +#ifdef PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED + write_unknown_chunks(png_ptr, info_ptr, PNG_AFTER_IDAT); #endif } png_ptr->mode |= PNG_AFTER_IDAT; - /* write end of PNG file */ + /* Write end of PNG file */ png_write_IEND(png_ptr); + /* This flush, added in libpng-1.0.8, removed from libpng-1.0.9beta03, + * and restored again in libpng-1.2.30, may cause some applications that + * do not set png_ptr->output_flush_fn to crash. If your application + * experiences a problem, please try building libpng with + * PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED defined, and report the event to + * png-mng-implement at lists.sf.net . + */ +#ifdef PNG_WRITE_FLUSH_SUPPORTED +# ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED + png_flush(png_ptr); +# endif +#endif } -#if defined(PNG_WRITE_tIME_SUPPORTED) -#if !defined(_WIN32_WCE) -/* "time.h" functions are not supported on WindowsCE */ +#ifdef PNG_CONVERT_tIME_SUPPORTED void PNGAPI -png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +png_convert_from_struct_tm(png_timep ptime, PNG_CONST struct tm * ttime) { - png_debug(1, "in png_convert_from_struct_tm\n"); + png_debug(1, "in png_convert_from_struct_tm"); + ptime->year = (png_uint_16)(1900 + ttime->tm_year); ptime->month = (png_byte)(ttime->tm_mon + 1); ptime->day = (png_byte)ttime->tm_mday; @@ -413,273 +465,90 @@ png_convert_from_time_t(png_timep ptime, time_t ttime) { struct tm *tbuf; - png_debug(1, "in png_convert_from_time_t\n"); + png_debug(1, "in png_convert_from_time_t"); + tbuf = gmtime(&ttime); png_convert_from_struct_tm(ptime, tbuf); } #endif -#endif /* Initialize png_ptr structure, and allocate any memory needed */ -png_structp PNGAPI -png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn) +PNG_FUNCTION(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),PNG_ALLOCATED) { -#ifdef PNG_USER_MEM_SUPPORTED - return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, - warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +#ifndef PNG_USER_MEM_SUPPORTED + png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, NULL, NULL, NULL); +#else + return png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, NULL, NULL, NULL); } /* Alternate initialize png_ptr structure, and allocate any memory needed */ -png_structp PNGAPI -png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, - png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, - png_malloc_ptr malloc_fn, png_free_ptr free_fn) +PNG_FUNCTION(png_structp,PNGAPI +png_create_write_struct_2,(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn),PNG_ALLOCATED) { + png_structrp png_ptr = png_create_png_struct(user_png_ver, error_ptr, + error_fn, warn_fn, mem_ptr, malloc_fn, free_fn); #endif /* PNG_USER_MEM_SUPPORTED */ - png_structp png_ptr; -#ifdef PNG_SETJMP_SUPPORTED -#ifdef USE_FAR_KEYWORD - jmp_buf jmpbuf; -#endif -#endif - int i; - png_debug(1, "in png_create_write_struct\n"); -#ifdef PNG_USER_MEM_SUPPORTED - png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, - (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); -#else - png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); -#endif /* PNG_USER_MEM_SUPPORTED */ - if (png_ptr == NULL) - return (NULL); - /* added at libpng-1.2.6 */ -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - png_ptr->user_width_max=PNG_USER_WIDTH_MAX; - png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; -#endif + /* Set the zlib control values to defaults; they can be overridden by the + * application after the struct has been created. + */ + png_ptr->zbuffer_size = PNG_ZBUF_SIZE; -#ifdef PNG_SETJMP_SUPPORTED -#ifdef USE_FAR_KEYWORD - if (setjmp(jmpbuf)) -#else - if (setjmp(png_ptr->jmpbuf)) -#endif + /* The 'zlib_strategy' setting is irrelevant because png_default_claim in + * pngwutil.c defaults it according to whether or not filters will be used, + * and ignores this setting. + */ + png_ptr->zlib_strategy = PNG_Z_DEFAULT_STRATEGY; + png_ptr->zlib_level = PNG_Z_DEFAULT_COMPRESSION; + png_ptr->zlib_mem_level = 8; + png_ptr->zlib_window_bits = 15; + png_ptr->zlib_method = 8; + +#ifdef PNG_WRITE_COMPRESSED_TEXT_SUPPORTED + png_ptr->zlib_text_strategy = PNG_TEXT_Z_DEFAULT_STRATEGY; + png_ptr->zlib_text_level = PNG_TEXT_Z_DEFAULT_COMPRESSION; + png_ptr->zlib_text_mem_level = 8; + png_ptr->zlib_text_window_bits = 15; + png_ptr->zlib_text_method = 8; +#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ + + /* This is a highly dubious configuration option; by default it is off, but + * it may be appropriate for private builds that are testing extensions not + * conformant to the current specification, or of applications that must not + * fail to write at all costs! + */ +# ifdef PNG_BENIGN_WRITE_ERRORS_SUPPORTED + png_ptr->flags |= PNG_FLAG_BENIGN_ERRORS_WARN; + /* In stable builds only warn if an application error can be completely + * handled. + */ +# endif + + /* App warnings are warnings in release (or release candidate) builds but + * are errors during development. + */ +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_ptr->flags |= PNG_FLAG_APP_WARNINGS_WARN; +# endif + + if (png_ptr != NULL) { - png_free(png_ptr, png_ptr->zbuf); - png_ptr->zbuf=NULL; - png_destroy_struct(png_ptr); - return (NULL); - } -#ifdef USE_FAR_KEYWORD - png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); -#endif -#endif - -#ifdef PNG_USER_MEM_SUPPORTED - png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); -#endif /* PNG_USER_MEM_SUPPORTED */ - png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); - - i=0; - do - { - if(user_png_ver[i] != png_libpng_ver[i]) - png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; - } while (png_libpng_ver[i++]); - - if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) - { - /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so - * we must recompile any applications that use any older library version. - * For versions after libpng 1.0, we will be compatible, so we need - * only check the first digit. - */ - if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || - (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || - (user_png_ver[0] == '0' && user_png_ver[2] < '9')) - { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char msg[80]; - if (user_png_ver) - { - png_snprintf(msg, 80, - "Application was compiled with png.h from libpng-%.20s", - user_png_ver); - png_warning(png_ptr, msg); - } - png_snprintf(msg, 80, - "Application is running with png.c from libpng-%.20s", - png_libpng_ver); - png_warning(png_ptr, msg); -#endif -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags=0; -#endif - png_error(png_ptr, - "Incompatible libpng version in application and library"); - } + /* TODO: delay this, it can be done in png_init_io() (if the app doesn't + * do it itself) avoiding setting the default function if it is not + * required. + */ + png_set_write_fn(png_ptr, NULL, NULL, 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); - - png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, - png_flush_ptr_NULL); - -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) - png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, - 1, png_doublep_NULL, png_doublep_NULL); -#endif - -#ifdef PNG_SETJMP_SUPPORTED -/* Applications that neglect to set up their own setjmp() and then encounter - a png_error() will longjmp here. Since the jmpbuf is then meaningless we - abort instead of returning. */ -#ifdef USE_FAR_KEYWORD - if (setjmp(jmpbuf)) - PNG_ABORT(); - png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); -#else - if (setjmp(png_ptr->jmpbuf)) - PNG_ABORT(); -#endif -#endif - return (png_ptr); + return png_ptr; } -/* Initialize png_ptr structure, and allocate any memory needed */ -#if defined(PNG_1_0_X) || defined(PNG_1_2_X) -/* Deprecated. */ -#undef png_write_init -void PNGAPI -png_write_init(png_structp png_ptr) -{ - /* We only come here via pre-1.0.7-compiled applications */ - png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); -} - -void PNGAPI -png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, - png_size_t png_struct_size, png_size_t png_info_size) -{ - /* We only come here via pre-1.0.12-compiled applications */ - if(png_ptr == NULL) return; -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - if(png_sizeof(png_struct) > png_struct_size || - png_sizeof(png_info) > png_info_size) - { - char msg[80]; - png_ptr->warning_fn=NULL; - if (user_png_ver) - { - png_snprintf(msg, 80, - "Application was compiled with png.h from libpng-%.20s", - user_png_ver); - png_warning(png_ptr, msg); - } - png_snprintf(msg, 80, - "Application is running with png.c from libpng-%.20s", - png_libpng_ver); - png_warning(png_ptr, msg); - } -#endif - if(png_sizeof(png_struct) > png_struct_size) - { - png_ptr->error_fn=NULL; -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags=0; -#endif - png_error(png_ptr, - "The png struct allocated by the application for writing is too small."); - } - if(png_sizeof(png_info) > png_info_size) - { - png_ptr->error_fn=NULL; -#ifdef PNG_ERROR_NUMBERS_SUPPORTED - png_ptr->flags=0; -#endif - png_error(png_ptr, - "The info struct allocated by the application for writing is too small."); - } - png_write_init_3(&png_ptr, user_png_ver, png_struct_size); -} -#endif /* PNG_1_0_X || PNG_1_2_X */ - - -void PNGAPI -png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, - png_size_t png_struct_size) -{ - png_structp png_ptr=*ptr_ptr; -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf tmp_jmp; /* to save current jump buffer */ -#endif - - int i = 0; - - if (png_ptr == NULL) - return; - - do - { - if (user_png_ver[i] != png_libpng_ver[i]) - { -#ifdef PNG_LEGACY_SUPPORTED - png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; -#else - png_ptr->warning_fn=NULL; - png_warning(png_ptr, - "Application uses deprecated png_write_init() and should be recompiled."); - break; -#endif - } - } while (png_libpng_ver[i++]); - - png_debug(1, "in png_write_init_3\n"); - -#ifdef PNG_SETJMP_SUPPORTED - /* save jump buffer and error functions */ - png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); -#endif - - if (png_sizeof(png_struct) > png_struct_size) - { - png_destroy_struct(png_ptr); - png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); - *ptr_ptr = png_ptr; - } - - /* reset all variables to 0 */ - png_memset(png_ptr, 0, png_sizeof (png_struct)); - - /* added at libpng-1.2.6 */ -#ifdef PNG_SET_USER_LIMITS_SUPPORTED - png_ptr->user_width_max=PNG_USER_WIDTH_MAX; - png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; -#endif - -#ifdef PNG_SETJMP_SUPPORTED - /* restore jump buffer */ - png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); -#endif - - png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, - png_flush_ptr_NULL); - - /* initialize zbuf - compression buffer */ - png_ptr->zbuf_size = PNG_ZBUF_SIZE; - png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, - (png_uint_32)png_ptr->zbuf_size); - -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) - png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, - 1, png_doublep_NULL, png_doublep_NULL); -#endif -} /* Write a few rows of image data. If the image is interlaced, * either you will have to write the 7 sub images, or, if you @@ -687,18 +556,18 @@ png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, * "write" the image seven times. */ void PNGAPI -png_write_rows(png_structp png_ptr, png_bytepp row, - png_uint_32 num_rows) +png_write_rows(png_structrp png_ptr, png_bytepp row, + png_uint_32 num_rows) { png_uint_32 i; /* row counter */ png_bytepp rp; /* row pointer */ - png_debug(1, "in png_write_rows\n"); + png_debug(1, "in png_write_rows"); if (png_ptr == NULL) return; - /* loop through the rows */ + /* Loop through the rows */ for (i = 0, rp = row; i < num_rows; i++, rp++) { png_write_row(png_ptr, *rp); @@ -709,7 +578,7 @@ png_write_rows(png_structp png_ptr, png_bytepp row, * if you are writing an interlaced image. */ void PNGAPI -png_write_image(png_structp png_ptr, png_bytepp image) +png_write_image(png_structrp png_ptr, png_bytepp image) { png_uint_32 i; /* row index */ int pass, num_pass; /* pass variables */ @@ -718,18 +587,20 @@ png_write_image(png_structp png_ptr, png_bytepp image) if (png_ptr == NULL) return; - png_debug(1, "in png_write_image\n"); -#if defined(PNG_WRITE_INTERLACING_SUPPORTED) - /* intialize interlace handling. If image is not interlaced, - this will set pass to 1 */ + png_debug(1, "in png_write_image"); + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Initialize interlace handling. If image is not interlaced, + * this will set pass to 1 + */ num_pass = png_set_interlace_handling(png_ptr); #else num_pass = 1; #endif - /* loop through passes */ + /* Loop through passes */ for (pass = 0; pass < num_pass; pass++) { - /* loop through image */ + /* Loop through image */ for (i = 0, rp = image; i < png_ptr->height; i++, rp++) { png_write_row(png_ptr, *rp); @@ -737,58 +608,69 @@ png_write_image(png_structp png_ptr, png_bytepp image) } } -/* called by user to write a row of image data */ +/* Called by user to write a row of image data */ void PNGAPI -png_write_row(png_structp png_ptr, png_bytep row) +png_write_row(png_structrp png_ptr, png_const_bytep row) { + /* 1.5.6: moved from png_struct to be a local structure: */ + png_row_info row_info; + if (png_ptr == NULL) return; - png_debug2(1, "in png_write_row (row %ld, pass %d)\n", + + png_debug2(1, "in png_write_row (row %u, pass %d)", png_ptr->row_number, png_ptr->pass); - /* initialize transformations and other stuff if first time */ + /* 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."); + /* 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 */ + /* 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."); + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined"); #endif + #if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) - if (png_ptr->transformations & PNG_FILLER) - png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined."); + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined"); #endif -#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED) - if (png_ptr->transformations & PNG_PACKSWAP) - png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && \ + defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, + "PNG_WRITE_PACKSWAP_SUPPORTED is not defined"); #endif + #if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) - if (png_ptr->transformations & PNG_PACK) - png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined"); #endif + #if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) - if (png_ptr->transformations & PNG_SHIFT) - png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined"); #endif + #if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) - if (png_ptr->transformations & PNG_BGR) - png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined"); #endif + #if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) - if (png_ptr->transformations & PNG_SWAP_BYTES) - png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined"); #endif png_write_start_row(png_ptr); } -#if defined(PNG_WRITE_INTERLACING_SUPPORTED) - /* if interlaced and not interested in row, return */ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* If interlaced and not interested in row, return */ if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) { switch (png_ptr->pass) @@ -800,6 +682,7 @@ png_write_row(png_structp png_ptr, png_bytep row) return; } break; + case 1: if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) { @@ -807,6 +690,7 @@ png_write_row(png_structp png_ptr, png_bytep row) return; } break; + case 2: if ((png_ptr->row_number & 0x07) != 4) { @@ -814,6 +698,7 @@ png_write_row(png_structp png_ptr, png_bytep row) return; } break; + case 3: if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) { @@ -821,6 +706,7 @@ png_write_row(png_structp png_ptr, png_bytep row) return; } break; + case 4: if ((png_ptr->row_number & 0x03) != 2) { @@ -828,6 +714,7 @@ png_write_row(png_structp png_ptr, png_bytep row) return; } break; + case 5: if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) { @@ -835,6 +722,7 @@ png_write_row(png_structp png_ptr, png_bytep row) return; } break; + case 6: if (!(png_ptr->row_number & 0x01)) { @@ -842,41 +730,39 @@ png_write_row(png_structp png_ptr, png_bytep row) return; } break; + + default: /* error: ignore it */ + break; } } #endif - /* set up row info for transformations */ - png_ptr->row_info.color_type = png_ptr->color_type; - png_ptr->row_info.width = png_ptr->usr_width; - png_ptr->row_info.channels = png_ptr->usr_channels; - png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; - png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * - png_ptr->row_info.channels); + /* Set up row info for transformations */ + row_info.color_type = png_ptr->color_type; + row_info.width = png_ptr->usr_width; + row_info.channels = png_ptr->usr_channels; + row_info.bit_depth = png_ptr->usr_bit_depth; + row_info.pixel_depth = (png_byte)(row_info.bit_depth * row_info.channels); + row_info.rowbytes = PNG_ROWBYTES(row_info.pixel_depth, row_info.width); - png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, - png_ptr->row_info.width); - - png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type); - png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width); - png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels); - png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth); - png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth); - png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes); + png_debug1(3, "row_info->color_type = %d", row_info.color_type); + png_debug1(3, "row_info->width = %u", row_info.width); + png_debug1(3, "row_info->channels = %d", row_info.channels); + png_debug1(3, "row_info->bit_depth = %d", row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d", row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu", (unsigned long)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); + memcpy(png_ptr->row_buf + 1, row, row_info.rowbytes); -#if defined(PNG_WRITE_INTERLACING_SUPPORTED) - /* handle interlacing */ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* Handle interlacing */ if (png_ptr->interlaced && png_ptr->pass < 6 && - (png_ptr->transformations & PNG_INTERLACE)) + (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_do_write_interlace(&row_info, png_ptr->row_buf + 1, png_ptr->pass); + /* This should always get caught above, but still ... */ + if (!(row_info.width)) { png_write_finish_row(png_ptr); return; @@ -884,11 +770,20 @@ png_write_row(png_structp png_ptr, png_bytep row) } #endif - /* handle other transformations */ +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED + /* Handle other transformations */ if (png_ptr->transformations) - png_do_write_transformations(png_ptr); + png_do_write_transformations(png_ptr, &row_info); +#endif -#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* At this point the row_info pixel depth must match the 'transformed' depth, + * which is also the output depth. + */ + if (row_info.pixel_depth != png_ptr->pixel_depth || + row_info.pixel_depth != png_ptr->transformed_pixel_depth) + png_error(png_ptr, "internal write transform logic error"); + +#ifdef PNG_MNG_FEATURES_SUPPORTED /* Write filter_method 64 (intrapixel differencing) only if * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and * 2. Libpng did not write a PNG signature (this filter_method is only @@ -898,248 +793,177 @@ png_write_row(png_structp png_ptr, png_bytep row) * 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)) + 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); + png_do_write_intrapixel(&row_info, png_ptr->row_buf + 1); } #endif +/* Added at libpng-1.5.10 */ +#ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED + /* Check for out-of-range palette index */ + if (row_info.color_type == PNG_COLOR_TYPE_PALETTE && + png_ptr->num_palette_max >= 0) + png_do_check_palette_indexes(png_ptr, &row_info); +#endif + /* Find a filter if necessary, filter the row and write it out. */ - png_write_find_filter(png_ptr, &(png_ptr->row_info)); + png_write_find_filter(png_ptr, &row_info); if (png_ptr->write_row_fn != NULL) (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); } -#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#ifdef PNG_WRITE_FLUSH_SUPPORTED /* Set the automatic flush interval or 0 to turn flushing off */ void PNGAPI -png_set_flush(png_structp png_ptr, int nrows) +png_set_flush(png_structrp png_ptr, int nrows) { - png_debug(1, "in png_set_flush\n"); + png_debug(1, "in png_set_flush"); + if (png_ptr == NULL) return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); } -/* flush the current output buffers now */ +/* Flush the current output buffers now */ void PNGAPI -png_write_flush(png_structp png_ptr) +png_write_flush(png_structrp png_ptr) { - int wrote_IDAT; + png_debug(1, "in png_write_flush"); - 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; + return; - do - { - int ret; - - /* compress the data */ - ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); - wrote_IDAT = 0; - - /* check for compression errors */ - if (ret != Z_OK) - { - if (png_ptr->zstream.msg != NULL) - png_error(png_ptr, png_ptr->zstream.msg); - else - png_error(png_ptr, "zlib error"); - } - - if (!(png_ptr->zstream.avail_out)) - { - /* write the IDAT and reset the zlib output buffer */ - png_write_IDAT(png_ptr, png_ptr->zbuf, - png_ptr->zbuf_size); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - wrote_IDAT = 1; - } - } while(wrote_IDAT == 1); - - /* If there is any data left to be output, write it into a new IDAT */ - if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) - { - /* write the IDAT and reset the zlib output buffer */ - png_write_IDAT(png_ptr, png_ptr->zbuf, - png_ptr->zbuf_size - png_ptr->zstream.avail_out); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - } + png_compress_IDAT(png_ptr, NULL, 0, Z_SYNC_FLUSH); png_ptr->flush_rows = 0; png_flush(png_ptr); } #endif /* PNG_WRITE_FLUSH_SUPPORTED */ -/* free all memory used by the write */ -void PNGAPI -png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +static void png_reset_filter_heuristics(png_structrp png_ptr);/* forward decl */ +#endif + +/* Free any memory used in png_ptr struct without freeing the struct itself. */ +static void +png_write_destroy(png_structrp png_ptr) { - png_structp png_ptr = NULL; - png_infop info_ptr = NULL; -#ifdef PNG_USER_MEM_SUPPORTED - png_free_ptr free_fn = NULL; - png_voidp mem_ptr = NULL; -#endif + png_debug(1, "in png_write_destroy"); - png_debug(1, "in png_destroy_write_struct\n"); - if (png_ptr_ptr != NULL) - { - png_ptr = *png_ptr_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - free_fn = png_ptr->free_fn; - mem_ptr = png_ptr->mem_ptr; -#endif - } + /* Free any memory zlib uses */ + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + deflateEnd(&png_ptr->zstream); - if (info_ptr_ptr != NULL) - info_ptr = *info_ptr_ptr; - - if (info_ptr != NULL) - { - png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); - -#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) - if (png_ptr->num_chunk_list) - { - png_free(png_ptr, png_ptr->chunk_list); - png_ptr->chunk_list=NULL; - png_ptr->num_chunk_list=0; - } -#endif - -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, - (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)info_ptr); -#endif - *info_ptr_ptr = NULL; - } - - if (png_ptr != NULL) - { - png_write_destroy(png_ptr); -#ifdef PNG_USER_MEM_SUPPORTED - png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, - (png_voidp)mem_ptr); -#else - png_destroy_struct((png_voidp)png_ptr); -#endif - *png_ptr_ptr = NULL; - } -} - - -/* Free any memory used in png_ptr struct (old method) */ -void /* PRIVATE */ -png_write_destroy(png_structp png_ptr) -{ -#ifdef PNG_SETJMP_SUPPORTED - jmp_buf tmp_jmp; /* save jump buffer */ -#endif - png_error_ptr error_fn; - png_error_ptr warning_fn; - png_voidp error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - png_free_ptr free_fn; -#endif - - png_debug(1, "in png_write_destroy\n"); - /* free any memory zlib uses */ - deflateEnd(&png_ptr->zstream); - - /* free our memory. png_free checks NULL for us. */ - png_free(png_ptr, png_ptr->zbuf); + /* Free our memory. png_free checks NULL for us. */ + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list); png_free(png_ptr, png_ptr->row_buf); +#ifdef PNG_WRITE_FILTER_SUPPORTED png_free(png_ptr, png_ptr->prev_row); png_free(png_ptr, png_ptr->sub_row); png_free(png_ptr, png_ptr->up_row); png_free(png_ptr, png_ptr->avg_row); png_free(png_ptr, png_ptr->paeth_row); - -#if defined(PNG_TIME_RFC1123_SUPPORTED) - png_free(png_ptr, png_ptr->time_buffer); #endif -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) - png_free(png_ptr, png_ptr->prev_filters); - png_free(png_ptr, png_ptr->filter_weights); - png_free(png_ptr, png_ptr->inv_filter_weights); +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + /* Use this to save a little code space, it doesn't free the filter_costs */ + png_reset_filter_heuristics(png_ptr); png_free(png_ptr, png_ptr->filter_costs); png_free(png_ptr, png_ptr->inv_filter_costs); #endif -#ifdef PNG_SETJMP_SUPPORTED - /* reset structure */ - png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#ifdef PNG_SET_UNKNOWN_CHUNKS_SUPPORTED + png_free(png_ptr, png_ptr->chunk_list); #endif - error_fn = png_ptr->error_fn; - warning_fn = png_ptr->warning_fn; - error_ptr = png_ptr->error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - free_fn = png_ptr->free_fn; -#endif + /* The error handling and memory handling information is left intact at this + * point: the jmp_buf may still have to be freed. See png_destroy_png_struct + * for how this happens. + */ +} - png_memset(png_ptr, 0, png_sizeof (png_struct)); +/* Free all memory used by the write. + * In libpng 1.6.0 this API changed quietly to no longer accept a NULL value for + * *png_ptr_ptr. Prior to 1.6.0 it would accept such a value and it would free + * the passed in info_structs but it would quietly fail to free any of the data + * inside them. In 1.6.0 it quietly does nothing (it has to be quiet because it + * has no png_ptr.) + */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_debug(1, "in png_destroy_write_struct"); - png_ptr->error_fn = error_fn; - png_ptr->warning_fn = warning_fn; - png_ptr->error_ptr = error_ptr; -#ifdef PNG_USER_MEM_SUPPORTED - png_ptr->free_fn = free_fn; -#endif + if (png_ptr_ptr != NULL) + { + png_structrp png_ptr = *png_ptr_ptr; -#ifdef PNG_SETJMP_SUPPORTED - png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); -#endif + if (png_ptr != NULL) /* added in libpng 1.6.0 */ + { + png_destroy_info_struct(png_ptr, info_ptr_ptr); + + *png_ptr_ptr = NULL; + png_write_destroy(png_ptr); + png_destroy_png_struct(png_ptr); + } + } } /* Allow the application to select one or more row filters to use. */ void PNGAPI -png_set_filter(png_structp png_ptr, int method, int filters) +png_set_filter(png_structrp png_ptr, int method, int filters) { - png_debug(1, "in png_set_filter\n"); + png_debug(1, "in png_set_filter"); + if (png_ptr == NULL) return; -#if defined(PNG_MNG_FEATURES_SUPPORTED) - if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - (method == PNG_INTRAPIXEL_DIFFERENCING)) - method = PNG_FILTER_TYPE_BASE; + +#ifdef PNG_MNG_FEATURES_SUPPORTED + if ((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; + #endif if (method == PNG_FILTER_TYPE_BASE) { switch (filters & (PNG_ALL_FILTERS | 0x07)) { -#ifndef PNG_NO_WRITE_FILTER +#ifdef PNG_WRITE_FILTER_SUPPORTED case 5: case 6: - case 7: png_warning(png_ptr, "Unknown row filter for method 0"); -#endif /* PNG_NO_WRITE_FILTER */ + case 7: png_app_error(png_ptr, "Unknown row filter for method 0"); + /* FALL THROUGH */ +#endif /* PNG_WRITE_FILTER_SUPPORTED */ case PNG_FILTER_VALUE_NONE: - png_ptr->do_filter=PNG_FILTER_NONE; break; -#ifndef PNG_NO_WRITE_FILTER + png_ptr->do_filter = PNG_FILTER_NONE; break; + +#ifdef PNG_WRITE_FILTER_SUPPORTED case PNG_FILTER_VALUE_SUB: - png_ptr->do_filter=PNG_FILTER_SUB; break; + png_ptr->do_filter = PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: - png_ptr->do_filter=PNG_FILTER_UP; break; + png_ptr->do_filter = PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: - png_ptr->do_filter=PNG_FILTER_AVG; break; + png_ptr->do_filter = PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: - png_ptr->do_filter=PNG_FILTER_PAETH; break; - default: png_ptr->do_filter = (png_byte)filters; break; + png_ptr->do_filter = PNG_FILTER_PAETH; break; + + default: + png_ptr->do_filter = (png_byte)filters; break; #else - default: png_warning(png_ptr, "Unknown row filter for method 0"); -#endif /* PNG_NO_WRITE_FILTER */ + default: + png_app_error(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_WRITE_FILTER_SUPPORTED */ } /* If we have allocated the row_buf, this means we have already started @@ -1153,11 +977,11 @@ png_set_filter(png_structp png_ptr, int method, int filters) */ if (png_ptr->row_buf != NULL) { -#ifndef PNG_NO_WRITE_FILTER +#ifdef PNG_WRITE_FILTER_SUPPORTED if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) { png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + (png_ptr->rowbytes + 1)); png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; } @@ -1166,12 +990,14 @@ png_set_filter(png_structp png_ptr, int method, int filters) if (png_ptr->prev_row == NULL) { png_warning(png_ptr, "Can't add Up filter after starting"); - png_ptr->do_filter &= ~PNG_FILTER_UP; + png_ptr->do_filter = (png_byte)(png_ptr->do_filter & + ~PNG_FILTER_UP); } + else { png_ptr->up_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + (png_ptr->rowbytes + 1)); png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; } } @@ -1181,12 +1007,14 @@ png_set_filter(png_structp png_ptr, int method, int filters) if (png_ptr->prev_row == NULL) { png_warning(png_ptr, "Can't add Average filter after starting"); - png_ptr->do_filter &= ~PNG_FILTER_AVG; + png_ptr->do_filter = (png_byte)(png_ptr->do_filter & + ~PNG_FILTER_AVG); } + else { png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + (png_ptr->rowbytes + 1)); png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; } } @@ -1199,16 +1027,17 @@ png_set_filter(png_structp png_ptr, int method, int filters) png_warning(png_ptr, "Can't add Paeth filter after starting"); png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); } + else { png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + (png_ptr->rowbytes + 1)); png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; } } if (png_ptr->do_filter == PNG_NO_FILTERS) -#endif /* PNG_NO_WRITE_FILTER */ +#endif /* PNG_WRITE_FILTER_SUPPORTED */ png_ptr->do_filter = PNG_FILTER_NONE; } } @@ -1223,292 +1052,1280 @@ png_set_filter(png_structp png_ptr, int method, int filters) * filtered data going to zlib more consistent, hopefully resulting in * better compression. */ -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */ -void PNGAPI -png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, - int num_weights, png_doublep filter_weights, - png_doublep filter_costs) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* GRR 970116 */ +/* Convenience reset API. */ +static void +png_reset_filter_heuristics(png_structrp png_ptr) { - int i; + /* Clear out any old values in the 'weights' - this must be done because if + * the app calls set_filter_heuristics multiple times with different + * 'num_weights' values we would otherwise potentially have wrong sized + * arrays. + */ + png_ptr->num_prev_filters = 0; + png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + if (png_ptr->prev_filters != NULL) + { + png_bytep old = png_ptr->prev_filters; + png_ptr->prev_filters = NULL; + png_free(png_ptr, old); + } + if (png_ptr->filter_weights != NULL) + { + png_uint_16p old = png_ptr->filter_weights; + png_ptr->filter_weights = NULL; + png_free(png_ptr, old); + } - png_debug(1, "in png_set_filter_heuristics\n"); + if (png_ptr->inv_filter_weights != NULL) + { + png_uint_16p old = png_ptr->inv_filter_weights; + png_ptr->inv_filter_weights = NULL; + png_free(png_ptr, old); + } + + /* Leave the filter_costs - this array is fixed size. */ +} + +static int +png_init_filter_heuristics(png_structrp png_ptr, int heuristic_method, + int num_weights) +{ if (png_ptr == NULL) - return; - if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) - { - png_warning(png_ptr, "Unknown filter heuristic method"); - return; - } + return 0; - if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) - { - heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; - } + /* Clear out the arrays */ + png_reset_filter_heuristics(png_ptr); - if (num_weights < 0 || filter_weights == NULL || - heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + /* Check arguments; the 'reset' function makes the correct settings for the + * unweighted case, but we must handle the weight case by initializing the + * arrays for the caller. + */ + if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { - num_weights = 0; - } + int i; - png_ptr->num_prev_filters = (png_byte)num_weights; - png_ptr->heuristic_method = (png_byte)heuristic_method; - - if (num_weights > 0) - { - if (png_ptr->prev_filters == NULL) + if (num_weights > 0) { png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, - (png_uint_32)(png_sizeof(png_byte) * num_weights)); + (png_uint_32)((sizeof (png_byte)) * num_weights)); /* To make sure that the weighting starts out fairly */ for (i = 0; i < num_weights; i++) { png_ptr->prev_filters[i] = 255; } - } - if (png_ptr->filter_weights == NULL) - { png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + (png_uint_32)((sizeof (png_uint_16)) * num_weights)); png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + (png_uint_32)((sizeof (png_uint_16)) * num_weights)); + for (i = 0; i < num_weights; i++) { png_ptr->inv_filter_weights[i] = png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; } + + /* Safe to set this now */ + png_ptr->num_prev_filters = (png_byte)num_weights; } - for (i = 0; i < num_weights; i++) + /* 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) { - if (filter_weights[i] < 0.0) - { - png_ptr->inv_filter_weights[i] = - png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; - } - else - { - png_ptr->inv_filter_weights[i] = - (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); - png_ptr->filter_weights[i] = - (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); - } + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)((sizeof (png_uint_16)) * PNG_FILTER_VALUE_LAST)); } - } - - /* If, in the future, there are other filter methods, this would - * need to be based on png_ptr->filter. - */ - if (png_ptr->filter_costs == NULL) - { - png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); - - png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, - (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) { png_ptr->inv_filter_costs[i] = png_ptr->filter_costs[i] = PNG_COST_FACTOR; } - } - /* Here is where we set the relative costs of the different filters. We - * should take the desired compression level into account when setting - * the costs, so that Paeth, for instance, has a high relative cost at low - * compression levels, while it has a lower relative cost at higher - * compression settings. The filter types are in order of increasing - * relative cost, so it would be possible to do this with an algorithm. - */ - for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + /* All the arrays are inited, safe to set this: */ + png_ptr->heuristic_method = PNG_FILTER_HEURISTIC_WEIGHTED; + + /* Return the 'ok' code. */ + return 1; + } + else if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) { - if (filter_costs == NULL || filter_costs[i] < 0.0) + return 1; + } + else + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return 0; + } +} + +/* Provide floating and fixed point APIs */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_filter_heuristics(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_doublep filter_weights, + png_const_doublep filter_costs) +{ + png_debug(1, "in png_set_filter_heuristics"); + + /* The internal API allocates all the arrays and ensures that the elements of + * those arrays are set to the default value. + */ + if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) + return; + + /* If using the weighted method copy in the weights. */ + if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int i; + for (i = 0; i < num_weights; i++) { - png_ptr->inv_filter_costs[i] = - png_ptr->filter_costs[i] = PNG_COST_FACTOR; + if (filter_weights[i] <= 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)(PNG_WEIGHT_FACTOR*filter_weights[i]+.5); + + png_ptr->filter_weights[i] = + (png_uint_16)(PNG_WEIGHT_FACTOR/filter_weights[i]+.5); + } } - else if (filter_costs[i] >= 1.0) + + /* 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[i] >= 1.0) { png_ptr->inv_filter_costs[i] = - (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + (png_uint_16)(PNG_COST_FACTOR / filter_costs[i] + .5); + png_ptr->filter_costs[i] = - (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + (png_uint_16)(PNG_COST_FACTOR * filter_costs[i] + .5); } } } +#endif /* FLOATING_POINT */ + +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_filter_heuristics_fixed(png_structrp png_ptr, int heuristic_method, + int num_weights, png_const_fixed_point_p filter_weights, + png_const_fixed_point_p filter_costs) +{ + png_debug(1, "in png_set_filter_heuristics_fixed"); + + /* The internal API allocates all the arrays and ensures that the elements of + * those arrays are set to the default value. + */ + if (!png_init_filter_heuristics(png_ptr, heuristic_method, num_weights)) + return; + + /* If using the weighted method copy in the weights. */ + if (heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int i; + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] <= 0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + + else + { + png_ptr->inv_filter_weights[i] = (png_uint_16) + ((PNG_WEIGHT_FACTOR*filter_weights[i]+PNG_FP_HALF)/PNG_FP_1); + + png_ptr->filter_weights[i] = (png_uint_16)((PNG_WEIGHT_FACTOR* + PNG_FP_1+(filter_weights[i]/2))/filter_weights[i]); + } + } + + /* 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[i] >= PNG_FP_1) + { + png_uint_32 tmp; + + /* Use a 32 bit unsigned temporary here because otherwise the + * intermediate value will be a 32 bit *signed* integer (ANSI rules) + * and this will get the wrong answer on division. + */ + tmp = PNG_COST_FACTOR*PNG_FP_1 + (filter_costs[i]/2); + tmp /= filter_costs[i]; + + png_ptr->inv_filter_costs[i] = (png_uint_16)tmp; + + tmp = PNG_COST_FACTOR * filter_costs[i] + PNG_FP_HALF; + tmp /= PNG_FP_1; + + png_ptr->filter_costs[i] = (png_uint_16)tmp; + } + } +} +#endif /* FIXED_POINT */ #endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ void PNGAPI -png_set_compression_level(png_structp png_ptr, int level) +png_set_compression_level(png_structrp png_ptr, int level) { - png_debug(1, "in png_set_compression_level\n"); + png_debug(1, "in png_set_compression_level"); + if (png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; } void PNGAPI -png_set_compression_mem_level(png_structp png_ptr, int mem_level) +png_set_compression_mem_level(png_structrp png_ptr, int mem_level) { - png_debug(1, "in png_set_compression_mem_level\n"); + png_debug(1, "in png_set_compression_mem_level"); + if (png_ptr == NULL) return; - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; } void PNGAPI -png_set_compression_strategy(png_structp png_ptr, int strategy) +png_set_compression_strategy(png_structrp png_ptr, int strategy) { - png_debug(1, "in png_set_compression_strategy\n"); + png_debug(1, "in png_set_compression_strategy"); + if (png_ptr == NULL) return; + + /* The flag setting here prevents the libpng dynamic selection of strategy. + */ png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; png_ptr->zlib_strategy = strategy; } +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ void PNGAPI -png_set_compression_window_bits(png_structp png_ptr, int window_bits) +png_set_compression_window_bits(png_structrp png_ptr, int window_bits) { if (png_ptr == NULL) return; + + /* Prior to 1.6.0 this would warn but then set the window_bits value, this + * meant that negative window bits values could be selected which would cause + * libpng to write a non-standard PNG file with raw deflate or gzip + * compressed IDAT or ancillary chunks. Such files can be read and there is + * no warning on read, so this seems like a very bad idea. + */ if (window_bits > 15) + { png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + else if (window_bits < 8) + { png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); -#ifndef WBITS_8_OK - /* avoid libpng bug with 256-byte windows */ - if (window_bits == 8) - { - png_warning(png_ptr, "Compression window is being reset to 512"); - window_bits=9; - } -#endif - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + window_bits = 8; + } + png_ptr->zlib_window_bits = window_bits; } void PNGAPI -png_set_compression_method(png_structp png_ptr, int method) +png_set_compression_method(png_structrp png_ptr, int method) { - png_debug(1, "in png_set_compression_method\n"); + png_debug(1, "in png_set_compression_method"); + if (png_ptr == NULL) return; + + /* This would produce an invalid PNG file if it worked, but it doesn't and + * deflate will fault it, so it is harmless to just warn here. + */ if (method != 8) png_warning(png_ptr, "Only compression method 8 is supported by PNG"); - png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; } +/* The following were added to libpng-1.5.4 */ +#ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED void PNGAPI -png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +png_set_text_compression_level(png_structrp png_ptr, int level) +{ + png_debug(1, "in png_set_text_compression_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_level = level; +} + +void PNGAPI +png_set_text_compression_mem_level(png_structrp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_text_compression_mem_level"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_mem_level = mem_level; +} + +void PNGAPI +png_set_text_compression_strategy(png_structrp png_ptr, int strategy) +{ + png_debug(1, "in png_set_text_compression_strategy"); + + if (png_ptr == NULL) + return; + + png_ptr->zlib_text_strategy = strategy; +} + +/* If PNG_WRITE_OPTIMIZE_CMF_SUPPORTED is defined, libpng will use a + * smaller value of window_bits if it can do so safely. + */ +void PNGAPI +png_set_text_compression_window_bits(png_structrp png_ptr, int window_bits) { if (png_ptr == NULL) return; + + if (window_bits > 15) + { + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + window_bits = 15; + } + + else if (window_bits < 8) + { + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); + window_bits = 8; + } + + png_ptr->zlib_text_window_bits = window_bits; +} + +void PNGAPI +png_set_text_compression_method(png_structrp png_ptr, int method) +{ + png_debug(1, "in png_set_text_compression_method"); + + if (png_ptr == NULL) + return; + + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + + png_ptr->zlib_text_method = method; +} +#endif /* PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED */ +/* end of API added to libpng-1.5.4 */ + +void PNGAPI +png_set_write_status_fn(png_structrp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; } -#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED void PNGAPI -png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr - write_user_transform_fn) +png_set_write_user_transform_fn(png_structrp png_ptr, png_user_transform_ptr + write_user_transform_fn) { - png_debug(1, "in png_set_write_user_transform_fn\n"); + png_debug(1, "in png_set_write_user_transform_fn"); + if (png_ptr == NULL) return; + png_ptr->transformations |= PNG_USER_TRANSFORM; png_ptr->write_user_transform_fn = write_user_transform_fn; } #endif -#if defined(PNG_INFO_IMAGE_SUPPORTED) +#ifdef PNG_INFO_IMAGE_SUPPORTED void PNGAPI -png_write_png(png_structp png_ptr, png_infop info_ptr, - int transforms, voidp params) +png_write_png(png_structrp png_ptr, png_inforp info_ptr, + int transforms, voidp params) { if (png_ptr == NULL || info_ptr == NULL) return; -#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) - /* invert the alpha channel from opacity to transparency */ - if (transforms & PNG_TRANSFORM_INVERT_ALPHA) - png_set_invert_alpha(png_ptr); -#endif /* Write the file header information. */ png_write_info(png_ptr, info_ptr); /* ------ these transformations don't touch the info structure ------- */ -#if defined(PNG_WRITE_INVERT_SUPPORTED) - /* invert monochrome pixels */ +#ifdef PNG_WRITE_INVERT_SUPPORTED + /* Invert monochrome pixels */ if (transforms & PNG_TRANSFORM_INVERT_MONO) - png_set_invert_mono(png_ptr); + png_set_invert_mono(png_ptr); #endif -#if defined(PNG_WRITE_SHIFT_SUPPORTED) +#ifdef PNG_WRITE_SHIFT_SUPPORTED /* Shift the pixels up to a legal bit depth and fill in * as appropriate to correctly scale the image. */ if ((transforms & PNG_TRANSFORM_SHIFT) - && (info_ptr->valid & PNG_INFO_sBIT)) - png_set_shift(png_ptr, &info_ptr->sig_bit); + && (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 */ +#ifdef 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 */ +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED + /* Swap location of alpha bytes from ARGB to RGBA */ if (transforms & PNG_TRANSFORM_SWAP_ALPHA) - png_set_swap_alpha(png_ptr); + 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); +#ifdef PNG_WRITE_FILLER_SUPPORTED + /* Pack XRGB/RGBX/ARGB/RGBA into RGB (4 channels -> 3 channels) */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER_AFTER) + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + + else if (transforms & PNG_TRANSFORM_STRIP_FILLER_BEFORE) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); #endif -#if defined(PNG_WRITE_BGR_SUPPORTED) - /* flip BGR pixels to RGB */ +#ifdef PNG_WRITE_BGR_SUPPORTED + /* Flip BGR pixels to RGB */ if (transforms & PNG_TRANSFORM_BGR) - png_set_bgr(png_ptr); + png_set_bgr(png_ptr); #endif -#if defined(PNG_WRITE_SWAP_SUPPORTED) - /* swap bytes of 16-bit files to most significant byte first */ +#ifdef PNG_WRITE_SWAP_SUPPORTED + /* Swap bytes of 16-bit files to most significant byte first */ if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) - png_set_swap(png_ptr); + png_set_swap(png_ptr); #endif -#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) - /* swap bits of 1, 2, 4 bit packed pixel formats */ +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED + /* Swap bits of 1, 2, 4 bit packed pixel formats */ if (transforms & PNG_TRANSFORM_PACKSWAP) - png_set_packswap(png_ptr); + png_set_packswap(png_ptr); +#endif + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED + /* Invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); #endif /* ----------------------- end of transformations ------------------- */ - /* write the bits */ + /* Write the bits */ if (info_ptr->valid & PNG_INFO_IDAT) png_write_image(png_ptr, info_ptr->row_pointers); /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); - transforms = transforms; /* quiet compiler warnings */ - params = params; + PNG_UNUSED(transforms) /* Quiet compiler warnings */ + PNG_UNUSED(params) } #endif + + +#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED +#ifdef PNG_STDIO_SUPPORTED /* currently required for png_image_write_* */ +/* Initialize the write structure - general purpose utility. */ +static int +png_image_write_init(png_imagep image) +{ + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, image, + png_safe_error, png_safe_warning); + + if (png_ptr != NULL) + { + png_infop info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr != NULL) + { + png_controlp control = png_voidcast(png_controlp, + png_malloc_warn(png_ptr, (sizeof *control))); + + if (control != NULL) + { + memset(control, 0, (sizeof *control)); + + control->png_ptr = png_ptr; + control->info_ptr = info_ptr; + control->for_write = 1; + + image->opaque = control; + return 1; + } + + /* Error clean up */ + png_destroy_info_struct(png_ptr, &info_ptr); + } + + png_destroy_write_struct(&png_ptr, NULL); + } + + return png_image_error(image, "png_image_write_: out of memory"); +} + +/* Arguments to png_image_write_main: */ +typedef struct +{ + /* Arguments: */ + png_imagep image; + png_const_voidp buffer; + png_int_32 row_stride; + png_const_voidp colormap; + int convert_to_8bit; + /* Local variables: */ + png_const_voidp first_row; + ptrdiff_t row_bytes; + png_voidp local_row; +} png_image_write_control; + +/* Write png_uint_16 input to a 16-bit PNG; the png_ptr has already been set to + * do any necessary byte swapping. The component order is defined by the + * png_image format value. + */ +static int +png_write_image_16bit(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, + display->first_row); + png_uint_16p output_row = png_voidcast(png_uint_16p, display->local_row); + png_uint_16p row_end; + const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + int aindex = 0; + png_uint_32 y = image->height; + + if (image->format & PNG_FORMAT_FLAG_ALPHA) + { + if (image->format & PNG_FORMAT_FLAG_AFIRST) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + + else + aindex = channels; + } + + else + png_error(png_ptr, "png_write_image: internal call error"); + + /* Work out the output row end and count over this, note that the increment + * above to 'row' means that row_end can actually be beyond the end of the + * row; this is correct. + */ + row_end = output_row + image->width * (channels+1); + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_uint_16p out_ptr = output_row; + + while (out_ptr < row_end) + { + const png_uint_16 alpha = in_ptr[aindex]; + png_uint_32 reciprocal = 0; + int c; + + out_ptr[aindex] = alpha; + + /* Calculate a reciprocal. The correct calculation is simply + * component/alpha*65535 << 15. (I.e. 15 bits of precision); this + * allows correct rounding by adding .5 before the shift. 'reciprocal' + * is only initialized when required. + */ + if (alpha > 0 && alpha < 65535) + reciprocal = ((0xffff<<15)+(alpha>>1))/alpha; + + c = channels; + do /* always at least one channel */ + { + png_uint_16 component = *in_ptr++; + + /* The following gives 65535 for an alpha of 0, which is fine, + * otherwise if 0/0 is represented as some other value there is more + * likely to be a discontinuity which will probably damage + * compression when moving from a fully transparent area to a + * nearly transparent one. (The assumption here is that opaque + * areas tend not to be 0 intensity.) + */ + if (component >= alpha) + component = 65535; + + /* component 0 && alpha < 65535) + { + png_uint_32 calc = component * reciprocal; + calc += 16384; /* round to nearest */ + component = (png_uint_16)(calc >> 15); + } + + *out_ptr++ = component; + } + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } + + png_write_row(png_ptr, png_voidcast(png_const_bytep, display->local_row)); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } + + return 1; +} + +/* Given 16-bit input (1 to 4 channels) write 8-bit output. If an alpha channel + * is present it must be removed from the components, the components are then + * written in sRGB encoding. No components are added or removed. + * + * Calculate an alpha reciprocal to reverse pre-multiplication. As above the + * calculation can be done to 15 bits of accuracy; however, the output needs to + * be scaled in the range 0..255*65535, so include that scaling here. + */ +#define UNP_RECIPROCAL(alpha) ((((0xffff*0xff)<<7)+(alpha>>1))/alpha) + +static png_byte +png_unpremultiply(png_uint_32 component, png_uint_32 alpha, + png_uint_32 reciprocal/*from the above macro*/) +{ + /* The following gives 1.0 for an alpha of 0, which is fine, otherwise if 0/0 + * is represented as some other value there is more likely to be a + * discontinuity which will probably damage compression when moving from a + * fully transparent area to a nearly transparent one. (The assumption here + * is that opaque areas tend not to be 0 intensity.) + * + * There is a rounding problem here; if alpha is less than 128 it will end up + * as 0 when scaled to 8 bits. To avoid introducing spurious colors into the + * output change for this too. + */ + if (component >= alpha || alpha < 128) + return 255; + + /* component 0) + { + /* The test is that alpha/257 (rounded) is less than 255, the first value + * that becomes 255 is 65407. + * NOTE: this must agree with the PNG_DIV257 macro (which must, therefore, + * be exact!) [Could also test reciprocal != 0] + */ + if (alpha < 65407) + { + component *= reciprocal; + component += 64; /* round to nearest */ + component >>= 7; + } + + else + component *= 255; + + /* Convert the component to sRGB. */ + return (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + else + return 0; +} + +static int +png_write_image_8bit(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + + png_const_uint_16p input_row = png_voidcast(png_const_uint_16p, + display->first_row); + png_bytep output_row = png_voidcast(png_bytep, display->local_row); + png_uint_32 y = image->height; + const int channels = (image->format & PNG_FORMAT_FLAG_COLOR) ? 3 : 1; + + if (image->format & PNG_FORMAT_FLAG_ALPHA) + { + png_bytep row_end; + int aindex; + + if (image->format & PNG_FORMAT_FLAG_AFIRST) + { + aindex = -1; + ++input_row; /* To point to the first component */ + ++output_row; + } + + else + aindex = channels; + + /* Use row_end in place of a loop counter: */ + row_end = output_row + image->width * (channels+1); + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_16 alpha = in_ptr[aindex]; + png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_uint_32 reciprocal = 0; + int c; + + /* Scale and write the alpha channel. */ + out_ptr[aindex] = alphabyte; + + if (alphabyte > 0 && alphabyte < 255) + reciprocal = UNP_RECIPROCAL(alpha); + + c = channels; + do /* always at least one channel */ + *out_ptr++ = png_unpremultiply(*in_ptr++, alpha, reciprocal); + while (--c > 0); + + /* Skip to next component (skip the intervening alpha channel) */ + ++in_ptr; + ++out_ptr; + } /* while out_ptr < row_end */ + + png_write_row(png_ptr, png_voidcast(png_const_bytep, + display->local_row)); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } /* while y */ + } + + else + { + /* No alpha channel, so the row_end really is the end of the row and it + * is sufficient to loop over the components one by one. + */ + png_bytep row_end = output_row + image->width * channels; + + while (y-- > 0) + { + png_const_uint_16p in_ptr = input_row; + png_bytep out_ptr = output_row; + + while (out_ptr < row_end) + { + png_uint_32 component = *in_ptr++; + + component *= 255; + *out_ptr++ = (png_byte)PNG_sRGB_FROM_LINEAR(component); + } + + png_write_row(png_ptr, output_row); + input_row += display->row_bytes/(sizeof (png_uint_16)); + } + } + + return 1; +} + +static void +png_image_set_PLTE(png_image_write_control *display) +{ + const png_imagep image = display->image; + const void *cmap = display->colormap; + const int entries = image->colormap_entries > 256 ? 256 : + (int)image->colormap_entries; + + /* NOTE: the caller must check for cmap != NULL and entries != 0 */ + const png_uint_32 format = image->format; + const int channels = PNG_IMAGE_SAMPLE_CHANNELS(format); + +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int afirst = (format & PNG_FORMAT_FLAG_AFIRST) != 0 && + (format & PNG_FORMAT_FLAG_ALPHA) != 0; +# else +# define afirst 0 +# endif + +# ifdef PNG_FORMAT_BGR_SUPPORTED + const int bgr = (format & PNG_FORMAT_FLAG_BGR) ? 2 : 0; +# else +# define bgr 0 +# endif + + int i, num_trans; + png_color palette[256]; + png_byte tRNS[256]; + + memset(tRNS, 255, (sizeof tRNS)); + memset(palette, 0, (sizeof palette)); + + for (i=num_trans=0; i= 3) /* RGB */ + { + palette[i].blue = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[(2 ^ bgr)]); + palette[i].green = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[1]); + palette[i].red = (png_byte)PNG_sRGB_FROM_LINEAR(255 * + entry[bgr]); + } + + else /* Gray */ + palette[i].blue = palette[i].red = palette[i].green = + (png_byte)PNG_sRGB_FROM_LINEAR(255 * *entry); + } + + else /* alpha */ + { + png_uint_16 alpha = entry[afirst ? 0 : channels-1]; + png_byte alphabyte = (png_byte)PNG_DIV257(alpha); + png_uint_32 reciprocal = 0; + + /* Calculate a reciprocal, as in the png_write_image_8bit code above + * this is designed to produce a value scaled to 255*65535 when + * divided by 128 (i.e. asr 7). + */ + if (alphabyte > 0 && alphabyte < 255) + reciprocal = (((0xffff*0xff)<<7)+(alpha>>1))/alpha; + + tRNS[i] = alphabyte; + if (alphabyte < 255) + num_trans = i+1; + + if (channels >= 3) /* RGB */ + { + palette[i].blue = png_unpremultiply(entry[afirst + (2 ^ bgr)], + alpha, reciprocal); + palette[i].green = png_unpremultiply(entry[afirst + 1], alpha, + reciprocal); + palette[i].red = png_unpremultiply(entry[afirst + bgr], alpha, + reciprocal); + } + + else /* gray */ + palette[i].blue = palette[i].red = palette[i].green = + png_unpremultiply(entry[afirst], alpha, reciprocal); + } + } + + else /* Color-map has sRGB values */ + { + png_const_bytep entry = png_voidcast(png_const_bytep, cmap); + + entry += i * channels; + + switch (channels) + { + case 4: + tRNS[i] = entry[afirst ? 0 : 3]; + if (tRNS[i] < 255) + num_trans = i+1; + /* FALL THROUGH */ + case 3: + palette[i].blue = entry[afirst + (2 ^ bgr)]; + palette[i].green = entry[afirst + 1]; + palette[i].red = entry[afirst + bgr]; + break; + + case 2: + tRNS[i] = entry[1 ^ afirst]; + if (tRNS[i] < 255) + num_trans = i+1; + /* FALL THROUGH */ + case 1: + palette[i].blue = palette[i].red = palette[i].green = + entry[afirst]; + break; + + default: + break; + } + } + } + +# ifdef afirst +# undef afirst +# endif +# ifdef bgr +# undef bgr +# endif + + png_set_PLTE(image->opaque->png_ptr, image->opaque->info_ptr, palette, + entries); + + if (num_trans > 0) + png_set_tRNS(image->opaque->png_ptr, image->opaque->info_ptr, tRNS, + num_trans, NULL); + + image->colormap_entries = entries; +} + +static int +png_image_write_main(png_voidp argument) +{ + png_image_write_control *display = png_voidcast(png_image_write_control*, + argument); + png_imagep image = display->image; + png_structrp png_ptr = image->opaque->png_ptr; + png_inforp info_ptr = image->opaque->info_ptr; + png_uint_32 format = image->format; + + int colormap = (format & PNG_FORMAT_FLAG_COLORMAP) != 0; + int linear = !colormap && (format & PNG_FORMAT_FLAG_LINEAR) != 0; /* input */ + int alpha = !colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0; + int write_16bit = linear && !colormap && !display->convert_to_8bit; + +# ifdef PNG_BENIGN_ERRORS_SUPPORTED + /* Make sure we error out on any bad situation */ + png_set_benign_errors(png_ptr, 0/*error*/); +# endif + + /* Default the 'row_stride' parameter if required. */ + if (display->row_stride == 0) + display->row_stride = PNG_IMAGE_ROW_STRIDE(*image); + + /* Set the required transforms then write the rows in the correct order. */ + if (format & PNG_FORMAT_FLAG_COLORMAP) + { + if (display->colormap != NULL && image->colormap_entries > 0) + { + png_uint_32 entries = image->colormap_entries; + + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + entries > 16 ? 8 : (entries > 4 ? 4 : (entries > 2 ? 2 : 1)), + PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + png_image_set_PLTE(display); + } + + else + png_error(image->opaque->png_ptr, + "no color-map for color-mapped image"); + } + + else + png_set_IHDR(png_ptr, info_ptr, image->width, image->height, + write_16bit ? 16 : 8, + ((format & PNG_FORMAT_FLAG_COLOR) ? PNG_COLOR_MASK_COLOR : 0) + + ((format & PNG_FORMAT_FLAG_ALPHA) ? PNG_COLOR_MASK_ALPHA : 0), + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* Counter-intuitively the data transformations must be called *after* + * png_write_info, not before as in the read code, but the 'set' functions + * must still be called before. Just set the color space information, never + * write an interlaced image. + */ + + if (write_16bit) + { + /* The gamma here is 1.0 (linear) and the cHRM chunk matches sRGB. */ + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_LINEAR); + + if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB)) + png_set_cHRM_fixed(png_ptr, info_ptr, + /* color x y */ + /* white */ 31270, 32900, + /* red */ 64000, 33000, + /* green */ 30000, 60000, + /* blue */ 15000, 6000 + ); + } + + else if (!(image->flags & PNG_IMAGE_FLAG_COLORSPACE_NOT_sRGB)) + png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); + + /* Else writing an 8-bit file and the *colors* aren't sRGB, but the 8-bit + * space must still be gamma encoded. + */ + else + png_set_gAMA_fixed(png_ptr, info_ptr, PNG_GAMMA_sRGB_INVERSE); + + /* Write the file header. */ + png_write_info(png_ptr, info_ptr); + + /* Now set up the data transformations (*after* the header is written), + * remove the handled transformations from the 'format' flags for checking. + * + * First check for a little endian system if writing 16 bit files. + */ + if (write_16bit) + { + PNG_CONST png_uint_16 le = 0x0001; + + if (*(png_const_bytep)&le) + png_set_swap(png_ptr); + } + +# ifdef PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED + if (format & PNG_FORMAT_FLAG_BGR) + { + if (!colormap && (format & PNG_FORMAT_FLAG_COLOR) != 0) + png_set_bgr(png_ptr); + format &= ~PNG_FORMAT_FLAG_BGR; + } +# endif + +# ifdef PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED + if (format & PNG_FORMAT_FLAG_AFIRST) + { + if (!colormap && (format & PNG_FORMAT_FLAG_ALPHA) != 0) + png_set_swap_alpha(png_ptr); + format &= ~PNG_FORMAT_FLAG_AFIRST; + } +# endif + + /* If there are 16 or fewer color-map entries we wrote a lower bit depth + * above, but the application data is still byte packed. + */ + if (colormap && image->colormap_entries <= 16) + png_set_packing(png_ptr); + + /* That should have handled all (both) the transforms. */ + if ((format & ~(png_uint_32)(PNG_FORMAT_FLAG_COLOR | PNG_FORMAT_FLAG_LINEAR | + PNG_FORMAT_FLAG_ALPHA | PNG_FORMAT_FLAG_COLORMAP)) != 0) + png_error(png_ptr, "png_write_image: unsupported transformation"); + + { + png_const_bytep row = png_voidcast(png_const_bytep, display->buffer); + ptrdiff_t row_bytes = display->row_stride; + + if (linear) + row_bytes *= (sizeof (png_uint_16)); + + if (row_bytes < 0) + row += (image->height-1) * (-row_bytes); + + display->first_row = row; + display->row_bytes = row_bytes; + } + + /* Apply 'fast' options if the flag is set. */ + if ((image->flags & PNG_IMAGE_FLAG_FAST) != 0) + { + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_NO_FILTERS); + /* NOTE: determined by experiment using pngstest, this reflects some + * balance between the time to write the image once and the time to read + * it about 50 times. The speed-up in pngstest was about 10-20% of the + * total (user) time on a heavily loaded system. + */ + png_set_compression_level(png_ptr, 3); + } + + /* Check for the cases that currently require a pre-transform on the row + * before it is written. This only applies when the input is 16-bit and + * either there is an alpha channel or it is converted to 8-bit. + */ + if ((linear && alpha) || (!colormap && display->convert_to_8bit)) + { + png_bytep row = png_voidcast(png_bytep, png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr))); + int result; + + display->local_row = row; + if (write_16bit) + result = png_safe_execute(image, png_write_image_16bit, display); + else + result = png_safe_execute(image, png_write_image_8bit, display); + display->local_row = NULL; + + png_free(png_ptr, row); + + /* Skip the 'write_end' on error: */ + if (!result) + return 0; + } + + /* Otherwise this is the case where the input is in a format currently + * supported by the rest of the libpng write code; call it directly. + */ + else + { + png_const_bytep row = png_voidcast(png_const_bytep, display->first_row); + ptrdiff_t row_bytes = display->row_bytes; + png_uint_32 y = image->height; + + while (y-- > 0) + { + png_write_row(png_ptr, row); + row += row_bytes; + } + } + + png_write_end(png_ptr, info_ptr); + return 1; +} + +int PNGAPI +png_image_write_to_stdio(png_imagep image, FILE *file, int convert_to_8bit, + const void *buffer, png_int_32 row_stride, const void *colormap) +{ + /* Write the image to the given (FILE*). */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file != NULL) + { + if (png_image_write_init(image)) + { + png_image_write_control display; + int result; + + /* This is slightly evil, but png_init_io doesn't do anything other + * than this and we haven't changed the standard IO functions so + * this saves a 'safe' function. + */ + image->opaque->png_ptr->io_ptr = file; + + memset(&display, 0, (sizeof display)); + display.image = image; + display.buffer = buffer; + display.row_stride = row_stride; + display.colormap = colormap; + display.convert_to_8bit = convert_to_8bit; + + result = png_safe_execute(image, png_image_write_main, &display); + png_image_free(image); + return result; + } + + else + return 0; + } + + else + return png_image_error(image, + "png_image_write_to_stdio: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_stdio: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} + +int PNGAPI +png_image_write_to_file(png_imagep image, const char *file_name, + int convert_to_8bit, const void *buffer, png_int_32 row_stride, + const void *colormap) +{ + /* Write the image to the named file. */ + if (image != NULL && image->version == PNG_IMAGE_VERSION) + { + if (file_name != NULL) + { + FILE *fp = fopen(file_name, "wb"); + + if (fp != NULL) + { + if (png_image_write_to_stdio(image, fp, convert_to_8bit, buffer, + row_stride, colormap)) + { + int error; /* from fflush/fclose */ + + /* Make sure the file is flushed correctly. */ + if (fflush(fp) == 0 && ferror(fp) == 0) + { + if (fclose(fp) == 0) + return 1; + + error = errno; /* from fclose */ + } + + else + { + error = errno; /* from fflush or ferror */ + (void)fclose(fp); + } + + (void)remove(file_name); + /* The image has already been cleaned up; this is just used to + * set the error (because the original write succeeded). + */ + return png_image_error(image, strerror(error)); + } + + else + { + /* Clean up: just the opened file. */ + (void)fclose(fp); + (void)remove(file_name); + return 0; + } + } + + else + return png_image_error(image, strerror(errno)); + } + + else + return png_image_error(image, + "png_image_write_to_file: invalid argument"); + } + + else if (image != NULL) + return png_image_error(image, + "png_image_write_to_file: incorrect PNG_IMAGE_VERSION"); + + else + return 0; +} +#endif /* PNG_STDIO_SUPPORTED */ +#endif /* SIMPLIFIED_WRITE */ #endif /* PNG_WRITE_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwtran.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwtran.c index 19b2968..2cdd7c9 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwtran.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwtran.c @@ -1,84 +1,98 @@ /* 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 + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" + #ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED /* Transform the data according to the user's wishes. The order of * transformations is significant. */ void /* PRIVATE */ -png_do_write_transformations(png_structp png_ptr) +png_do_write_transformations(png_structrp png_ptr, png_row_infop row_info) { - png_debug(1, "in png_do_write_transformations\n"); + png_debug(1, "in png_do_write_transformations"); if (png_ptr == NULL) return; -#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +#ifdef PNG_WRITE_USER_TRANSFORM_SUPPORTED if (png_ptr->transformations & PNG_USER_TRANSFORM) - if(png_ptr->write_user_transform_fn != NULL) - (*(png_ptr->write_user_transform_fn)) /* user write transform function */ - (png_ptr, /* png_ptr */ - &(png_ptr->row_info), /* row_info: */ - /* png_uint_32 width; width of row */ - /* png_uint_32 rowbytes; number of bytes in row */ - /* png_byte color_type; color type of pixels */ - /* png_byte bit_depth; bit depth of samples */ - /* png_byte channels; number of channels (1-4) */ - /* png_byte pixel_depth; bits per pixel (depth*channels) */ - png_ptr->row_buf + 1); /* start of pixel data for row */ + if (png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* User write transform + function */ + (png_ptr, /* png_ptr */ + row_info, /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_size_t 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) + +#ifdef PNG_WRITE_FILLER_SUPPORTED if (png_ptr->transformations & PNG_FILLER) - png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, - png_ptr->flags); + png_do_strip_channel(row_info, png_ptr->row_buf + 1, + !(png_ptr->flags & PNG_FLAG_FILLER_AFTER)); #endif -#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + +#ifdef PNG_WRITE_PACKSWAP_SUPPORTED if (png_ptr->transformations & PNG_PACKSWAP) - png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_packswap(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_WRITE_PACK_SUPPORTED) + +#ifdef PNG_WRITE_PACK_SUPPORTED if (png_ptr->transformations & PNG_PACK) - png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, - (png_uint_32)png_ptr->bit_depth); + png_do_pack(row_info, png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); #endif -#if defined(PNG_WRITE_SWAP_SUPPORTED) + +#ifdef PNG_WRITE_SWAP_SUPPORTED if (png_ptr->transformations & PNG_SWAP_BYTES) - png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_swap(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_WRITE_SHIFT_SUPPORTED) + +#ifdef PNG_WRITE_SHIFT_SUPPORTED if (png_ptr->transformations & PNG_SHIFT) - png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, - &(png_ptr->shift)); + png_do_shift(row_info, png_ptr->row_buf + 1, + &(png_ptr->shift)); #endif -#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_SWAP_ALPHA) - png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_write_swap_alpha(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED if (png_ptr->transformations & PNG_INVERT_ALPHA) - png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_write_invert_alpha(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_WRITE_BGR_SUPPORTED) + +#ifdef PNG_WRITE_BGR_SUPPORTED if (png_ptr->transformations & PNG_BGR) - png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_bgr(row_info, png_ptr->row_buf + 1); #endif -#if defined(PNG_WRITE_INVERT_SUPPORTED) + +#ifdef PNG_WRITE_INVERT_SUPPORTED if (png_ptr->transformations & PNG_INVERT_MONO) - png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); + png_do_invert(row_info, png_ptr->row_buf + 1); #endif } -#if defined(PNG_WRITE_PACK_SUPPORTED) +#ifdef PNG_WRITE_PACK_SUPPORTED /* Pack pixels into bytes. Pass the true bit depth in bit_depth. The * row_info bit depth should be 8 (one pixel per byte). The channels * should be 1 (this only happens on grayscale and paletted images). @@ -86,11 +100,9 @@ png_do_write_transformations(png_structp png_ptr) void /* PRIVATE */ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) { - png_debug(1, "in png_do_pack\n"); + png_debug(1, "in png_do_pack"); + if (row_info->bit_depth == 8 && -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif row_info->channels == 1) { switch ((int)bit_depth) @@ -111,9 +123,12 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) { if (*sp != 0) v |= mask; + sp++; + if (mask > 1) mask >>= 1; + else { mask = 0x80; @@ -122,10 +137,13 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) v = 0; } } + if (mask != 0x80) *dp = (png_byte)v; + break; } + case 2: { png_bytep sp, dp; @@ -137,12 +155,14 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) dp = row; shift = 6; v = 0; + for (i = 0; i < row_width; i++) { png_byte value; value = (png_byte)(*sp & 0x03); v |= (value << shift); + if (shift == 0) { shift = 6; @@ -150,14 +170,19 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) dp++; v = 0; } + else shift -= 2; + sp++; } + if (shift != 6) *dp = (png_byte)v; + break; } + case 4: { png_bytep sp, dp; @@ -169,6 +194,7 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) dp = row; shift = 4; v = 0; + for (i = 0; i < row_width; i++) { png_byte value; @@ -183,25 +209,32 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) dp++; v = 0; } + else shift -= 4; sp++; } + if (shift != 4) *dp = (png_byte)v; + break; } + + default: + break; } + row_info->bit_depth = (png_byte)bit_depth; row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_info->width); + row_info->width); } } #endif -#if defined(PNG_WRITE_SHIFT_SUPPORTED) +#ifdef PNG_WRITE_SHIFT_SUPPORTED /* Shift pixel values to take advantage of whole range. Pass the * true number of bits in bit_depth. The row should be packed * according to row_info->bit_depth. Thus, if you had a row of @@ -210,15 +243,12 @@ png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) * data to 0 to 15. */ void /* PRIVATE */ -png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +png_do_shift(png_row_infop row_info, png_bytep row, + png_const_color_8p bit_depth) { - png_debug(1, "in png_do_shift\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL && -#else - if ( -#endif - row_info->color_type != PNG_COLOR_TYPE_PALETTE) + png_debug(1, "in png_do_shift"); + + if (row_info->color_type != PNG_COLOR_TYPE_PALETTE) { int shift_start[4], shift_dec[4]; int channels = 0; @@ -228,19 +258,23 @@ png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) shift_start[channels] = row_info->bit_depth - bit_depth->red; shift_dec[channels] = bit_depth->red; channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; shift_dec[channels] = bit_depth->green; channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; shift_dec[channels] = bit_depth->blue; channels++; } + else { shift_start[channels] = row_info->bit_depth - bit_depth->gray; shift_dec[channels] = bit_depth->gray; channels++; } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) { shift_start[channels] = row_info->bit_depth - bit_depth->alpha; @@ -248,37 +282,44 @@ 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 */ + /* With low row depths, could only be grayscale, so one channel */ if (row_info->bit_depth < 8) { png_bytep bp = row; - png_uint_32 i; - png_byte mask; - png_uint_32 row_bytes = row_info->rowbytes; + png_size_t i; + unsigned int mask; + png_size_t row_bytes = row_info->rowbytes; if (bit_depth->gray == 1 && row_info->bit_depth == 2) mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) mask = 0x11; + else mask = 0xff; for (i = 0; i < row_bytes; i++, bp++) { - png_uint_16 v; int j; + unsigned int v, out; v = *bp; - *bp = 0; + out = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) { if (j > 0) - *bp |= (png_byte)((v << j) & 0xff); + out |= v << j; + else - *bp |= (png_byte)((v >> (-j)) & mask); + out |= (v >> (-j)) & mask; } + + *bp = (png_byte)(out & 0xff); } } + else if (row_info->bit_depth == 8) { png_bytep bp = row; @@ -288,21 +329,26 @@ png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) for (i = 0; i < istop; i++, bp++) { - png_uint_16 v; + const unsigned int c = i%channels; int j; - int c = (int)(i%channels); + unsigned int v, out; v = *bp; - *bp = 0; + out = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) { if (j > 0) - *bp |= (png_byte)((v << j) & 0xff); + out |= v << j; + else - *bp |= (png_byte)((v >> (-j)) & 0xff); + out |= v >> (-j); } + + *bp = (png_byte)(out & 0xff); } } + else { png_bytep bp; @@ -311,20 +357,22 @@ png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) for (bp = row, i = 0; i < istop; i++) { - int c = (int)(i%channels); - png_uint_16 value, v; + const unsigned int c = i%channels; int j; + unsigned int value, v; - v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + v = png_get_uint_16(bp); value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) { if (j > 0) - value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + value |= v << j; + else - value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + value |= v >> (-j); } - *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)((value >> 8) & 0xff); *bp++ = (png_byte)(value & 0xff); } } @@ -332,23 +380,22 @@ png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) } #endif -#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +#ifdef PNG_WRITE_SWAP_ALPHA_SUPPORTED void /* PRIVATE */ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_write_swap_alpha\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL) -#endif + png_debug(1, "in png_do_write_swap_alpha"); + { if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - /* This converts from ARGB to RGBA */ if (row_info->bit_depth == 8) { + /* This converts from ARGB to RGBA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) { png_byte save = *(sp++); @@ -358,9 +405,11 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) *(dp++) = save; } } - /* This converts from AARRGGBB to RRGGBBAA */ + +#ifdef PNG_WRITE_16BIT_SUPPORTED else { + /* This converts from AARRGGBB to RRGGBBAA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; @@ -380,12 +429,14 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) *(dp++) = save[1]; } } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { - /* This converts from AG to GA */ if (row_info->bit_depth == 8) { + /* This converts from AG to GA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; @@ -397,9 +448,11 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) *(dp++) = save; } } - /* This converts from AAGG to GGAA */ + +#ifdef PNG_WRITE_16BIT_SUPPORTED else { + /* This converts from AAGG to GGAA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; @@ -415,31 +468,31 @@ png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) *(dp++) = save[1]; } } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ } } } #endif -#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +#ifdef PNG_WRITE_INVERT_ALPHA_SUPPORTED void /* PRIVATE */ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_write_invert_alpha\n"); -#if defined(PNG_USELESS_TESTS_SUPPORTED) - if (row != NULL && row_info != NULL) -#endif + png_debug(1, "in png_do_write_invert_alpha"); + { if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) { - /* This inverts the alpha channel in RGBA */ if (row_info->bit_depth == 8) { + /* This inverts the alpha channel in RGBA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) { - /* does nothing + /* Does nothing *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); @@ -448,16 +501,18 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) *(dp++) = (png_byte)(255 - *(sp++)); } } - /* This inverts the alpha channel in RRGGBBAA */ + +#ifdef PNG_WRITE_16BIT_SUPPORTED else { + /* This inverts the alpha channel in RRGGBBAA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { - /* does nothing + /* Does nothing *(dp++) = *(sp++); *(dp++) = *(sp++); *(dp++) = *(sp++); @@ -470,12 +525,14 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) *(dp++) = (png_byte)(255 - *(sp++)); } } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { - /* This inverts the alpha channel in GA */ if (row_info->bit_depth == 8) { + /* This inverts the alpha channel in GA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; @@ -486,16 +543,18 @@ 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 */ + +#ifdef PNG_WRITE_16BIT_SUPPORTED else { + /* This inverts the alpha channel in GGAA */ png_bytep sp, dp; png_uint_32 i; png_uint_32 row_width = row_info->width; for (i = 0, sp = dp = row; i < row_width; i++) { - /* does nothing + /* Does nothing *(dp++) = *(sp++); *(dp++) = *(sp++); */ @@ -504,22 +563,21 @@ png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) *(dp++) = (png_byte)(255 - *(sp++)); } } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ } } } #endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ -#if defined(PNG_MNG_FEATURES_SUPPORTED) -/* undoes intrapixel differencing */ +#ifdef PNG_MNG_FEATURES_SUPPORTED +/* Undoes intrapixel differencing */ void /* PRIVATE */ png_do_write_intrapixel(png_row_infop row_info, png_bytep row) { - png_debug(1, "in png_do_write_intrapixel\n"); - if ( -#if defined(PNG_USELESS_TESTS_SUPPORTED) - row != NULL && row_info != NULL && -#endif - (row_info->color_type & PNG_COLOR_MASK_COLOR)) + png_debug(1, "in png_do_write_intrapixel"); + + if ((row_info->color_type & PNG_COLOR_MASK_COLOR)) { int bytes_per_pixel; png_uint_32 row_width = row_info->width; @@ -530,17 +588,21 @@ png_do_write_intrapixel(png_row_infop row_info, png_bytep row) if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 4; + else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { - *(rp) = (png_byte)((*rp - *(rp+1))&0xff); - *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + *(rp) = (png_byte)((*rp - *(rp + 1)) & 0xff); + *(rp + 2) = (png_byte)((*(rp + 2) - *(rp + 1)) & 0xff); } } + +#ifdef PNG_WRITE_16BIT_SUPPORTED else if (row_info->bit_depth == 16) { png_bytep rp; @@ -548,24 +610,27 @@ png_do_write_intrapixel(png_row_infop row_info, png_bytep row) if (row_info->color_type == PNG_COLOR_TYPE_RGB) bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) bytes_per_pixel = 8; + else return; for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) { - png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); - png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); - png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); - png_uint_32 red = (png_uint_32)((s0-s1) & 0xffffL); - png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL); - *(rp ) = (png_byte)((red >> 8) & 0xff); - *(rp+1) = (png_byte)(red & 0xff); - *(rp+4) = (png_byte)((blue >> 8) & 0xff); - *(rp+5) = (png_byte)(blue & 0xff); + png_uint_32 s0 = (*(rp ) << 8) | *(rp + 1); + png_uint_32 s1 = (*(rp + 2) << 8) | *(rp + 3); + png_uint_32 s2 = (*(rp + 4) << 8) | *(rp + 5); + png_uint_32 red = (png_uint_32)((s0 - s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2 - s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp + 1) = (png_byte)(red & 0xff); + *(rp + 4) = (png_byte)((blue >> 8) & 0xff); + *(rp + 5) = (png_byte)(blue & 0xff); } } +#endif /* PNG_WRITE_16BIT_SUPPORTED */ } } #endif /* PNG_MNG_FEATURES_SUPPORTED */ diff --git a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwutil.c b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwutil.c index 352035d..c90ff8b 100644 --- a/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwutil.c +++ b/JuceLibraryCode/modules/juce_graphics/image_formats/pnglib/pngwutil.c @@ -1,17 +1,21 @@ /* 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 + * Last changed in libpng 1.6.0 [February 14, 2013] + * Copyright (c) 1998-2013 Glenn Randers-Pehrson * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h */ -#define PNG_INTERNAL -#include "png.h" +#include "pngpriv.h" + #ifdef PNG_WRITE_SUPPORTED +#ifdef PNG_WRITE_INT_FUNCTIONS_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. @@ -25,19 +29,6 @@ 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) -{ - buf[0] = (png_byte)((i >> 24) & 0xff); - buf[1] = (png_byte)((i >> 16) & 0xff); - buf[2] = (png_byte)((i >> 8) & 0xff); - buf[3] = (png_byte)(i & 0xff); -} - /* Place a 16-bit number into a buffer in PNG byte order. * The parameter is declared unsigned int, not png_uint_16, * just to avoid potential problems on pre-ANSI C compilers. @@ -48,6 +39,129 @@ png_save_uint_16(png_bytep buf, unsigned int i) buf[0] = (png_byte)((i >> 8) & 0xff); buf[1] = (png_byte)(i & 0xff); } +#endif + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void PNGAPI +png_write_sig(png_structrp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the signature is being written */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_SIGNATURE; +#endif + + /* Write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)(8 - png_ptr->sig_bytes)); + + if (png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +/* Write 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(). + */ +static void +png_write_chunk_header(png_structrp png_ptr, png_uint_32 chunk_name, + png_uint_32 length) +{ + png_byte buf[8]; + +#if defined(PNG_DEBUG) && (PNG_DEBUG > 0) + PNG_CSTRING_FROM_CHUNK(buf, chunk_name); + png_debug2(0, "Writing %s chunk, length = %lu", buf, (unsigned long)length); +#endif + + if (png_ptr == NULL) + return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk header is being written. + * PNG_IO_CHUNK_HDR requires a single I/O call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_HDR; +#endif + + /* Write the length and the chunk name */ + png_save_uint_32(buf, length); + png_save_uint_32(buf + 4, chunk_name); + png_write_data(png_ptr, buf, 8); + + /* Put the chunk name into png_ptr->chunk_name */ + png_ptr->chunk_name = chunk_name; + + /* Reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + + png_calculate_crc(png_ptr, buf + 4, 4); + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that chunk data will (possibly) be written. + * PNG_IO_CHUNK_DATA does NOT require a specific number of I/O calls. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_DATA; +#endif +} + +void PNGAPI +png_write_chunk_start(png_structrp png_ptr, png_const_bytep chunk_string, + png_uint_32 length) +{ + png_write_chunk_header(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), length); +} + +/* Write the data of a PNG chunk started with png_write_chunk_header(). + * 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_header(). + */ +void PNGAPI +png_write_chunk_data(png_structrp png_ptr, png_const_bytep data, + png_size_t length) +{ + /* Write the data, and run the CRC over it */ + if (png_ptr == NULL) + return; + + if (data != NULL && length > 0) + { + png_write_data(png_ptr, data, length); + + /* Update the CRC after writing the data, + * in case that the user I/O routine alters it. + */ + png_calculate_crc(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_header(). */ +void PNGAPI +png_write_chunk_end(png_structrp png_ptr) +{ + png_byte buf[4]; + + if (png_ptr == NULL) return; + +#ifdef PNG_IO_STATE_SUPPORTED + /* Inform the I/O callback that the chunk CRC is being written. + * PNG_IO_CHUNK_CRC requires a single I/O function call. + */ + png_ptr->io_state = PNG_IO_WRITING | PNG_IO_CHUNK_CRC; +#endif + + /* Write the crc in a single operation */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} /* 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 @@ -58,313 +172,577 @@ png_save_uint_16(png_bytep buf, unsigned int i) * 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) +static void +png_write_complete_chunk(png_structrp png_ptr, png_uint_32 chunk_name, + png_const_bytep data, png_size_t length) { - if(png_ptr == NULL) return; - png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + if (png_ptr == NULL) + return; + + /* On 64 bit architectures 'length' may not fit in a png_uint_32. */ + if (length > PNG_UINT_31_MAX) + png_error(png_ptr, "length exceeds PNG maxima"); + + png_write_chunk_header(png_ptr, chunk_name, (png_uint_32)length); png_write_chunk_data(png_ptr, data, length); png_write_chunk_end(png_ptr); } -/* Write the start of a PNG chunk. The type is the chunk type. - * The total_length is the sum of the lengths of all the data you will be - * passing in png_write_chunk_data(). - */ +/* This is the API that calls the internal function above. */ void PNGAPI -png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, - png_uint_32 length) +png_write_chunk(png_structrp png_ptr, png_const_bytep chunk_string, + png_const_bytep data, png_size_t length) { - png_byte buf[4]; - png_debug2(0, "Writing %s chunk (%lu bytes)\n", chunk_name, length); - if(png_ptr == NULL) return; - - /* write the length */ - png_save_uint_32(buf, length); - png_write_data(png_ptr, buf, (png_size_t)4); - - /* write the chunk name */ - png_write_data(png_ptr, chunk_name, (png_size_t)4); - /* reset the crc and run it over the chunk name */ - png_reset_crc(png_ptr); - png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); + png_write_complete_chunk(png_ptr, PNG_CHUNK_FROM_STRING(chunk_string), data, + length); } -/* 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(). +/* This is used below to find the size of an image to pass to png_deflate_claim, + * so it only needs to be accurate if the size is less than 16384 bytes (the + * point at which a lower LZ window size can be used.) */ -void PNGAPI -png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +static png_alloc_size_t +png_image_size(png_structrp png_ptr) { - /* write the data, and run the CRC over it */ - if(png_ptr == NULL) return; - if (data != NULL && length > 0) + /* Only return sizes up to the maximum of a png_uint_32, do this by limiting + * the width and height used to 15 bits. + */ + png_uint_32 h = png_ptr->height; + + if (png_ptr->rowbytes < 32768 && h < 32768) { - png_calculate_crc(png_ptr, data, length); - png_write_data(png_ptr, data, length); + if (png_ptr->interlaced) + { + /* Interlacing makes the image larger because of the replication of + * both the filter byte and the padding to a byte boundary. + */ + png_uint_32 w = png_ptr->width; + unsigned int pd = png_ptr->pixel_depth; + png_alloc_size_t cb_base; + int pass; + + for (cb_base=0, pass=0; pass<=6; ++pass) + { + png_uint_32 pw = PNG_PASS_COLS(w, pass); + + if (pw > 0) + cb_base += (PNG_ROWBYTES(pd, pw)+1) * PNG_PASS_ROWS(h, pass); + } + + return cb_base; + } + + else + return (png_ptr->rowbytes+1) * h; + } + + else + return 0xffffffffU; +} + +#ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + /* This is the code to hack the first two bytes of the deflate stream (the + * deflate header) to correct the windowBits value to match the actual data + * size. Note that the second argument is the *uncompressed* size but the + * first argument is the *compressed* data (and it must be deflate + * compressed.) + */ +static void +optimize_cmf(png_bytep data, png_alloc_size_t data_size) +{ + /* Optimize the CMF field in the zlib stream. The resultant zlib stream is + * still compliant to the stream specification. + */ + if (data_size <= 16384) /* else windowBits must be 15 */ + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + unsigned int z_cinfo; + unsigned int half_z_window_size; + + z_cinfo = z_cmf >> 4; + half_z_window_size = 1U << (z_cinfo + 7); + + if (data_size <= half_z_window_size) /* else no change */ + { + unsigned int tmp; + + do + { + half_z_window_size >>= 1; + --z_cinfo; + } + while (z_cinfo > 0 && data_size <= half_z_window_size); + + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + + data[0] = (png_byte)z_cmf; + tmp = data[1] & 0xe0; + tmp += 0x1f - ((z_cmf << 8) + tmp) % 0x1f; + data[1] = (png_byte)tmp; + } + } + } +} +#else +# define optimize_cmf(dp,dl) ((void)0) +#endif /* PNG_WRITE_OPTIMIZE_CMF_SUPPORTED */ + +/* Initialize the compressor for the appropriate type of compression. */ +static int +png_deflate_claim(png_structrp png_ptr, png_uint_32 owner, + png_alloc_size_t data_size) +{ + if (png_ptr->zowner != 0) + { + char msg[64]; + + PNG_STRING_FROM_CHUNK(msg, owner); + msg[4] = ':'; + msg[5] = ' '; + PNG_STRING_FROM_CHUNK(msg+6, png_ptr->zowner); + /* So the message that results is " using zstream"; this is an + * internal error, but is very useful for debugging. i18n requirements + * are minimal. + */ + (void)png_safecat(msg, (sizeof msg), 10, " using zstream"); +# if PNG_LIBPNG_BUILD_BASE_TYPE >= PNG_LIBPNG_BUILD_RC + png_warning(png_ptr, msg); + + /* Attempt sane error recovery */ + if (png_ptr->zowner == png_IDAT) /* don't steal from IDAT */ + { + png_ptr->zstream.msg = PNGZ_MSG_CAST("in use by IDAT"); + return Z_STREAM_ERROR; + } + + png_ptr->zowner = 0; +# else + png_error(png_ptr, msg); +# endif + } + + { + int level = png_ptr->zlib_level; + int method = png_ptr->zlib_method; + int windowBits = png_ptr->zlib_window_bits; + int memLevel = png_ptr->zlib_mem_level; + int strategy; /* set below */ + int ret; /* zlib return code */ + + if (owner == png_IDAT) + { + if (png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY) + strategy = png_ptr->zlib_strategy; + + else if (png_ptr->do_filter != PNG_FILTER_NONE) + strategy = PNG_Z_DEFAULT_STRATEGY; + + else + strategy = PNG_Z_DEFAULT_NOFILTER_STRATEGY; + } + + else + { +# ifdef PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED + level = png_ptr->zlib_text_level; + method = png_ptr->zlib_text_method; + windowBits = png_ptr->zlib_text_window_bits; + memLevel = png_ptr->zlib_text_mem_level; + strategy = png_ptr->zlib_text_strategy; +# else + /* If customization is not supported the values all come from the + * IDAT values except for the strategy, which is fixed to the + * default. (This is the pre-1.6.0 behavior too, although it was + * implemented in a very different way.) + */ + strategy = Z_DEFAULT_STRATEGY; +# endif + } + + /* Adjust 'windowBits' down if larger than 'data_size'; to stop this + * happening just pass 32768 as the data_size parameter. Notice that zlib + * requires an extra 262 bytes in the window in addition to the data to be + * able to see the whole of the data, so if data_size+262 takes us to the + * next windowBits size we need to fix up the value later. (Because even + * though deflate needs the extra window, inflate does not!) + */ + if (data_size <= 16384) + { + /* IMPLEMENTATION NOTE: this 'half_window_size' stuff is only here to + * work round a Microsoft Visual C misbehavior which, contrary to C-90, + * widens the result of the following shift to 64-bits if (and, + * apparently, only if) it is used in a test. + */ + unsigned int half_window_size = 1U << (windowBits-1); + + while (data_size + 262 <= half_window_size) + { + half_window_size >>= 1; + --windowBits; + } + } + + /* Check against the previous initialized values, if any. */ + if ((png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) && + (png_ptr->zlib_set_level != level || + png_ptr->zlib_set_method != method || + png_ptr->zlib_set_window_bits != windowBits || + png_ptr->zlib_set_mem_level != memLevel || + png_ptr->zlib_set_strategy != strategy)) + { + if (deflateEnd(&png_ptr->zstream) != Z_OK) + png_warning(png_ptr, "deflateEnd failed (ignored)"); + + png_ptr->flags &= ~PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* For safety clear out the input and output pointers (currently zlib + * doesn't use them on Init, but it might in the future). + */ + png_ptr->zstream.next_in = NULL; + png_ptr->zstream.avail_in = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->zstream.avail_out = 0; + + /* Now initialize if required, setting the new parameters, otherwise just + * to a simple reset to the previous parameters. + */ + if (png_ptr->flags & PNG_FLAG_ZSTREAM_INITIALIZED) + ret = deflateReset(&png_ptr->zstream); + + else + { + ret = deflateInit2(&png_ptr->zstream, level, method, windowBits, + memLevel, strategy); + + if (ret == Z_OK) + png_ptr->flags |= PNG_FLAG_ZSTREAM_INITIALIZED; + } + + /* The return code is from either deflateReset or deflateInit2; they have + * pretty much the same set of error codes. + */ + if (ret == Z_OK) + png_ptr->zowner = owner; + + else + png_zstream_error(png_ptr, ret); + + return ret; } } -/* Finish a chunk started with png_write_chunk_start(). */ -void PNGAPI -png_write_chunk_end(png_structp png_ptr) -{ - png_byte buf[4]; - - if(png_ptr == NULL) return; - - /* write the crc */ - png_save_uint_32(buf, png_ptr->crc); - - png_write_data(png_ptr, buf, (png_size_t)4); -} - -/* Simple function to write the signature. If we have already written - * the magic bytes of the signature, or more likely, the PNG stream is - * being embedded into another stream and doesn't need its own signature, - * we should call png_set_sig_bytes() to tell libpng how many of the - * bytes have already been written. - */ +/* Clean up (or trim) a linked list of compression buffers. */ void /* PRIVATE */ -png_write_sig(png_structp png_ptr) +png_free_buffer_list(png_structrp png_ptr, png_compression_bufferp *listp) { - png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; - /* write the rest of the 8 byte signature */ - png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], - (png_size_t)8 - png_ptr->sig_bytes); - if(png_ptr->sig_bytes < 3) - png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + png_compression_bufferp list = *listp; + + if (list != NULL) + { + *listp = NULL; + + do + { + png_compression_bufferp next = list->next; + + png_free(png_ptr, list); + list = next; + } + while (list != NULL); + } } -#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) -/* - * This pair of functions encapsulates the operation of (a) compressing a +#ifdef PNG_WRITE_COMPRESSED_TEXT_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. + * set up by the caller to allow access to the relevant local variables. + * + * compression_buffer (new in 1.6.0) is just a linked list of zbuffer_size + * temporary buffers. From 1.6.0 it is retained in png_struct so that it will + * be correctly freed in the event of a write error (previous implementations + * just leaked memory.) */ - typedef struct { - char *input; /* the uncompressed input data */ - int input_len; /* its length */ - int num_output_ptr; /* number of output pointers used */ - int max_output_ptr; /* size of output_ptr */ - png_charpp output_ptr; /* array of pointers to output */ + png_const_bytep input; /* The uncompressed input data */ + png_alloc_size_t input_len; /* Its length */ + png_uint_32 output_len; /* Final compressed length */ + png_byte output[1024]; /* First block of output */ } compression_state; -/* compress given text into storage in the png_ptr structure */ -static int /* PRIVATE */ -png_text_compress(png_structp png_ptr, - png_charp text, png_size_t text_len, int compression, - compression_state *comp) +static void +png_text_compress_init(compression_state *comp, png_const_bytep input, + png_alloc_size_t input_len) +{ + comp->input = input; + comp->input_len = input_len; + comp->output_len = 0; +} + +/* Compress the data in the compression state input */ +static int +png_text_compress(png_structrp png_ptr, png_uint_32 chunk_name, + compression_state *comp, png_uint_32 prefix_len) { int ret; - comp->num_output_ptr = 0; - comp->max_output_ptr = 0; - comp->output_ptr = NULL; - comp->input = NULL; - comp->input_len = 0; - - /* we may just want to pass the text right through */ - if (compression == PNG_TEXT_COMPRESSION_NONE) - { - comp->input = text; - comp->input_len = text_len; - return((int)text_len); - } - - if (compression >= PNG_TEXT_COMPRESSION_LAST) - { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char msg[50]; - png_snprintf(msg, 50, "Unknown compression type %d", compression); - png_warning(png_ptr, msg); -#else - png_warning(png_ptr, "Unknown compression type"); -#endif - } - - /* We can't write the chunk until we find out how much data we have, - * which means we need to run the compressor first and save the - * output. This shouldn't be a problem, as the vast majority of - * comments should be reasonable, but we will set up an array of - * malloc'd pointers to be sure. + /* To find the length of the output it is necessary to first compress the + * input, the result is buffered rather than using the two-pass algorithm + * that is used on the inflate side; deflate is assumed to be slower and a + * PNG writer is assumed to have more memory available than a PNG reader. * - * 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). + * IMPLEMENTATION NOTE: the zlib API deflateBound() can be used to find an + * upper limit on the output size, but it is always bigger than the input + * size so it is likely to be more efficient to use this linked-list + * approach. */ + ret = png_deflate_claim(png_ptr, chunk_name, comp->input_len); - /* 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; + if (ret != Z_OK) + return ret; - /* this is the same compression loop as in png_write_row() */ - do + /* Set up the compression buffers, we need a loop here to avoid overflowing a + * uInt. Use ZLIB_IO_MAX to limit the input. The output is always limited + * by the output buffer size, so there is no need to check that. Since this + * is ANSI-C we know that an 'int', hence a uInt, is always at least 16 bits + * in size. + */ { - /* compress the data */ - ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); - if (ret != Z_OK) + png_compression_bufferp *end = &png_ptr->zbuffer_list; + png_alloc_size_t input_len = comp->input_len; /* may be zero! */ + png_uint_32 output_len; + + /* zlib updates these for us: */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(comp->input); + png_ptr->zstream.avail_in = 0; /* Set below */ + png_ptr->zstream.next_out = comp->output; + png_ptr->zstream.avail_out = (sizeof comp->output); + + output_len = png_ptr->zstream.avail_out; + + do { - /* 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) + uInt avail_in = ZLIB_IO_MAX; + + if (avail_in > input_len) + avail_in = (uInt)input_len; + + input_len -= avail_in; + + png_ptr->zstream.avail_in = avail_in; + + if (png_ptr->zstream.avail_out == 0) { - int old_max; + png_compression_buffer *next; - old_max = comp->max_output_ptr; - comp->max_output_ptr = comp->num_output_ptr + 4; - if (comp->output_ptr != NULL) + /* Chunk data is limited to 2^31 bytes in length, so the prefix + * length must be counted here. + */ + if (output_len + prefix_len > PNG_UINT_31_MAX) { - png_charpp old_ptr; - - old_ptr = comp->output_ptr; - comp->output_ptr = (png_charpp)png_malloc(png_ptr, - (png_uint_32)(comp->max_output_ptr * - png_sizeof (png_charpp))); - png_memcpy(comp->output_ptr, old_ptr, old_max - * png_sizeof (png_charp)); - png_free(png_ptr, old_ptr); + ret = Z_MEM_ERROR; + break; } - else - comp->output_ptr = (png_charpp)png_malloc(png_ptr, - (png_uint_32)(comp->max_output_ptr * - png_sizeof (png_charp))); - } - /* save the data */ - comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr, - (png_uint_32)png_ptr->zbuf_size); - png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, - png_ptr->zbuf_size); - comp->num_output_ptr++; - - /* and reset the buffer */ - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - png_ptr->zstream.next_out = png_ptr->zbuf; - } - /* continue until we don't have any more to compress */ - } while (png_ptr->zstream.avail_in); - - /* finish the compression */ - do - { - /* tell zlib we are finished */ - ret = deflate(&png_ptr->zstream, Z_FINISH); - - if (ret == Z_OK) - { - /* check to see if we need more room */ - if (!(png_ptr->zstream.avail_out)) - { - /* check to make sure our output array has room */ - if (comp->num_output_ptr >= comp->max_output_ptr) + /* Need a new (malloc'ed) buffer, but there may be one present + * already. + */ + next = *end; + if (next == NULL) { - int old_max; + next = png_voidcast(png_compression_bufferp, png_malloc_base + (png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); - old_max = comp->max_output_ptr; - comp->max_output_ptr = comp->num_output_ptr + 4; - if (comp->output_ptr != NULL) + if (next == NULL) { - png_charpp old_ptr; - - old_ptr = comp->output_ptr; - /* This could be optimized to realloc() */ - comp->output_ptr = (png_charpp)png_malloc(png_ptr, - (png_uint_32)(comp->max_output_ptr * - png_sizeof (png_charpp))); - png_memcpy(comp->output_ptr, old_ptr, - old_max * png_sizeof (png_charp)); - png_free(png_ptr, old_ptr); + ret = Z_MEM_ERROR; + break; } - else - comp->output_ptr = (png_charpp)png_malloc(png_ptr, - (png_uint_32)(comp->max_output_ptr * - png_sizeof (png_charp))); + + /* Link in this buffer (so that it will be freed later) */ + next->next = NULL; + *end = next; } - /* 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++; + png_ptr->zstream.next_out = next->output; + png_ptr->zstream.avail_out = png_ptr->zbuffer_size; + output_len += png_ptr->zstream.avail_out; - /* and reset the buffer pointers */ - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - png_ptr->zstream.next_out = png_ptr->zbuf; + /* Move 'end' to the next buffer pointer. */ + end = &next->next; } + + /* Compress the data */ + ret = deflate(&png_ptr->zstream, + input_len > 0 ? Z_NO_FLUSH : Z_FINISH); + + /* Claw back input data that was not consumed (because avail_in is + * reset above every time round the loop). + */ + input_len += png_ptr->zstream.avail_in; + png_ptr->zstream.avail_in = 0; /* safety */ } - else if (ret != Z_STREAM_END) + while (ret == Z_OK); + + /* There may be some space left in the last output buffer, this needs to + * be subtracted from output_len. + */ + output_len -= png_ptr->zstream.avail_out; + png_ptr->zstream.avail_out = 0; /* safety */ + comp->output_len = output_len; + + /* Now double check the output length, put in a custom message if it is + * too long. Otherwise ensure the z_stream::msg pointer is set to + * something. + */ + if (output_len + prefix_len >= PNG_UINT_31_MAX) { - /* we got an error */ - if (png_ptr->zstream.msg != NULL) - png_error(png_ptr, png_ptr->zstream.msg); - else - png_error(png_ptr, "zlib error"); + png_ptr->zstream.msg = PNGZ_MSG_CAST("compressed data too long"); + ret = Z_MEM_ERROR; } - } while (ret != Z_STREAM_END); - /* text length is number of buffers plus last buffer */ - text_len = png_ptr->zbuf_size * comp->num_output_ptr; - if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) - text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + else + png_zstream_error(png_ptr, ret); - return((int)text_len); + /* Reset zlib for another zTXt/iTXt or image data */ + png_ptr->zowner = 0; + + /* The only success case is Z_STREAM_END, input_len must be 0, if not this + * is an internal error. + */ + if (ret == Z_STREAM_END && input_len == 0) + { + /* Fix up the deflate header, if required */ + optimize_cmf(comp->output, comp->input_len); + + /* But Z_OK is returned, not Z_STREAM_END; this allows the claim + * function above to return Z_STREAM_END on an error (though it never + * does in the current versions of zlib.) + */ + return Z_OK; + } + + else + return ret; + } } -/* ship the compressed text out via chunk writes */ -static void /* PRIVATE */ -png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +/* Ship the compressed text out via chunk writes */ +static void +png_write_compressed_data_out(png_structrp png_ptr, compression_state *comp) { - int i; + png_uint_32 output_len = comp->output_len; + png_const_bytep output = comp->output; + png_uint_32 avail = (sizeof comp->output); + png_compression_buffer *next = png_ptr->zbuffer_list; - /* handle the no-compression case */ - if (comp->input) + for (;;) { - png_write_chunk_data(png_ptr, (png_bytep)comp->input, - (png_size_t)comp->input_len); - return; + if (avail > output_len) + avail = output_len; + + png_write_chunk_data(png_ptr, output, avail); + + output_len -= avail; + + if (output_len == 0 || next == NULL) + break; + + avail = png_ptr->zbuffer_size; + output = next->output; + next = next->next; } - /* write saved output buffers, if any */ - for (i = 0; i < comp->num_output_ptr; i++) - { - png_write_chunk_data(png_ptr,(png_bytep)comp->output_ptr[i], - png_ptr->zbuf_size); - png_free(png_ptr, comp->output_ptr[i]); - comp->output_ptr[i]=NULL; - } - if (comp->max_output_ptr != 0) - png_free(png_ptr, comp->output_ptr); - comp->output_ptr=NULL; - /* write anything left in zbuf */ - if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) - png_write_chunk_data(png_ptr, png_ptr->zbuf, - png_ptr->zbuf_size - png_ptr->zstream.avail_out); + /* This is an internal error; 'next' must have been NULL! */ + if (output_len > 0) + png_error(png_ptr, "error writing ancillary chunked compressed data"); +} +#endif /* PNG_WRITE_COMPRESSED_TEXT_SUPPORTED */ - /* reset zlib for another zTXt/iTXt or image data */ - deflateReset(&png_ptr->zstream); - png_ptr->zstream.data_type = Z_BINARY; +#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' buffer must be 80 characters in size (for the keyword plus a + * trailing '\0'). If this routine returns 0 then there was no keyword, or a + * valid one could not be generated, and the caller must png_error. + */ +static png_uint_32 +png_check_keyword(png_structrp png_ptr, png_const_charp key, png_bytep new_key) +{ + png_const_charp orig_key = key; + png_uint_32 key_len = 0; + int bad_character = 0; + int space = 1; + + png_debug(1, "in png_check_keyword"); + + if (key == NULL) + { + *new_key = 0; + return 0; + } + + while (*key && key_len < 79) + { + png_byte ch = (png_byte)(0xff & *key++); + + if ((ch > 32 && ch <= 126) || (ch >= 161 /*&& ch <= 255*/)) + *new_key++ = ch, ++key_len, space = 0; + + else if (!space) + { + /* A space or an invalid character when one wasn't seen immediately + * before; output just a space. + */ + *new_key++ = 32, ++key_len, space = 1; + + /* If the character was not a space then it is invalid. */ + if (ch != 32) + bad_character = ch; + } + + else if (!bad_character) + bad_character = ch; /* just skip it, record the first error */ + } + + if (key_len > 0 && space) /* trailing space */ + { + --key_len, --new_key; + if (!bad_character) + bad_character = 32; + } + + /* Terminate the keyword */ + *new_key = 0; + + if (key_len == 0) + return 0; + + /* Try to only output one warning per keyword: */ + if (*key) /* keyword too long */ + png_warning(png_ptr, "keyword truncated"); + + else if (bad_character) + { + PNG_WARNING_PARAMETERS(p) + + png_warning_parameter(p, 1, orig_key); + png_warning_parameter_signed(p, 2, PNG_NUMBER_FORMAT_02x, bad_character); + + png_formatted_warning(png_ptr, p, "keyword \"@1\": bad character '0x@2'"); + } + + return key_len; } #endif @@ -373,16 +751,14 @@ png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) * information being correct. */ void /* PRIVATE */ -png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, - int bit_depth, int color_type, int compression_type, int filter_type, - int interlace_type) +png_write_IHDR(png_structrp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_IHDR; -#endif - png_byte buf[13]; /* buffer to store the IHDR info */ + png_byte buf[13]; /* Buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR"); - png_debug(1, "in png_write_IHDR\n"); /* Check that we have valid input data from the application info */ switch (color_type) { @@ -393,35 +769,61 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, case 2: case 4: case 8: - case 16: png_ptr->channels = 1; break; - default: png_error(png_ptr,"Invalid bit depth for grayscale image"); +#ifdef PNG_WRITE_16BIT_SUPPORTED + case 16: +#endif + png_ptr->channels = 1; break; + + default: + png_error(png_ptr, + "Invalid bit depth for grayscale image"); } break; + case PNG_COLOR_TYPE_RGB: +#ifdef PNG_WRITE_16BIT_SUPPORTED if (bit_depth != 8 && bit_depth != 16) +#else + if (bit_depth != 8) +#endif png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; break; + case PNG_COLOR_TYPE_PALETTE: switch (bit_depth) { case 1: case 2: case 4: - case 8: png_ptr->channels = 1; break; - default: png_error(png_ptr, "Invalid bit depth for paletted image"); + case 8: + png_ptr->channels = 1; + break; + + default: + png_error(png_ptr, "Invalid bit depth for paletted image"); } break; + case PNG_COLOR_TYPE_GRAY_ALPHA: if (bit_depth != 8 && bit_depth != 16) png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; break; + case PNG_COLOR_TYPE_RGB_ALPHA: +#ifdef PNG_WRITE_16BIT_SUPPORTED if (bit_depth != 8 && bit_depth != 16) +#else + if (bit_depth != 8) +#endif png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; break; + default: png_error(png_ptr, "Invalid image color type specified"); } @@ -442,14 +844,14 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, * 5. The color_type is RGB or RGBA */ if ( -#if defined(PNG_MNG_FEATURES_SUPPORTED) - !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && - ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && - (color_type == PNG_COLOR_TYPE_RGB || - color_type == PNG_COLOR_TYPE_RGB_ALPHA) && - (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#ifdef PNG_MNG_FEATURES_SUPPORTED + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && #endif - filter_type != PNG_FILTER_TYPE_BASE) + filter_type != PNG_FILTER_TYPE_BASE) { png_warning(png_ptr, "Invalid filter type specified"); filter_type = PNG_FILTER_TYPE_BASE; @@ -457,7 +859,7 @@ png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, #ifdef PNG_WRITE_INTERLACING_SUPPORTED if (interlace_type != PNG_INTERLACE_NONE && - interlace_type != PNG_INTERLACE_ADAM7) + interlace_type != PNG_INTERLACE_ADAM7) { png_warning(png_ptr, "Invalid interlace type specified"); interlace_type = PNG_INTERLACE_ADAM7; @@ -466,11 +868,11 @@ 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 */ + /* Save the relevent information */ png_ptr->bit_depth = (png_byte)bit_depth; png_ptr->color_type = (png_byte)color_type; png_ptr->interlaced = (png_byte)interlace_type; -#if defined(PNG_MNG_FEATURES_SUPPORTED) +#ifdef PNG_MNG_FEATURES_SUPPORTED png_ptr->filter_type = (png_byte)filter_type; #endif png_ptr->compression_type = (png_byte)compression_type; @@ -479,12 +881,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 */ + /* 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 */ + /* 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; @@ -493,93 +895,68 @@ 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); + /* Write the chunk */ + png_write_complete_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); - /* initialize zlib with PNG info */ - png_ptr->zstream.zalloc = png_zalloc; - png_ptr->zstream.zfree = png_zfree; - png_ptr->zstream.opaque = (voidpf)png_ptr; if (!(png_ptr->do_filter)) { if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || - png_ptr->bit_depth < 8) + png_ptr->bit_depth < 8) png_ptr->do_filter = PNG_FILTER_NONE; + else png_ptr->do_filter = PNG_ALL_FILTERS; } - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) - { - if (png_ptr->do_filter != PNG_FILTER_NONE) - png_ptr->zlib_strategy = Z_FILTERED; - else - png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; - } - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) - png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) - png_ptr->zlib_mem_level = 8; - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) - png_ptr->zlib_window_bits = 15; - if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) - png_ptr->zlib_method = 8; - if (deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, - png_ptr->zlib_method, png_ptr->zlib_window_bits, - png_ptr->zlib_mem_level, png_ptr->zlib_strategy) != Z_OK) - png_error(png_ptr, "zlib failed to initialize compressor"); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - /* libpng is not interested in zstream.data_type */ - /* set it to a predefined value, to avoid its evaluation inside zlib */ - png_ptr->zstream.data_type = Z_BINARY; - png_ptr->mode = PNG_HAVE_IHDR; + png_ptr->mode = PNG_HAVE_IHDR; /* not READY_FOR_ZTXT */ } -/* write the palette. We are careful not to trust png_color to be in the +/* Write the palette. We are careful not to trust png_color to be in the * correct order for PNG, so people can redefine it to any convenient * structure. */ void /* PRIVATE */ -png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +png_write_PLTE(png_structrp png_ptr, png_const_colorp palette, + png_uint_32 num_pal) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_PLTE; -#endif png_uint_32 i; - png_colorp pal_ptr; + png_const_colorp pal_ptr; png_byte buf[3]; - png_debug(1, "in png_write_PLTE\n"); + png_debug(1, "in png_write_PLTE"); + if (( -#if defined(PNG_MNG_FEATURES_SUPPORTED) - !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#ifdef PNG_MNG_FEATURES_SUPPORTED + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && #endif - num_pal == 0) || num_pal > 256) + num_pal == 0) || num_pal > 256) { - if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) - { - png_error(png_ptr, "Invalid number of colors in palette"); - } - else - { - png_warning(png_ptr, "Invalid number of colors in palette"); - return; - } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } } if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) { png_warning(png_ptr, - "Ignoring request to write a PLTE chunk in grayscale PNG"); + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; } png_ptr->num_palette = (png_uint_16)num_pal; - png_debug1(3, "num_palette = %d\n", png_ptr->num_palette); + png_debug1(3, "num_palette = %d", png_ptr->num_palette); + + png_write_chunk_header(png_ptr, png_PLTE, (png_uint_32)(num_pal * 3)); +#ifdef PNG_POINTER_INDEXING_SUPPORTED - png_write_chunk_start(png_ptr, png_PLTE, num_pal * 3); -#ifndef PNG_NO_POINTER_INDEXING for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) { buf[0] = pal_ptr->red; @@ -587,9 +964,13 @@ png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) buf[2] = pal_ptr->blue; png_write_chunk_data(png_ptr, buf, (png_size_t)3); } + #else - /* This is a little slower but some buggy compilers need to do this instead */ + /* This is a little slower but some buggy compilers need to do this + * instead + */ pal_ptr=palette; + for (i = 0; i < num_pal; i++) { buf[0] = pal_ptr[i].red; @@ -597,307 +978,374 @@ png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) buf[2] = pal_ptr[i].blue; png_write_chunk_data(png_ptr, buf, (png_size_t)3); } + #endif png_write_chunk_end(png_ptr); png_ptr->mode |= PNG_HAVE_PLTE; } -/* write an IDAT chunk */ +/* This is similar to png_text_compress, above, except that it does not require + * all of the data at once and, instead of buffering the compressed result, + * writes it as IDAT chunks. Unlike png_text_compress it *can* png_error out + * because it calls the write interface. As a result it does its own error + * reporting and does not return an error code. In the event of error it will + * just call png_error. The input data length may exceed 32-bits. The 'flush' + * parameter is exactly the same as that to deflate, with the following + * meanings: + * + * Z_NO_FLUSH: normal incremental output of compressed data + * Z_SYNC_FLUSH: do a SYNC_FLUSH, used by png_write_flush + * Z_FINISH: this is the end of the input, do a Z_FINISH and clean up + * + * The routine manages the acquire and release of the png_ptr->zstream by + * checking and (at the end) clearing png_ptr->zowner, it does some sanity + * checks on the 'mode' flags while doing this. + */ void /* PRIVATE */ -png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +png_compress_IDAT(png_structrp png_ptr, png_const_bytep input, + png_alloc_size_t input_len, int flush) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_IDAT; -#endif - png_debug(1, "in png_write_IDAT\n"); - - /* Optimize the CMF field in the zlib stream. */ - /* This hack of the zlib stream is compliant to the stream specification. */ - if (!(png_ptr->mode & PNG_HAVE_IDAT) && - png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + if (png_ptr->zowner != png_IDAT) { - unsigned int z_cmf = data[0]; /* zlib compression method and flags */ - if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + /* First time. Ensure we have a temporary buffer for compression and + * trim the buffer list if it has more than one entry to free memory. + * If 'WRITE_COMPRESSED_TEXT' is not set the list will never have been + * created at this point, but the check here is quick and safe. + */ + if (png_ptr->zbuffer_list == NULL) { - /* Avoid memory underflows and multiplication overflows. */ - /* The conditions below are practically always satisfied; - however, they still must be checked. */ - if (length >= 2 && - png_ptr->height < 16384 && png_ptr->width < 16384) - { - png_uint_32 uncompressed_idat_size = png_ptr->height * - ((png_ptr->width * - png_ptr->channels * png_ptr->bit_depth + 15) >> 3); - unsigned int z_cinfo = z_cmf >> 4; - unsigned int half_z_window_size = 1 << (z_cinfo + 7); - while (uncompressed_idat_size <= half_z_window_size && - half_z_window_size >= 256) - { - z_cinfo--; - half_z_window_size >>= 1; - } - z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); - if (data[0] != (png_byte)z_cmf) - { - data[0] = (png_byte)z_cmf; - data[1] &= 0xe0; - data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); - } - } + png_ptr->zbuffer_list = png_voidcast(png_compression_bufferp, + png_malloc(png_ptr, PNG_COMPRESSION_BUFFER_SIZE(png_ptr))); + png_ptr->zbuffer_list->next = NULL; } + else - png_error(png_ptr, - "Invalid zlib compression method or flags in IDAT"); + png_free_buffer_list(png_ptr, &png_ptr->zbuffer_list->next); + + /* It is a terminal error if we can't claim the zstream. */ + if (png_deflate_claim(png_ptr, png_IDAT, png_image_size(png_ptr)) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + /* The output state is maintained in png_ptr->zstream, so it must be + * initialized here after the claim. + */ + png_ptr->zstream.next_out = png_ptr->zbuffer_list->output; + png_ptr->zstream.avail_out = png_ptr->zbuffer_size; } - png_write_chunk(png_ptr, png_IDAT, data, length); - png_ptr->mode |= PNG_HAVE_IDAT; + /* Now loop reading and writing until all the input is consumed or an error + * terminates the operation. The _out values are maintained across calls to + * this function, but the input must be reset each time. + */ + png_ptr->zstream.next_in = PNGZ_INPUT_CAST(input); + png_ptr->zstream.avail_in = 0; /* set below */ + for (;;) + { + int ret; + + /* INPUT: from the row data */ + uInt avail = ZLIB_IO_MAX; + + if (avail > input_len) + avail = (uInt)input_len; /* safe because of the check */ + + png_ptr->zstream.avail_in = avail; + input_len -= avail; + + ret = deflate(&png_ptr->zstream, input_len > 0 ? Z_NO_FLUSH : flush); + + /* Include as-yet unconsumed input */ + input_len += png_ptr->zstream.avail_in; + png_ptr->zstream.avail_in = 0; + + /* OUTPUT: write complete IDAT chunks when avail_out drops to zero, note + * that these two zstream fields are preserved across the calls, therefore + * there is no need to set these up on entry to the loop. + */ + if (png_ptr->zstream.avail_out == 0) + { + png_bytep data = png_ptr->zbuffer_list->output; + uInt size = png_ptr->zbuffer_size; + + /* Write an IDAT containing the data then reset the buffer. The + * first IDAT may need deflate header optimization. + */ +# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + optimize_cmf(data, png_image_size(png_ptr)); +# endif + + png_write_complete_chunk(png_ptr, png_IDAT, data, size); + png_ptr->mode |= PNG_HAVE_IDAT; + + png_ptr->zstream.next_out = data; + png_ptr->zstream.avail_out = size; + + /* For SYNC_FLUSH or FINISH it is essential to keep calling zlib with + * the same flush parameter until it has finished output, for NO_FLUSH + * it doesn't matter. + */ + if (ret == Z_OK && flush != Z_NO_FLUSH) + continue; + } + + /* The order of these checks doesn't matter much; it just effect which + * possible error might be detected if multiple things go wrong at once. + */ + if (ret == Z_OK) /* most likely return code! */ + { + /* If all the input has been consumed then just return. If Z_FINISH + * was used as the flush parameter something has gone wrong if we get + * here. + */ + if (input_len == 0) + { + if (flush == Z_FINISH) + png_error(png_ptr, "Z_OK on Z_FINISH with output space"); + + return; + } + } + + else if (ret == Z_STREAM_END && flush == Z_FINISH) + { + /* This is the end of the IDAT data; any pending output must be + * flushed. For small PNG files we may still be at the beginning. + */ + png_bytep data = png_ptr->zbuffer_list->output; + uInt size = png_ptr->zbuffer_size - png_ptr->zstream.avail_out; + +# ifdef PNG_WRITE_OPTIMIZE_CMF_SUPPORTED + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + optimize_cmf(data, png_image_size(png_ptr)); +# endif + + png_write_complete_chunk(png_ptr, png_IDAT, data, size); + png_ptr->zstream.avail_out = 0; + png_ptr->zstream.next_out = NULL; + png_ptr->mode |= PNG_HAVE_IDAT | PNG_AFTER_IDAT; + + png_ptr->zowner = 0; /* Release the stream */ + return; + } + + else + { + /* This is an error condition. */ + png_zstream_error(png_ptr, ret); + png_error(png_ptr, png_ptr->zstream.msg); + } + } } -/* write an IEND chunk */ +/* Write an IEND chunk */ void /* PRIVATE */ -png_write_IEND(png_structp png_ptr) +png_write_IEND(png_structrp png_ptr) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_IEND; -#endif - png_debug(1, "in png_write_IEND\n"); - png_write_chunk(png_ptr, png_IEND, png_bytep_NULL, - (png_size_t)0); + png_debug(1, "in png_write_IEND"); + + png_write_complete_chunk(png_ptr, png_IEND, NULL, (png_size_t)0); png_ptr->mode |= PNG_HAVE_IEND; } -#if defined(PNG_WRITE_gAMA_SUPPORTED) -/* write a gAMA chunk */ -#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_WRITE_gAMA_SUPPORTED +/* Write a gAMA chunk */ void /* PRIVATE */ -png_write_gAMA(png_structp png_ptr, double file_gamma) +png_write_gAMA_fixed(png_structrp png_ptr, png_fixed_point file_gamma) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_gAMA; -#endif - png_uint_32 igamma; png_byte buf[4]; - png_debug(1, "in png_write_gAMA\n"); - /* file_gamma is saved in 1/100,000ths */ - igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); - png_save_uint_32(buf, igamma); - png_write_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); -} -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -void /* PRIVATE */ -png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) -{ -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_gAMA; -#endif - png_byte buf[4]; + png_debug(1, "in png_write_gAMA"); - 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); + png_write_complete_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); } #endif -#endif -#if defined(PNG_WRITE_sRGB_SUPPORTED) -/* write a sRGB chunk */ +#ifdef PNG_WRITE_sRGB_SUPPORTED +/* Write a sRGB chunk */ void /* PRIVATE */ -png_write_sRGB(png_structp png_ptr, int srgb_intent) +png_write_sRGB(png_structrp png_ptr, int srgb_intent) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_sRGB; -#endif png_byte buf[1]; - png_debug(1, "in png_write_sRGB\n"); - if(srgb_intent >= PNG_sRGB_INTENT_LAST) - png_warning(png_ptr, - "Invalid sRGB rendering intent specified"); + png_debug(1, "in png_write_sRGB"); + + if (srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; - png_write_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); + png_write_complete_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); } #endif -#if defined(PNG_WRITE_iCCP_SUPPORTED) -/* write an iCCP chunk */ +#ifdef PNG_WRITE_iCCP_SUPPORTED +/* Write an iCCP chunk */ void /* PRIVATE */ -png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, - png_charp profile, int profile_len) +png_write_iCCP(png_structrp png_ptr, png_const_charp name, + png_const_bytep profile) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_iCCP; -#endif - png_size_t name_len; - png_charp new_name; + png_uint_32 name_len; + png_uint_32 profile_len; + png_byte new_name[81]; /* 1 byte for the compression byte */ compression_state comp; - int embedded_profile_len = 0; - png_debug(1, "in png_write_iCCP\n"); + png_debug(1, "in png_write_iCCP"); - comp.num_output_ptr = 0; - comp.max_output_ptr = 0; - comp.output_ptr = NULL; - comp.input = NULL; - comp.input_len = 0; + /* These are all internal problems: the profile should have been checked + * before when it was stored. + */ + if (profile == NULL) + png_error(png_ptr, "No profile for iCCP chunk"); /* internal error */ + + profile_len = png_get_uint_32(profile); + + if (profile_len < 132) + png_error(png_ptr, "ICC profile too short"); + + if (profile_len & 0x03) + png_error(png_ptr, "ICC profile length invalid (not a multiple of 4)"); - if (name == NULL || (name_len = png_check_keyword(png_ptr, name, - &new_name)) == 0) { - png_warning(png_ptr, "Empty keyword in iCCP chunk"); - return; + png_uint_32 embedded_profile_len = png_get_uint_32(profile); + + if (profile_len != embedded_profile_len) + png_error(png_ptr, "Profile length does not match profile"); } - if (compression_type != PNG_COMPRESSION_TYPE_BASE) - png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + name_len = png_check_keyword(png_ptr, name, new_name); - if (profile == NULL) - profile_len = 0; + if (name_len == 0) + png_error(png_ptr, "iCCP: invalid keyword"); - if (profile_len > 3) - embedded_profile_len = - ((*( (png_bytep)profile ))<<24) | - ((*( (png_bytep)profile+1))<<16) | - ((*( (png_bytep)profile+2))<< 8) | - ((*( (png_bytep)profile+3)) ); + new_name[++name_len] = PNG_COMPRESSION_TYPE_BASE; - if (profile_len < embedded_profile_len) - { - png_warning(png_ptr, - "Embedded profile length too large in iCCP chunk"); - return; - } + /* Make sure we include the NULL after the name and the compression type */ + ++name_len; - if (profile_len > embedded_profile_len) - { - png_warning(png_ptr, - "Truncating profile to actual length in iCCP chunk"); - profile_len = embedded_profile_len; - } + png_text_compress_init(&comp, profile, profile_len); - if (profile_len) - profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len, - PNG_COMPRESSION_TYPE_BASE, &comp); + /* Allow for keyword terminator and compression byte */ + if (png_text_compress(png_ptr, png_iCCP, &comp, name_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); - /* make sure we include the NULL after the name and the compression type */ - png_write_chunk_start(png_ptr, png_iCCP, - (png_uint_32)name_len+profile_len+2); - new_name[name_len+1]=0x00; - png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2); + png_write_chunk_header(png_ptr, png_iCCP, name_len + comp.output_len); - if (profile_len) - png_write_compressed_data_out(png_ptr, &comp); + png_write_chunk_data(png_ptr, new_name, name_len); + + png_write_compressed_data_out(png_ptr, &comp); png_write_chunk_end(png_ptr); - png_free(png_ptr, new_name); } #endif -#if defined(PNG_WRITE_sPLT_SUPPORTED) -/* write a sPLT chunk */ +#ifdef PNG_WRITE_sPLT_SUPPORTED +/* Write a sPLT chunk */ void /* PRIVATE */ -png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +png_write_sPLT(png_structrp png_ptr, png_const_sPLT_tp spalette) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_sPLT; -#endif - png_size_t name_len; - png_charp new_name; + png_uint_32 name_len; + png_byte new_name[80]; png_byte entrybuf[10]; - int entry_size = (spalette->depth == 8 ? 6 : 10); - int palette_size = entry_size * spalette->nentries; + png_size_t entry_size = (spalette->depth == 8 ? 6 : 10); + png_size_t palette_size = entry_size * spalette->nentries; png_sPLT_entryp ep; -#ifdef PNG_NO_POINTER_INDEXING +#ifndef PNG_POINTER_INDEXING_SUPPORTED int i; #endif - png_debug(1, "in png_write_sPLT\n"); - if (spalette->name == NULL || (name_len = png_check_keyword(png_ptr, - spalette->name, &new_name))==0) - { - png_warning(png_ptr, "Empty keyword in sPLT chunk"); - return; - } + png_debug(1, "in png_write_sPLT"); - /* 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); + name_len = png_check_keyword(png_ptr, spalette->name, new_name); - /* loop through each palette entry, writing appropriately */ -#ifndef PNG_NO_POINTER_INDEXING - for (ep = spalette->entries; epentries+spalette->nentries; ep++) + if (name_len == 0) + png_error(png_ptr, "sPLT: invalid keyword"); + + /* Make sure we include the NULL after the name */ + png_write_chunk_header(png_ptr, png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + + png_write_chunk_data(png_ptr, (png_bytep)new_name, + (png_size_t)(name_len + 1)); + + png_write_chunk_data(png_ptr, &spalette->depth, (png_size_t)1); + + /* Loop through each palette entry, writing appropriately */ +#ifdef PNG_POINTER_INDEXING_SUPPORTED + for (ep = spalette->entries; epentries + spalette->nentries; ep++) { - if (spalette->depth == 8) - { - entrybuf[0] = (png_byte)ep->red; - entrybuf[1] = (png_byte)ep->green; - entrybuf[2] = (png_byte)ep->blue; - entrybuf[3] = (png_byte)ep->alpha; - png_save_uint_16(entrybuf + 4, ep->frequency); - } - else - { - png_save_uint_16(entrybuf + 0, ep->red); - png_save_uint_16(entrybuf + 2, ep->green); - png_save_uint_16(entrybuf + 4, ep->blue); - png_save_uint_16(entrybuf + 6, ep->alpha); - png_save_uint_16(entrybuf + 8, ep->frequency); - } - png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + + png_write_chunk_data(png_ptr, entrybuf, entry_size); } #else ep=spalette->entries; - for (i=0; i>spalette->nentries; i++) + for (i = 0; i>spalette->nentries; i++) { - if (spalette->depth == 8) - { - entrybuf[0] = (png_byte)ep[i].red; - entrybuf[1] = (png_byte)ep[i].green; - entrybuf[2] = (png_byte)ep[i].blue; - entrybuf[3] = (png_byte)ep[i].alpha; - png_save_uint_16(entrybuf + 4, ep[i].frequency); - } - else - { - png_save_uint_16(entrybuf + 0, ep[i].red); - png_save_uint_16(entrybuf + 2, ep[i].green); - png_save_uint_16(entrybuf + 4, ep[i].blue); - png_save_uint_16(entrybuf + 6, ep[i].alpha); - png_save_uint_16(entrybuf + 8, ep[i].frequency); - } - png_write_chunk_data(png_ptr, entrybuf, entry_size); + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + + png_write_chunk_data(png_ptr, entrybuf, entry_size); } #endif png_write_chunk_end(png_ptr); - png_free(png_ptr, new_name); } #endif -#if defined(PNG_WRITE_sBIT_SUPPORTED) -/* write the sBIT chunk */ +#ifdef PNG_WRITE_sBIT_SUPPORTED +/* Write the sBIT chunk */ void /* PRIVATE */ -png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +png_write_sBIT(png_structrp png_ptr, png_const_color_8p sbit, int color_type) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_sBIT; -#endif png_byte buf[4]; png_size_t size; - png_debug(1, "in png_write_sBIT\n"); - /* make sure we don't depend upon the order of PNG_COLOR_8 */ + png_debug(1, "in png_write_sBIT"); + + /* Make sure we don't depend upon the order of PNG_COLOR_8 */ if (color_type & PNG_COLOR_MASK_COLOR) { png_byte maxbits; maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : - png_ptr->usr_bit_depth); + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || sbit->green == 0 || sbit->green > maxbits || sbit->blue == 0 || sbit->blue > maxbits) @@ -905,11 +1353,13 @@ png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) png_warning(png_ptr, "Invalid sBIT depth specified"); return; } + buf[0] = sbit->red; buf[1] = sbit->green; buf[2] = sbit->blue; size = 3; } + else { if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) @@ -917,6 +1367,7 @@ png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) png_warning(png_ptr, "Invalid sBIT depth specified"); return; } + buf[0] = sbit->gray; size = 1; } @@ -928,600 +1379,401 @@ png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) png_warning(png_ptr, "Invalid sBIT depth specified"); return; } + buf[size++] = sbit->alpha; } - png_write_chunk(png_ptr, png_sBIT, buf, size); + png_write_complete_chunk(png_ptr, png_sBIT, buf, size); } #endif -#if defined(PNG_WRITE_cHRM_SUPPORTED) -/* write the cHRM chunk */ -#ifdef PNG_FLOATING_POINT_SUPPORTED +#ifdef PNG_WRITE_cHRM_SUPPORTED +/* Write the cHRM chunk */ void /* PRIVATE */ -png_write_cHRM(png_structp png_ptr, double white_x, double white_y, - double red_x, double red_y, double green_x, double green_y, - double blue_x, double blue_y) +png_write_cHRM_fixed(png_structrp png_ptr, const png_xy *xy) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_cHRM; -#endif - png_byte buf[32]; - png_uint_32 itemp; - - png_debug(1, "in png_write_cHRM\n"); - /* each value is saved in 1/100,000ths */ - if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 || - white_x + white_y > 1.0) - { - png_warning(png_ptr, "Invalid cHRM white point specified"); -#if !defined(PNG_NO_CONSOLE_IO) - fprintf(stderr,"white_x=%f, white_y=%f\n",white_x, white_y); -#endif - return; - } - itemp = (png_uint_32)(white_x * 100000.0 + 0.5); - png_save_uint_32(buf, itemp); - itemp = (png_uint_32)(white_y * 100000.0 + 0.5); - png_save_uint_32(buf + 4, itemp); - - if (red_x < 0 || red_y < 0 || red_x + red_y > 1.0) - { - png_warning(png_ptr, "Invalid cHRM red point specified"); - return; - } - itemp = (png_uint_32)(red_x * 100000.0 + 0.5); - png_save_uint_32(buf + 8, itemp); - itemp = (png_uint_32)(red_y * 100000.0 + 0.5); - png_save_uint_32(buf + 12, itemp); - - if (green_x < 0 || green_y < 0 || green_x + green_y > 1.0) - { - png_warning(png_ptr, "Invalid cHRM green point specified"); - return; - } - itemp = (png_uint_32)(green_x * 100000.0 + 0.5); - png_save_uint_32(buf + 16, itemp); - itemp = (png_uint_32)(green_y * 100000.0 + 0.5); - png_save_uint_32(buf + 20, itemp); - - if (blue_x < 0 || blue_y < 0 || blue_x + blue_y > 1.0) - { - png_warning(png_ptr, "Invalid cHRM blue point specified"); - return; - } - itemp = (png_uint_32)(blue_x * 100000.0 + 0.5); - png_save_uint_32(buf + 24, itemp); - itemp = (png_uint_32)(blue_y * 100000.0 + 0.5); - png_save_uint_32(buf + 28, itemp); - - png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32); -} -#endif -#ifdef PNG_FIXED_POINT_SUPPORTED -void /* PRIVATE */ -png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, - png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, - png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, - png_fixed_point blue_y) -{ -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_cHRM; -#endif png_byte buf[32]; - png_debug(1, "in png_write_cHRM\n"); - /* each value is saved in 1/100,000ths */ - if (white_x > 80000L || white_y > 80000L || white_x + white_y > 100000L) - { - png_warning(png_ptr, "Invalid fixed cHRM white point specified"); -#if !defined(PNG_NO_CONSOLE_IO) - fprintf(stderr,"white_x=%ld, white_y=%ld\n",white_x, white_y); -#endif - return; - } - png_save_uint_32(buf, (png_uint_32)white_x); - png_save_uint_32(buf + 4, (png_uint_32)white_y); + png_debug(1, "in png_write_cHRM"); - if (red_x + red_y > 100000L) - { - png_warning(png_ptr, "Invalid cHRM fixed red point specified"); - return; - } - png_save_uint_32(buf + 8, (png_uint_32)red_x); - png_save_uint_32(buf + 12, (png_uint_32)red_y); + /* Each value is saved in 1/100,000ths */ + png_save_int_32(buf, xy->whitex); + png_save_int_32(buf + 4, xy->whitey); - if (green_x + green_y > 100000L) - { - png_warning(png_ptr, "Invalid fixed cHRM green point specified"); - return; - } - png_save_uint_32(buf + 16, (png_uint_32)green_x); - png_save_uint_32(buf + 20, (png_uint_32)green_y); + png_save_int_32(buf + 8, xy->redx); + png_save_int_32(buf + 12, xy->redy); - if (blue_x + blue_y > 100000L) - { - png_warning(png_ptr, "Invalid fixed cHRM blue point specified"); - return; - } - png_save_uint_32(buf + 24, (png_uint_32)blue_x); - png_save_uint_32(buf + 28, (png_uint_32)blue_y); + png_save_int_32(buf + 16, xy->greenx); + png_save_int_32(buf + 20, xy->greeny); - png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32); + png_save_int_32(buf + 24, xy->bluex); + png_save_int_32(buf + 28, xy->bluey); + + png_write_complete_chunk(png_ptr, png_cHRM, buf, 32); } #endif -#endif -#if defined(PNG_WRITE_tRNS_SUPPORTED) -/* write the tRNS chunk */ +#ifdef 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) +png_write_tRNS(png_structrp png_ptr, png_const_bytep trans_alpha, + png_const_color_16p tran, int num_trans, int color_type) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_tRNS; -#endif png_byte buf[6]; - png_debug(1, "in png_write_tRNS\n"); + png_debug(1, "in png_write_tRNS"); + if (color_type == PNG_COLOR_TYPE_PALETTE) { if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) { - png_warning(png_ptr,"Invalid number of transparent colors specified"); + png_app_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); + + /* Write the chunk out as it is */ + png_write_complete_chunk(png_ptr, png_tRNS, trans_alpha, + (png_size_t)num_trans); } + else if (color_type == PNG_COLOR_TYPE_GRAY) { - /* one 16 bit value */ - if(tran->gray >= (1 << png_ptr->bit_depth)) + /* One 16 bit value */ + if (tran->gray >= (1 << png_ptr->bit_depth)) { - png_warning(png_ptr, - "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + png_app_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; } + png_save_uint_16(buf, tran->gray); - png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); + png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); } + else if (color_type == PNG_COLOR_TYPE_RGB) { - /* three 16 bit values */ + /* Three 16 bit values */ png_save_uint_16(buf, tran->red); png_save_uint_16(buf + 2, tran->green); png_save_uint_16(buf + 4, tran->blue); - if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) - { - png_warning(png_ptr, - "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); - return; - } - png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) +#else + if (buf[0] | buf[2] | buf[4]) +#endif + { + png_app_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + + png_write_complete_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); } + else { - png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + png_app_warning(png_ptr, "Can't write tRNS with an alpha channel"); } } #endif -#if defined(PNG_WRITE_bKGD_SUPPORTED) -/* write the background chunk */ +#ifdef PNG_WRITE_bKGD_SUPPORTED +/* Write the background chunk */ void /* PRIVATE */ -png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +png_write_bKGD(png_structrp png_ptr, png_const_color_16p back, int color_type) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_bKGD; -#endif png_byte buf[6]; - png_debug(1, "in png_write_bKGD\n"); + png_debug(1, "in png_write_bKGD"); + if (color_type == PNG_COLOR_TYPE_PALETTE) { if ( -#if defined(PNG_MNG_FEATURES_SUPPORTED) +#ifdef PNG_MNG_FEATURES_SUPPORTED (png_ptr->num_palette || (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && #endif - back->index > png_ptr->num_palette) + back->index >= png_ptr->num_palette) { png_warning(png_ptr, "Invalid background palette index"); return; } + buf[0] = back->index; - png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); } + else if (color_type & PNG_COLOR_MASK_COLOR) { png_save_uint_16(buf, back->red); png_save_uint_16(buf + 2, back->green); png_save_uint_16(buf + 4, back->blue); - if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) - { - png_warning(png_ptr, - "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); - return; - } - png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)6); - } - else - { - if(back->gray >= (1 << png_ptr->bit_depth)) +#ifdef PNG_WRITE_16BIT_SUPPORTED + if (png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) +#else + if (buf[0] | buf[2] | buf[4]) +#endif { png_warning(png_ptr, - "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; } + + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)6); + } + + else + { + if (back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + + return; + } + png_save_uint_16(buf, back->gray); - png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)2); + png_write_complete_chunk(png_ptr, png_bKGD, buf, (png_size_t)2); } } #endif -#if defined(PNG_WRITE_hIST_SUPPORTED) -/* write the histogram */ +#ifdef PNG_WRITE_hIST_SUPPORTED +/* Write the histogram */ void /* PRIVATE */ -png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +png_write_hIST(png_structrp png_ptr, png_const_uint_16p hist, int num_hist) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_hIST; -#endif int i; png_byte buf[3]; - png_debug(1, "in png_write_hIST\n"); + png_debug(1, "in png_write_hIST"); + if (num_hist > (int)png_ptr->num_palette) { - png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist, - png_ptr->num_palette); + png_debug2(3, "num_hist = %d, num_palette = %d", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); return; } - png_write_chunk_start(png_ptr, png_hIST, (png_uint_32)(num_hist * 2)); + png_write_chunk_header(png_ptr, png_hIST, (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) { png_save_uint_16(buf, hist[i]); png_write_chunk_data(png_ptr, buf, (png_size_t)2); } + png_write_chunk_end(png_ptr); } #endif -#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ - defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) -/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, - * and if invalid, correct the keyword rather than discarding the entire - * chunk. The PNG 1.0 specification requires keywords 1-79 characters in - * length, forbids leading or trailing whitespace, multiple internal spaces, - * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. - * - * The new_key is allocated to hold the corrected keyword and must be freed - * by the calling routine. This avoids problems with trying to write to - * static keywords without having to have duplicate copies of the strings. - */ -png_size_t /* PRIVATE */ -png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +#ifdef PNG_WRITE_tEXt_SUPPORTED +/* Write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, + png_size_t text_len) { - png_size_t key_len; - png_charp kp, dp; - int kflag; - int kwarn=0; + png_uint_32 key_len; + png_byte new_key[80]; - png_debug(1, "in png_check_keyword\n"); - *new_key = NULL; + png_debug(1, "in png_write_tEXt"); - if (key == NULL || (key_len = png_strlen(key)) == 0) - { - png_warning(png_ptr, "zero length keyword"); - return ((png_size_t)0); - } - - png_debug1(2, "Keyword to be checked is '%s'\n", key); - - *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); - if (*new_key == NULL) - { - png_warning(png_ptr, "Out of memory while procesing keyword"); - return ((png_size_t)0); - } - - /* Replace non-printing characters with a blank and print a warning */ - for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) - { - if ((png_byte)*kp < 0x20 || - ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) - { -#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) - char msg[40]; - - png_snprintf(msg, 40, - "invalid keyword character 0x%02X", (png_byte)*kp); - png_warning(png_ptr, msg); -#else - png_warning(png_ptr, "invalid character in keyword"); -#endif - *dp = ' '; - } - else - { - *dp = *kp; - } - } - *dp = '\0'; - - /* Remove any trailing white space. */ - kp = *new_key + key_len - 1; - if (*kp == ' ') - { - png_warning(png_ptr, "trailing spaces removed from keyword"); - - while (*kp == ' ') - { - *(kp--) = '\0'; - key_len--; - } - } - - /* Remove any leading white space. */ - kp = *new_key; - if (*kp == ' ') - { - png_warning(png_ptr, "leading spaces removed from keyword"); - - while (*kp == ' ') - { - kp++; - key_len--; - } - } - - png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp); - - /* Remove multiple internal spaces. */ - for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) - { - if (*kp == ' ' && kflag == 0) - { - *(dp++) = *kp; - kflag = 1; - } - else if (*kp == ' ') - { - key_len--; - kwarn=1; - } - else - { - *(dp++) = *kp; - kflag = 0; - } - } - *dp = '\0'; - if(kwarn) - png_warning(png_ptr, "extra interior spaces removed from keyword"); + key_len = png_check_keyword(png_ptr, key, new_key); if (key_len == 0) - { - png_free(png_ptr, *new_key); - *new_key=NULL; - png_warning(png_ptr, "Zero length keyword"); - } - - if (key_len > 79) - { - png_warning(png_ptr, "keyword length must be 1 - 79 characters"); - new_key[79] = '\0'; - key_len = 79; - } - - return (key_len); -} -#endif - -#if defined(PNG_WRITE_tEXt_SUPPORTED) -/* write a tEXt chunk */ -void /* PRIVATE */ -png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, - png_size_t text_len) -{ -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_tEXt; -#endif - png_size_t key_len; - png_charp new_key; - - png_debug(1, "in png_write_tEXt\n"); - if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) - { - png_warning(png_ptr, "Empty keyword in tEXt chunk"); - return; - } + png_error(png_ptr, "tEXt: invalid keyword"); if (text == NULL || *text == '\0') text_len = 0; - else - text_len = png_strlen(text); - /* make sure we include the 0 after the key */ - png_write_chunk_start(png_ptr, png_tEXt, (png_uint_32)key_len+text_len+1); + else + text_len = strlen(text); + + if (text_len > PNG_UINT_31_MAX - (key_len+1)) + png_error(png_ptr, "tEXt: text too long"); + + /* Make sure we include the 0 after the key */ + png_write_chunk_header(png_ptr, png_tEXt, + (png_uint_32)/*checked above*/(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); + png_write_chunk_data(png_ptr, new_key, key_len + 1); + if (text_len) - png_write_chunk_data(png_ptr, (png_bytep)text, text_len); + png_write_chunk_data(png_ptr, (png_const_bytep)text, text_len); png_write_chunk_end(png_ptr); - png_free(png_ptr, new_key); } #endif -#if defined(PNG_WRITE_zTXt_SUPPORTED) -/* write a compressed text chunk */ +#ifdef PNG_WRITE_zTXt_SUPPORTED +/* Write a compressed text chunk */ void /* PRIVATE */ -png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, - png_size_t text_len, int compression) +png_write_zTXt(png_structrp png_ptr, png_const_charp key, png_const_charp text, + png_size_t text_len, int compression) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_zTXt; -#endif - png_size_t key_len; - char buf[1]; - png_charp new_key; + png_uint_32 key_len; + png_byte new_key[81]; compression_state comp; - png_debug(1, "in png_write_zTXt\n"); + png_debug(1, "in png_write_zTXt"); + PNG_UNUSED(text_len) /* Always use strlen */ - comp.num_output_ptr = 0; - comp.max_output_ptr = 0; - comp.output_ptr = NULL; - comp.input = NULL; - comp.input_len = 0; - - if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + if (compression == PNG_TEXT_COMPRESSION_NONE) { - png_warning(png_ptr, "Empty keyword in zTXt chunk"); + png_write_tEXt(png_ptr, key, text, 0); return; } - if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) - { - png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); - png_free(png_ptr, new_key); - return; - } + if (compression != PNG_TEXT_COMPRESSION_zTXt) + png_error(png_ptr, "zTXt: invalid compression type"); - text_len = png_strlen(text); + key_len = png_check_keyword(png_ptr, key, new_key); - /* compute the compressed data; do it now for the length */ - text_len = png_text_compress(png_ptr, text, text_len, compression, - &comp); + if (key_len == 0) + png_error(png_ptr, "zTXt: invalid keyword"); - /* 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); + /* Add the compression method and 1 for the keyword separator. */ + new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; + ++key_len; - buf[0] = (png_byte)compression; - /* write compression */ - png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); - /* write the compressed data */ + /* Compute the compressed data; do it now for the length */ + png_text_compress_init(&comp, (png_const_bytep)text, + text == NULL ? 0 : strlen(text)); + + if (png_text_compress(png_ptr, png_zTXt, &comp, key_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + + /* Write start of chunk */ + png_write_chunk_header(png_ptr, png_zTXt, key_len + comp.output_len); + + /* Write key */ + png_write_chunk_data(png_ptr, new_key, key_len); + + /* Write the compressed data */ png_write_compressed_data_out(png_ptr, &comp); - /* close the chunk */ + /* Close the chunk */ png_write_chunk_end(png_ptr); } #endif -#if defined(PNG_WRITE_iTXt_SUPPORTED) -/* write an iTXt chunk */ +#ifdef PNG_WRITE_iTXt_SUPPORTED +/* Write an iTXt chunk */ void /* PRIVATE */ -png_write_iTXt(png_structp png_ptr, int compression, png_charp key, - png_charp lang, png_charp lang_key, png_charp text) +png_write_iTXt(png_structrp png_ptr, int compression, png_const_charp key, + png_const_charp lang, png_const_charp lang_key, png_const_charp text) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_iTXt; -#endif - png_size_t lang_len, key_len, lang_key_len, text_len; - png_charp new_lang, new_key; - png_byte cbuf[2]; + png_uint_32 key_len, prefix_len; + png_size_t lang_len, lang_key_len; + png_byte new_key[82]; compression_state comp; - png_debug(1, "in png_write_iTXt\n"); + png_debug(1, "in png_write_iTXt"); - comp.num_output_ptr = 0; - comp.max_output_ptr = 0; - comp.output_ptr = NULL; - comp.input = NULL; + key_len = png_check_keyword(png_ptr, key, new_key); - if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + if (key_len == 0) + png_error(png_ptr, "iTXt: invalid keyword"); + + /* Set the compression flag */ + switch (compression) { - png_warning(png_ptr, "Empty keyword in iTXt chunk"); - return; - } - if (lang == NULL || (lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) - { - png_warning(png_ptr, "Empty language field in iTXt chunk"); - new_lang = NULL; - lang_len = 0; + case PNG_ITXT_COMPRESSION_NONE: + case PNG_TEXT_COMPRESSION_NONE: + compression = new_key[++key_len] = 0; /* no compression */ + break; + + case PNG_TEXT_COMPRESSION_zTXt: + case PNG_ITXT_COMPRESSION_zTXt: + compression = new_key[++key_len] = 1; /* compressed */ + break; + + default: + png_error(png_ptr, "iTXt: invalid compression"); } - if (lang_key == NULL) - lang_key_len = 0; - else - lang_key_len = png_strlen(lang_key); + new_key[++key_len] = PNG_COMPRESSION_TYPE_BASE; + ++key_len; /* for the keywod separator */ - if (text == NULL) - text_len = 0; - else - text_len = png_strlen(text); - - /* compute the compressed data; do it now for the length */ - text_len = png_text_compress(png_ptr, text, text_len, compression-2, - &comp); - - - /* make sure we include the compression flag, the compression byte, - * and the NULs after the key, lang, and lang_key parts */ - - png_write_chunk_start(png_ptr, png_iTXt, - (png_uint_32)( - 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ - + key_len - + lang_len - + lang_key_len - + text_len)); - - /* - * We leave it to the application to meet PNG-1.0 requirements on the + /* 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. + * any non-Latin-1 characters except for NEWLINE. ISO PNG, however, + * specifies that the text is UTF-8 and this really doesn't require any + * checking. + * * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + * + * TODO: validate the language tag correctly (see the spec.) */ - png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + if (lang == NULL) lang = ""; /* empty language is valid */ + lang_len = strlen(lang)+1; + if (lang_key == NULL) lang_key = ""; /* may be empty */ + lang_key_len = strlen(lang_key)+1; + if (text == NULL) text = ""; /* may be empty */ - /* 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); + prefix_len = key_len; + if (lang_len > PNG_UINT_31_MAX-prefix_len) + prefix_len = PNG_UINT_31_MAX; + else + prefix_len = (png_uint_32)(prefix_len + lang_len); - cbuf[0] = 0; - png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), lang_len + 1); - png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), lang_key_len + 1); - png_write_compressed_data_out(png_ptr, &comp); + if (lang_key_len > PNG_UINT_31_MAX-prefix_len) + prefix_len = PNG_UINT_31_MAX; + else + prefix_len = (png_uint_32)(prefix_len + lang_key_len); + + png_text_compress_init(&comp, (png_const_bytep)text, strlen(text)); + + if (compression) + { + if (png_text_compress(png_ptr, png_iTXt, &comp, prefix_len) != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg); + } + + else + { + if (comp.input_len > PNG_UINT_31_MAX-prefix_len) + png_error(png_ptr, "iTXt: uncompressed text too long"); + } + + png_write_chunk_header(png_ptr, png_iTXt, comp.output_len + prefix_len); + + png_write_chunk_data(png_ptr, new_key, key_len); + + png_write_chunk_data(png_ptr, (png_const_bytep)lang, lang_len); + + png_write_chunk_data(png_ptr, (png_const_bytep)lang_key, lang_key_len); + + if (compression) + png_write_compressed_data_out(png_ptr, &comp); + + else + png_write_chunk_data(png_ptr, (png_const_bytep)text, comp.input_len); png_write_chunk_end(png_ptr); - png_free(png_ptr, new_key); - if (new_lang) - png_free(png_ptr, new_lang); } #endif -#if defined(PNG_WRITE_oFFs_SUPPORTED) -/* write the oFFs chunk */ +#ifdef PNG_WRITE_oFFs_SUPPORTED +/* Write the oFFs chunk */ void /* PRIVATE */ -png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, - int unit_type) +png_write_oFFs(png_structrp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_oFFs; -#endif png_byte buf[9]; - png_debug(1, "in png_write_oFFs\n"); + png_debug(1, "in png_write_oFFs"); + if (unit_type >= PNG_OFFSET_LAST) png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); @@ -1529,62 +1781,67 @@ png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, png_save_int_32(buf + 4, y_offset); buf[8] = (png_byte)unit_type; - png_write_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); + png_write_complete_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); } #endif -#if defined(PNG_WRITE_pCAL_SUPPORTED) -/* write the pCAL chunk (described in the PNG extensions document) */ +#ifdef PNG_WRITE_pCAL_SUPPORTED +/* Write the pCAL chunk (described in the PNG extensions document) */ void /* PRIVATE */ -png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, - png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +png_write_pCAL(png_structrp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_const_charp units, + png_charpp params) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_pCAL; -#endif - png_size_t purpose_len, units_len, total_len; - png_uint_32p params_len; + png_uint_32 purpose_len; + png_size_t units_len, total_len; + png_size_tp params_len; png_byte buf[10]; - png_charp new_purpose; + png_byte new_purpose[80]; int i; - png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams); - if (type >= PNG_EQUATION_LAST) - png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + png_debug1(1, "in png_write_pCAL (%d parameters)", nparams); - purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; - png_debug1(3, "pCAL purpose length = %d\n", (int)purpose_len); - units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); - png_debug1(3, "pCAL units length = %d\n", (int)units_len); + if (type >= PNG_EQUATION_LAST) + png_error(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, new_purpose); + + if (purpose_len == 0) + png_error(png_ptr, "pCAL: invalid keyword"); + + ++purpose_len; /* terminator */ + + png_debug1(3, "pCAL purpose length = %d", (int)purpose_len); + units_len = strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d", (int)units_len); total_len = purpose_len + units_len + 10; - params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams - *png_sizeof(png_uint_32))); + params_len = (png_size_tp)png_malloc(png_ptr, + (png_alloc_size_t)(nparams * (sizeof (png_size_t)))); /* Find the length of each parameter, making sure we don't count the - null terminator for the last parameter. */ + * null terminator for the last parameter. + */ for (i = 0; i < nparams; i++) { - params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); - png_debug2(3, "pCAL parameter %d length = %lu\n", i, params_len[i]); - total_len += (png_size_t)params_len[i]; + params_len[i] = strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu", i, + (unsigned long)params_len[i]); + total_len += params_len[i]; } - png_debug1(3, "pCAL total length = %d\n", (int)total_len); - png_write_chunk_start(png_ptr, png_pCAL, (png_uint_32)total_len); - png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len); + png_debug1(3, "pCAL total length = %d", (int)total_len); + png_write_chunk_header(png_ptr, png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, new_purpose, purpose_len); png_save_int_32(buf, X0); png_save_int_32(buf + 4, X1); buf[8] = (png_byte)type; buf[9] = (png_byte)nparams; png_write_chunk_data(png_ptr, buf, (png_size_t)10); - png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); - - png_free(png_ptr, new_purpose); + png_write_chunk_data(png_ptr, (png_const_bytep)units, (png_size_t)units_len); for (i = 0; i < nparams; i++) { - png_write_chunk_data(png_ptr, (png_bytep)params[i], - (png_size_t)params_len[i]); + png_write_chunk_data(png_ptr, (png_const_bytep)params[i], params_len[i]); } png_free(png_ptr, params_len); @@ -1592,63 +1849,21 @@ 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) +#ifdef PNG_WRITE_sCAL_SUPPORTED +/* Write the sCAL chunk */ void /* PRIVATE */ -png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +png_write_sCAL_s(png_structrp png_ptr, int unit, png_const_charp width, + png_const_charp height) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_sCAL; -#endif - char buf[64]; - png_size_t total_len; - - png_debug(1, "in png_write_sCAL\n"); - - buf[0] = (char)unit; -#if defined(_WIN32_WCE) -/* sprintf() function is not supported on WindowsCE */ - { - wchar_t wc_buf[32]; - size_t wc_len; - swprintf(wc_buf, TEXT("%12.12e"), width); - wc_len = wcslen(wc_buf); - WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, NULL); - total_len = wc_len + 2; - swprintf(wc_buf, TEXT("%12.12e"), height); - wc_len = wcslen(wc_buf); - WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len, - NULL, NULL); - total_len += wc_len; - } -#else - png_snprintf(buf + 1, 63, "%12.12e", width); - total_len = 1 + png_strlen(buf + 1) + 1; - png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); - total_len += png_strlen(buf + total_len); -#endif - - png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); - png_write_chunk(png_ptr, png_sCAL, (png_bytep)buf, total_len); -} -#else -#ifdef PNG_FIXED_POINT_SUPPORTED -void /* PRIVATE */ -png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, - png_charp height) -{ -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_sCAL; -#endif png_byte buf[64]; png_size_t wlen, hlen, total_len; - png_debug(1, "in png_write_sCAL_s\n"); + png_debug(1, "in png_write_sCAL_s"); - wlen = png_strlen(width); - hlen = png_strlen(height); + wlen = strlen(width); + hlen = strlen(height); total_len = wlen + hlen + 2; + if (total_len > 64) { png_warning(png_ptr, "Can't write sCAL (buffer too small)"); @@ -1656,29 +1871,25 @@ png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, } buf[0] = (png_byte)unit; - png_memcpy(buf + 1, width, wlen + 1); /* append the '\0' here */ - png_memcpy(buf + wlen + 2, height, hlen); /* do NOT append the '\0' here */ + memcpy(buf + 1, width, wlen + 1); /* Append the '\0' here */ + memcpy(buf + wlen + 2, height, hlen); /* Do NOT append the '\0' here */ - png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); - png_write_chunk(png_ptr, png_sCAL, buf, total_len); + png_debug1(3, "sCAL total length = %u", (unsigned int)total_len); + png_write_complete_chunk(png_ptr, png_sCAL, buf, total_len); } #endif -#endif -#endif -#if defined(PNG_WRITE_pHYs_SUPPORTED) -/* write the pHYs chunk */ +#ifdef PNG_WRITE_pHYs_SUPPORTED +/* Write the pHYs chunk */ void /* PRIVATE */ -png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, - png_uint_32 y_pixels_per_unit, - int unit_type) +png_write_pHYs(png_structrp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_pHYs; -#endif png_byte buf[9]; - png_debug(1, "in png_write_pHYs\n"); + png_debug(1, "in png_write_pHYs"); + if (unit_type >= PNG_RESOLUTION_LAST) png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); @@ -1686,23 +1897,21 @@ png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, png_save_uint_32(buf + 4, y_pixels_per_unit); buf[8] = (png_byte)unit_type; - png_write_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); + png_write_complete_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); } #endif -#if defined(PNG_WRITE_tIME_SUPPORTED) +#ifdef PNG_WRITE_tIME_SUPPORTED /* Write the tIME chunk. Use either png_convert_from_struct_tm() * or png_convert_from_time_t(), or fill in the structure yourself. */ void /* PRIVATE */ -png_write_tIME(png_structp png_ptr, png_timep mod_time) +png_write_tIME(png_structrp png_ptr, png_const_timep mod_time) { -#ifdef PNG_USE_LOCAL_ARRAYS - PNG_tIME; -#endif png_byte buf[7]; - png_debug(1, "in png_write_tIME\n"); + png_debug(1, "in png_write_tIME"); + if (mod_time->month > 12 || mod_time->month < 1 || mod_time->day > 31 || mod_time->day < 1 || mod_time->hour > 23 || mod_time->second > 60) @@ -1718,142 +1927,147 @@ png_write_tIME(png_structp png_ptr, png_timep mod_time) buf[5] = mod_time->minute; buf[6] = mod_time->second; - png_write_chunk(png_ptr, png_tIME, buf, (png_size_t)7); + png_write_complete_chunk(png_ptr, png_tIME, buf, (png_size_t)7); } #endif -/* initializes the row writing capability of libpng */ +/* Initializes the row writing capability of libpng */ void /* PRIVATE */ -png_write_start_row(png_structp png_ptr) +png_write_start_row(png_structrp png_ptr) { #ifdef PNG_WRITE_INTERLACING_SUPPORTED -#ifdef PNG_USE_LOCAL_ARRAYS - /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* 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}; + /* Start of interlace block */ + static PNG_CONST png_byte 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}; + /* Offset to next interlace block */ + static PNG_CONST png_byte 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}; + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte 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 + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif - png_size_t buf_size; + png_alloc_size_t buf_size; + int usr_pixel_depth; - png_debug(1, "in png_write_start_row\n"); - buf_size = (png_size_t)(PNG_ROWBYTES( - png_ptr->usr_channels*png_ptr->usr_bit_depth,png_ptr->width)+1); + png_debug(1, "in png_write_start_row"); + + usr_pixel_depth = png_ptr->usr_channels * png_ptr->usr_bit_depth; + buf_size = PNG_ROWBYTES(usr_pixel_depth, png_ptr->width) + 1; + + /* 1.5.6: added to allow checking in the row write code. */ + png_ptr->transformed_pixel_depth = png_ptr->pixel_depth; + png_ptr->maximum_pixel_depth = (png_byte)usr_pixel_depth; + + /* Set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, buf_size); - /* 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 */ +#ifdef PNG_WRITE_FILTER_SUPPORTED + /* Set up filtering buffer, if using this filter */ if (png_ptr->do_filter & PNG_FILTER_SUB) { - png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, png_ptr->rowbytes + 1); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; } /* We only need to keep the previous row if we are using one of these. */ if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) { - /* set up previous row buffer */ - png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); - png_memset(png_ptr->prev_row, 0, buf_size); + /* Set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_calloc(png_ptr, buf_size); if (png_ptr->do_filter & PNG_FILTER_UP) { png_ptr->up_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + png_ptr->rowbytes + 1); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; } if (png_ptr->do_filter & PNG_FILTER_AVG) { png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + png_ptr->rowbytes + 1); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; } if (png_ptr->do_filter & PNG_FILTER_PAETH) { png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, - (png_ptr->rowbytes + 1)); + png_ptr->rowbytes + 1); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; } -#endif /* PNG_NO_WRITE_FILTERING */ } +#endif /* PNG_WRITE_FILTER_SUPPORTED */ #ifdef PNG_WRITE_INTERLACING_SUPPORTED - /* if interlaced, we need to set up width and height of pass */ + /* If interlaced, we need to set up width and height of pass */ if (png_ptr->interlaced) { if (!(png_ptr->transformations & PNG_INTERLACE)) { png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - - png_pass_ystart[0]) / png_pass_yinc[0]; + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - - png_pass_start[0]) / png_pass_inc[0]; + png_pass_start[0]) / png_pass_inc[0]; } + else { png_ptr->num_rows = png_ptr->height; png_ptr->usr_width = png_ptr->width; } } + else #endif { png_ptr->num_rows = png_ptr->height; png_ptr->usr_width = png_ptr->width; } - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - png_ptr->zstream.next_out = png_ptr->zbuf; } /* Internal use only. Called when finished processing a row of data. */ void /* PRIVATE */ -png_write_finish_row(png_structp png_ptr) +png_write_finish_row(png_structrp png_ptr) { #ifdef PNG_WRITE_INTERLACING_SUPPORTED -#ifdef PNG_USE_LOCAL_ARRAYS - /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* 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}; + /* Start of interlace block */ + static PNG_CONST png_byte 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}; + /* Offset to next interlace block */ + static PNG_CONST png_byte 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}; + /* Start of interlace block in the y direction */ + static PNG_CONST png_byte 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 + /* Offset to next interlace block in the y direction */ + static PNG_CONST png_byte png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; #endif - int ret; + png_debug(1, "in png_write_finish_row"); - png_debug(1, "in png_write_finish_row\n"); - /* next row */ + /* Next row */ png_ptr->row_number++; - /* see if we are done */ + /* 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 interlaced, go to next pass */ if (png_ptr->interlaced) { png_ptr->row_number = 0; @@ -1861,78 +2075,53 @@ png_write_finish_row(png_structp png_ptr) { png_ptr->pass++; } + else { - /* loop until we find a non-zero width or height pass */ + /* Loop until we find a non-zero width or height pass */ do { png_ptr->pass++; + if (png_ptr->pass >= 7) break; + png_ptr->usr_width = (png_ptr->width + - png_pass_inc[png_ptr->pass] - 1 - - png_pass_start[png_ptr->pass]) / - png_pass_inc[png_ptr->pass]; + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + - png_pass_yinc[png_ptr->pass] - 1 - - png_pass_ystart[png_ptr->pass]) / - png_pass_yinc[png_ptr->pass]; + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); } - /* reset the row above the image for the next pass */ + /* Reset the row above the image for the next pass */ if (png_ptr->pass < 7) { if (png_ptr->prev_row != NULL) - png_memset(png_ptr->prev_row, 0, - (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* - png_ptr->usr_bit_depth,png_ptr->width))+1); + memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth, png_ptr->width)) + 1); + return; } } #endif - /* if we get here, we've just written the last row, so we need + /* If we get here, we've just written the last row, so we need to flush the compressor */ - do - { - /* tell the compressor we are done */ - ret = deflate(&png_ptr->zstream, Z_FINISH); - /* check for an error */ - if (ret == Z_OK) - { - /* check to see if we need more room */ - if (!(png_ptr->zstream.avail_out)) - { - png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); - png_ptr->zstream.next_out = png_ptr->zbuf; - png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; - } - } - else if (ret != Z_STREAM_END) - { - if (png_ptr->zstream.msg != NULL) - png_error(png_ptr, png_ptr->zstream.msg); - else - png_error(png_ptr, "zlib error"); - } - } while (ret != Z_STREAM_END); - - /* write any extra space */ - if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) - { - png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - - png_ptr->zstream.avail_out); - } - - deflateReset(&png_ptr->zstream); - png_ptr->zstream.data_type = Z_BINARY; + png_compress_IDAT(png_ptr, NULL, 0, Z_FINISH); } -#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +#ifdef PNG_WRITE_INTERLACING_SUPPORTED /* Pick out the correct pixels for the interlace pass. * The basic idea here is to go through the row with a source * pointer and a destination pointer (sp and dp), and copy the @@ -1943,25 +2132,20 @@ png_write_finish_row(png_structp png_ptr) 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 */ + /* 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}; + /* Start of interlace block */ + static PNG_CONST png_byte 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 + /* Offset to next interlace block */ + static PNG_CONST png_byte png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; - 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 + png_debug(1, "in png_do_write_interlace"); + + /* We don't have to do anything on the last pass (6) */ if (pass < 6) -#endif { - /* each pixel depth is handled separately */ + /* Each pixel depth is handled separately */ switch (row_info->pixel_depth) { case 1: @@ -1977,6 +2161,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) dp = row; d = 0; shift = 7; + for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { @@ -1990,14 +2175,17 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) *dp++ = (png_byte)d; d = 0; } + else shift--; } if (shift != 7) *dp = (png_byte)d; + break; } + case 2: { png_bytep sp; @@ -2011,6 +2199,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) dp = row; shift = 6; d = 0; + for (i = png_pass_start[pass]; i < row_width; i += png_pass_inc[pass]) { @@ -2024,13 +2213,16 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) *dp++ = (png_byte)d; d = 0; } + else shift -= 2; } if (shift != 6) - *dp = (png_byte)d; + *dp = (png_byte)d; + break; } + case 4: { png_bytep sp; @@ -2045,7 +2237,7 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) shift = 4; d = 0; for (i = png_pass_start[pass]; i < row_width; - i += png_pass_inc[pass]) + i += png_pass_inc[pass]) { sp = row + (png_size_t)(i >> 1); value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; @@ -2057,13 +2249,16 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) *dp++ = (png_byte)d; d = 0; } + else shift -= 4; } if (shift != 4) *dp = (png_byte)d; + break; } + default: { png_bytep sp; @@ -2072,33 +2267,37 @@ 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 */ + /* Start at the beginning */ dp = row; - /* find out how many bytes each pixel takes up */ + + /* 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 */ + + /* 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 */ + /* Find out where the original pixel is */ sp = row + (png_size_t)i * pixel_bytes; - /* move the pixel */ + + /* Move the pixel */ if (dp != sp) - png_memcpy(dp, sp, pixel_bytes); - /* next pixel */ + memcpy(dp, sp, pixel_bytes); + + /* Next pixel */ dp += pixel_bytes; } break; } } - /* set new row width */ + /* Set new row width */ row_info->width = (row_info->width + - png_pass_inc[pass] - 1 - - png_pass_start[pass]) / - png_pass_inc[pass]; - row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, - row_info->width); + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); } } #endif @@ -2107,31 +2306,43 @@ png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) * been specified by the application, and then writes the row out with the * chosen filter. */ +static void png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, + png_size_t row_bytes); + #define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) #define PNG_HISHIFT 10 #define PNG_LOMASK ((png_uint_32)0xffffL) #define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) void /* PRIVATE */ -png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +png_write_find_filter(png_structrp png_ptr, png_row_infop row_info) { png_bytep best_row; -#ifndef PNG_NO_WRITE_FILTER +#ifdef PNG_WRITE_FILTER_SUPPORTED png_bytep prev_row, row_buf; png_uint_32 mins, bpp; png_byte filter_to_do = png_ptr->do_filter; - png_uint_32 row_bytes = row_info->rowbytes; -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) - int num_p_filters = (int)png_ptr->num_prev_filters; + png_size_t row_bytes = row_info->rowbytes; +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + int num_p_filters = png_ptr->num_prev_filters; #endif - png_debug(1, "in png_write_find_filter\n"); - /* find out how many bytes offset each pixel is */ + png_debug(1, "in png_write_find_filter"); + +#ifndef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED + if (png_ptr->row_number == 0 && filter_to_do == PNG_ALL_FILTERS) + { + /* These will never be selected so we need not test them. */ + filter_to_do &= ~(PNG_FILTER_UP | PNG_FILTER_PAETH); + } +#endif + + /* Find out how many bytes offset each pixel is */ bpp = (row_info->pixel_depth + 7) >> 3; prev_row = png_ptr->prev_row; #endif best_row = png_ptr->row_buf; -#ifndef PNG_NO_WRITE_FILTER +#ifdef PNG_WRITE_FILTER_SUPPORTED row_buf = best_row; mins = PNG_MAXSUM; @@ -2147,11 +2358,14 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) * 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) */ @@ -2160,12 +2374,11 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) /* 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) + if ((filter_to_do & PNG_FILTER_NONE) && filter_to_do != PNG_FILTER_NONE) { png_bytep rp; png_uint_32 sum = 0; - png_uint_32 i; + png_size_t i; int v; for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) @@ -2174,7 +2387,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) sum += (v < 128) ? v : 256 - v; } -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { png_uint_32 sumhi, sumlo; @@ -2188,9 +2401,10 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } @@ -2199,12 +2413,14 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) * it has the minimum possible computational cost - none). */ sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; + else sum = (sumhi << PNG_HISHIFT) + sumlo; } @@ -2212,22 +2428,25 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) mins = sum; } - /* sub filter */ + /* Sub filter */ if (filter_to_do == PNG_FILTER_SUB) - /* it's the only filter so no testing is needed */ + /* It's the only filter so no testing is needed */ { png_bytep rp, lp, dp; - png_uint_32 i; + png_size_t i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; i++, rp++, dp++) { *dp = *rp; } + for (lp = row_buf + 1; i < row_bytes; i++, rp++, lp++, dp++) { *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); } + best_row = png_ptr->sub_row; } @@ -2235,10 +2454,10 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) { png_bytep rp, dp, lp; png_uint_32 sum = 0, lmins = mins; - png_uint_32 i; + png_size_t i; int v; -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* We temporarily increase the "minimum sum" by the factor we * would reduce the sum of this filter, so that we can do the * early exit comparison without scaling the sum each time. @@ -2255,19 +2474,22 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; + else lmins = (lmhi << PNG_HISHIFT) + lmlo; } @@ -2280,6 +2502,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) sum += (v < 128) ? v : 256 - v; } + for (lp = row_buf + 1; i < row_bytes; i++, rp++, lp++, dp++) { @@ -2291,7 +2514,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) break; } -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; @@ -2304,19 +2527,22 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) { sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; + else sum = (sumhi << PNG_HISHIFT) + sumlo; } @@ -2329,18 +2555,19 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) } } - /* up filter */ + /* Up filter */ if (filter_to_do == PNG_FILTER_UP) { png_bytep rp, dp, pp; - png_uint_32 i; + png_size_t i; for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, - pp = prev_row + 1; i < row_bytes; - i++, rp++, pp++, dp++) + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) { *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); } + best_row = png_ptr->up_row; } @@ -2348,11 +2575,11 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) { png_bytep rp, dp, pp; png_uint_32 sum = 0, lmins = mins; - png_uint_32 i; + png_size_t i; int v; -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; @@ -2365,26 +2592,29 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; + else lmins = (lmhi << PNG_HISHIFT) + lmlo; } #endif for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, - pp = prev_row + 1; i < row_bytes; i++) + pp = prev_row + 1; i < row_bytes; i++) { v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); @@ -2394,7 +2624,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) break; } -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; @@ -2407,19 +2637,22 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; + else sum = (sumhi << PNG_HISHIFT) + sumlo; } @@ -2432,16 +2665,18 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) } } - /* avg filter */ + /* Avg filter */ if (filter_to_do == PNG_FILTER_AVG) { png_bytep rp, dp, pp, lp; png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, pp = prev_row + 1; i < bpp; i++) { *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); } + for (lp = row_buf + 1; i < row_bytes; i++) { *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) @@ -2454,10 +2689,10 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) { png_bytep rp, dp, pp, lp; png_uint_32 sum = 0, lmins = mins; - png_uint_32 i; + png_size_t i; int v; -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; @@ -2470,19 +2705,22 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; + else lmins = (lmhi << PNG_HISHIFT) + lmlo; } @@ -2495,10 +2733,11 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) sum += (v < 128) ? v : 256 - v; } + for (lp = row_buf + 1; i < row_bytes; i++) { v = *dp++ = - (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); sum += (v < 128) ? v : 256 - v; @@ -2506,7 +2745,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) break; } -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; @@ -2519,19 +2758,22 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; + else sum = (sumhi << PNG_HISHIFT) + sumlo; } @@ -2548,9 +2790,10 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (filter_to_do == PNG_FILTER_PAETH) { png_bytep rp, dp, pp, cp, lp; - png_uint_32 i; + png_size_t i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, - pp = prev_row + 1; i < bpp; i++) + pp = prev_row + 1; i < bpp; i++) { *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); } @@ -2587,10 +2830,10 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) { png_bytep rp, dp, pp, cp, lp; png_uint_32 sum = 0, lmins = mins; - png_uint_32 i; + png_size_t i; int v; -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; @@ -2603,26 +2846,29 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) { lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (lmhi > PNG_HIMASK) lmins = PNG_MAXSUM; + else lmins = (lmhi << PNG_HISHIFT) + lmlo; } #endif for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, - pp = prev_row + 1; i < bpp; i++) + pp = prev_row + 1; i < bpp; i++) { v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); @@ -2655,10 +2901,13 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) pa = abs(p - a); pb = abs(p - b); pc = abs(p - c); + if (pa <= pb && pa <= pc) p = a; + else if (pb <= pc) p = b; + else p = c; #endif /* PNG_SLOW_PAETH */ @@ -2671,7 +2920,7 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) break; } -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) { int j; @@ -2684,19 +2933,22 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) { sumlo = (sumlo * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> - PNG_WEIGHT_SHIFT; + PNG_WEIGHT_SHIFT; } } sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> - PNG_COST_SHIFT; + PNG_COST_SHIFT; if (sumhi > PNG_HIMASK) sum = PNG_MAXSUM; + else sum = (sumhi << PNG_HISHIFT) + sumlo; } @@ -2707,66 +2959,42 @@ png_write_find_filter(png_structp png_ptr, png_row_infop row_info) best_row = png_ptr->paeth_row; } } -#endif /* PNG_NO_WRITE_FILTER */ +#endif /* PNG_WRITE_FILTER_SUPPORTED */ + /* Do the actual writing of the filtered row data from the chosen filter. */ + png_write_filtered_row(png_ptr, best_row, row_info->rowbytes+1); - png_write_filtered_row(png_ptr, best_row); - -#ifndef PNG_NO_WRITE_FILTER -#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) +#ifdef PNG_WRITE_FILTER_SUPPORTED +#ifdef PNG_WRITE_WEIGHTED_FILTER_SUPPORTED /* Save the type of filter we picked this time for future calculations */ if (png_ptr->num_prev_filters > 0) { int j; + for (j = 1; j < num_p_filters; j++) { png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; } + png_ptr->prev_filters[j] = best_row[0]; } #endif -#endif /* PNG_NO_WRITE_FILTER */ +#endif /* PNG_WRITE_FILTER_SUPPORTED */ } /* Do the actual writing of a previously filtered row. */ -void /* PRIVATE */ -png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +static void +png_write_filtered_row(png_structrp png_ptr, png_bytep filtered_row, + png_size_t full_row_length/*includes filter byte*/) { - 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_debug(1, "in png_write_filtered_row"); - 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 */ + png_debug1(2, "filter = %d", filtered_row[0]); - /* compress the data */ - ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); - /* check for compression errors */ - if (ret != Z_OK) - { - if (png_ptr->zstream.msg != NULL) - png_error(png_ptr, png_ptr->zstream.msg); - else - png_error(png_ptr, "zlib error"); - } + png_compress_IDAT(png_ptr, filtered_row, full_row_length, Z_NO_FLUSH); - /* 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 */ + /* Swap the current and previous rows */ if (png_ptr->prev_row != NULL) { png_bytep tptr; @@ -2776,10 +3004,10 @@ 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 */ + /* Finish row - updates counters and flushes zlib if last row */ png_write_finish_row(png_ptr); -#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#ifdef PNG_WRITE_FLUSH_SUPPORTED png_ptr->flush_rows++; if (png_ptr->flush_dist > 0 && diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp b/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp index cf49ccb..d9aa0c4 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_Image.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -32,6 +31,12 @@ ImagePixelData::ImagePixelData (const Image::PixelFormat format, const int w, co ImagePixelData::~ImagePixelData() { + listeners.call (&Listener::imageDataBeingDeleted, this); +} + +void ImagePixelData::sendDataChangeMessage() +{ + listeners.call (&Listener::imageDataChanged, this); } //============================================================================== @@ -51,20 +56,11 @@ Image ImageType::convert (const Image& source) const jassert (src.pixelStride == dest.pixelStride && src.pixelFormat == dest.pixelFormat); for (int y = 0; y < dest.height; ++y) - memcpy (dest.getLinePointer (y), src.getLinePointer (y), dest.lineStride); + memcpy (dest.getLinePointer (y), src.getLinePointer (y), (size_t) dest.lineStride); return newImage; } -//============================================================================== -NativeImageType::NativeImageType() {} -NativeImageType::~NativeImageType() {} - -int NativeImageType::getTypeID() const -{ - return 1; -} - //============================================================================== class SoftwarePixelData : public ImagePixelData { @@ -77,39 +73,43 @@ public: imageData.allocate ((size_t) (lineStride * jmax (1, h)), clearImage); } - LowLevelGraphicsContext* createLowLevelContext() + LowLevelGraphicsContext* createLowLevelContext() override { + sendDataChangeMessage(); return new LowLevelGraphicsSoftwareRenderer (Image (this)); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; + + if (mode != Image::BitmapData::readOnly) + sendDataChangeMessage(); } - ImagePixelData* clone() + ImagePixelData* clone() override { SoftwarePixelData* s = new SoftwarePixelData (pixelFormat, width, height, false); memcpy (s->imageData, imageData, (size_t) (lineStride * height)); return s; } - ImageType* createType() const { return new SoftwareImageType(); } + ImageType* createType() const override { return new SoftwareImageType(); } private: HeapBlock imageData; const int pixelStride, lineStride; - JUCE_LEAK_DETECTOR (SoftwarePixelData); + JUCE_LEAK_DETECTOR (SoftwarePixelData) }; SoftwareImageType::SoftwareImageType() {} SoftwareImageType::~SoftwareImageType() {} -ImagePixelData* SoftwareImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const +ImagePixelData::Ptr SoftwareImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { return new SoftwarePixelData (format, width, height, clearImage); } @@ -119,30 +119,49 @@ int SoftwareImageType::getTypeID() const return 2; } +//============================================================================== +NativeImageType::NativeImageType() {} +NativeImageType::~NativeImageType() {} + +int NativeImageType::getTypeID() const +{ + return 1; +} + +#if JUCE_WINDOWS || JUCE_LINUX +ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const +{ + return new SoftwarePixelData (format, width, height, clearImage); +} +#endif + //============================================================================== class SubsectionPixelData : public ImagePixelData { public: - SubsectionPixelData (ImagePixelData* const image_, const Rectangle& area_) - : ImagePixelData (image_->pixelFormat, area_.getWidth(), area_.getHeight()), - image (image_), area (area_) + SubsectionPixelData (ImagePixelData* const im, const Rectangle& r) + : ImagePixelData (im->pixelFormat, r.getWidth(), r.getHeight()), + image (im), area (r) { } - LowLevelGraphicsContext* createLowLevelContext() + LowLevelGraphicsContext* createLowLevelContext() override { LowLevelGraphicsContext* g = image->createLowLevelContext(); g->clipToRectangle (area); - g->setOrigin (area.getX(), area.getY()); + g->setOrigin (area.getPosition()); return g; } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { image->initialiseBitmapData (bitmap, x + area.getX(), y + area.getY(), mode); + + if (mode != Image::BitmapData::readOnly) + sendDataChangeMessage(); } - ImagePixelData* clone() + ImagePixelData* clone() override { jassert (getReferenceCount() > 0); // (This method can't be used on an unowned pointer, as it will end up self-deleting) const ScopedPointer type (image->createType()); @@ -151,20 +170,20 @@ public: { Graphics g (newImage); - g.drawImageAt (Image (this), -area.getX(), -area.getY()); + g.drawImageAt (Image (this), 0, 0); } newImage.getPixelData()->incReferenceCount(); return newImage.getPixelData(); } - ImageType* createType() const { return image->createType(); } + ImageType* createType() const override { return image->createType(); } private: - const ReferenceCountedObjectPtr image; + const ImagePixelData::Ptr image; const Rectangle area; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SubsectionPixelData) }; Image Image::getClippedImage (const Rectangle& area) const @@ -210,13 +229,13 @@ Image& Image::operator= (const Image& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS Image::Image (Image&& other) noexcept - : image (static_cast &&> (other.image)) + : image (static_cast (other.image)) { } Image& Image::operator= (Image&& other) noexcept { - image = static_cast &&> (other.image); + image = static_cast (other.image); return *this; } #endif @@ -250,7 +269,10 @@ void Image::duplicateIfShared() Image Image::createCopy() const { - return Image (image != nullptr ? image->clone() : nullptr); + if (image != nullptr) + return Image (image->clone()); + + return Image(); } Image Image::rescaled (const int newWidth, const int newHeight, const Graphics::ResamplingQuality quality) const @@ -263,8 +285,8 @@ Image Image::rescaled (const int newWidth, const int newHeight, const Graphics:: Graphics g (newImage); g.setImageResamplingQuality (quality); - g.drawImage (*this, 0, 0, newWidth, newHeight, 0, 0, image->width, image->height, false); - + g.drawImageTransformed (*this, AffineTransform::scale (newWidth / (float) image->width, + newHeight / (float) image->height), false); return newImage; } @@ -331,38 +353,36 @@ NamedValueSet* Image::getProperties() const } //============================================================================== -Image::BitmapData::BitmapData (Image& image, const int x, const int y, const int w, const int h, BitmapData::ReadWriteMode mode) - : width (w), - height (h) +Image::BitmapData::BitmapData (Image& im, const int x, const int y, const int w, const int h, BitmapData::ReadWriteMode mode) + : width (w), height (h) { // The BitmapData class must be given a valid image, and a valid rectangle within it! - jassert (image.image != nullptr); - jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); + jassert (im.image != nullptr); + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= im.getWidth() && y + h <= im.getHeight()); - image.image->initialiseBitmapData (*this, x, y, mode); + im.image->initialiseBitmapData (*this, x, y, mode); jassert (data != nullptr && pixelStride > 0 && lineStride != 0); } -Image::BitmapData::BitmapData (const Image& image, const int x, const int y, const int w, const int h) - : width (w), - height (h) +Image::BitmapData::BitmapData (const Image& im, const int x, const int y, const int w, const int h) + : width (w), height (h) { // The BitmapData class must be given a valid image, and a valid rectangle within it! - jassert (image.image != nullptr); - jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= image.getWidth() && y + h <= image.getHeight()); + jassert (im.image != nullptr); + jassert (x >= 0 && y >= 0 && w > 0 && h > 0 && x + w <= im.getWidth() && y + h <= im.getHeight()); - image.image->initialiseBitmapData (*this, x, y, readOnly); + im.image->initialiseBitmapData (*this, x, y, readOnly); jassert (data != nullptr && pixelStride > 0 && lineStride != 0); } -Image::BitmapData::BitmapData (const Image& image, BitmapData::ReadWriteMode mode) - : width (image.getWidth()), - height (image.getHeight()) +Image::BitmapData::BitmapData (const Image& im, BitmapData::ReadWriteMode mode) + : width (im.getWidth()), + height (im.getHeight()) { // The BitmapData class must be given a valid image! - jassert (image.image != nullptr); + jassert (im.image != nullptr); - image.image->initialiseBitmapData (*this, 0, 0, mode); + im.image->initialiseBitmapData (*this, 0, 0, mode); jassert (data != nullptr && pixelStride > 0 && lineStride != 0); } @@ -387,7 +407,7 @@ Colour Image::BitmapData::getPixelColour (const int x, const int y) const noexce return Colour(); } -void Image::BitmapData::setPixelColour (const int x, const int y, const Colour& colour) const noexcept +void Image::BitmapData::setPixelColour (const int x, const int y, Colour colour) const noexcept { jassert (isPositiveAndBelow (x, width) && isPositiveAndBelow (y, height)); @@ -404,7 +424,7 @@ void Image::BitmapData::setPixelColour (const int x, const int y, const Colour& } //============================================================================== -void Image::clear (const Rectangle& area, const Colour& colourToClearTo) +void Image::clear (const Rectangle& area, Colour colourToClearTo) { const ScopedPointer g (image->createLowLevelContext()); g->setFill (colourToClearTo); @@ -423,7 +443,7 @@ Colour Image::getPixelAt (const int x, const int y) const return Colour(); } -void Image::setPixelAt (const int x, const int y, const Colour& colour) +void Image::setPixelAt (const int x, const int y, Colour colour) { if (isPositiveAndBelow (x, getWidth()) && isPositiveAndBelow (y, getHeight())) { @@ -489,7 +509,7 @@ struct AlphaMultiplyOp pixel.multiplyAlpha (alpha); } - JUCE_DECLARE_NON_COPYABLE (AlphaMultiplyOp); + JUCE_DECLARE_NON_COPYABLE (AlphaMultiplyOp) }; void Image::multiplyAllAlphas (const float amountToMultiplyBy) @@ -518,7 +538,7 @@ void Image::desaturate() } } -void Image::createSolidAreaMask (RectangleList& result, const float alphaThreshold) const +void Image::createSolidAreaMask (RectangleList& result, const float alphaThreshold) const { if (hasAlphaChannel()) { diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h b/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h index 84f084d..3580f9f 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_Image.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IMAGE_JUCEHEADER__ -#define __JUCE_IMAGE_JUCEHEADER__ - -#include "../colour/juce_Colour.h" -#include "../contexts/juce_GraphicsContext.h" +#ifndef JUCE_IMAGE_H_INCLUDED +#define JUCE_IMAGE_H_INCLUDED class ImageType; class ImagePixelData; @@ -110,7 +106,7 @@ public: point to the same shared image data. To make sure that an Image object has its own unique, unshared internal data, call duplicateIfShared(). */ - Image (const Image& other); + Image (const Image&); /** Makes this image refer to the same underlying image as another object. @@ -121,7 +117,7 @@ public: Image& operator= (const Image&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - Image (Image&& other) noexcept; + Image (Image&&) noexcept; Image& operator= (Image&&) noexcept; #endif @@ -188,7 +184,7 @@ public: 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). */ - void clear (const Rectangle& area, const Colour& colourToClearTo = Colour (0x00000000)); + void clear (const Rectangle& area, Colour colourToClearTo = Colour (0x00000000)); /** Returns a rescaled version of this image. @@ -245,7 +241,7 @@ public: //============================================================================== /** Returns the colour of one of the pixels in the image. - If the co-ordinates given are beyond the image's boundaries, this will + If the coordinates given are beyond the image's boundaries, this will return Colours::transparentBlack. @see setPixelAt, Image::BitmapData::getPixelColour @@ -254,7 +250,7 @@ public: /** Sets the colour of one of the image's pixels. - If the co-ordinates are beyond the image's boundaries, then nothing will happen. + If the coordinates are beyond the image's boundaries, then nothing will happen. Note that 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 @@ -262,15 +258,15 @@ public: @see getPixelAt, Image::BitmapData::setPixelColour */ - void setPixelAt (int x, int y, const Colour& colour); + void setPixelAt (int x, int y, 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. + given coordinates 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. + at the given coordinates will be multiplied by this value. @see setPixelAt */ @@ -307,7 +303,7 @@ public: 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 + class JUCE_API BitmapData { public: enum ReadWriteMode @@ -323,13 +319,13 @@ public: ~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 + The coordinate 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 noexcept { 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 + The coordinates 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 noexcept { return data + y * lineStride + x * pixelStride; } @@ -344,11 +340,16 @@ public: For performance reasons, this won't do any bounds-checking on the coordinates, so it's the caller's repsonsibility to make sure they're within the image's size. */ - void setPixelColour (int x, int y, const Colour& colour) const noexcept; + void setPixelColour (int x, int y, Colour colour) const noexcept; - uint8* data; - PixelFormat pixelFormat; - int lineStride, pixelStride, width, height; + /** Returns the size of the bitmap. */ + Rectangle getBounds() const noexcept { return Rectangle (width, height); } + + uint8* data; /**< The raw pixel data, packed according to the image's pixel format. */ + PixelFormat pixelFormat; /**< The format of the data. */ + int lineStride; /**< The number of bytes between each line. */ + int pixelStride; /**< The number of bytes between each pixel. */ + int width, height; //============================================================================== /** Used internally by custom image types to manage pixel data lifetime. */ @@ -363,7 +364,7 @@ public: ScopedPointer dataReleaser; private: - JUCE_DECLARE_NON_COPYABLE (BitmapData); + JUCE_DECLARE_NON_COPYABLE (BitmapData) }; //============================================================================== @@ -379,8 +380,7 @@ public: @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; + void createSolidAreaMask (RectangleList& result, float alphaThreshold) const; //============================================================================== /** Returns a NamedValueSet that is attached to the image and which can be used for @@ -414,7 +414,7 @@ private: //============================================================================== ReferenceCountedObjectPtr image; - JUCE_LEAK_DETECTOR (Image); + JUCE_LEAK_DETECTOR (Image) }; @@ -453,8 +453,23 @@ public: */ NamedValueSet userData; + typedef ReferenceCountedObjectPtr Ptr; + + //============================================================================== + struct Listener + { + virtual ~Listener() {} + + virtual void imageDataChanged (ImagePixelData*) = 0; + virtual void imageDataBeingDeleted (ImagePixelData*) = 0; + }; + + ListenerList listeners; + + void sendDataChangeMessage(); + private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePixelData) }; //============================================================================== @@ -471,7 +486,7 @@ public: virtual ~ImageType(); /** Creates a new image of this type, and the specified parameters. */ - virtual ImagePixelData* create (Image::PixelFormat format, int width, int height, bool shouldClearImage) const = 0; + virtual ImagePixelData::Ptr create (Image::PixelFormat format, int width, int height, bool shouldClearImage) const = 0; /** Must return a unique number to identify this type. */ virtual int getTypeID() const = 0; @@ -494,8 +509,8 @@ public: SoftwareImageType(); ~SoftwareImageType(); - ImagePixelData* create (Image::PixelFormat, int width, int height, bool clearImage) const; - int getTypeID() const; + ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool clearImage) const override; + int getTypeID() const override; }; //============================================================================== @@ -510,9 +525,9 @@ public: NativeImageType(); ~NativeImageType(); - ImagePixelData* create (Image::PixelFormat, int width, int height, bool clearImage) const; - int getTypeID() const; + ImagePixelData::Ptr create (Image::PixelFormat, int width, int height, bool clearImage) const override; + int getTypeID() const override; }; -#endif // __JUCE_IMAGE_JUCEHEADER__ +#endif // JUCE_IMAGE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp index a39cfeb..992ff32 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.cpp @@ -1,34 +1,32 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -class ImageCache::Pimpl : public Timer, - public DeletedAtShutdown +class ImageCache::Pimpl : private Timer, + private DeletedAtShutdown { public: - Pimpl() - : cacheTimeout (5000) + Pimpl() : cacheTimeout (5000) { } @@ -43,7 +41,7 @@ public: for (int i = images.size(); --i >= 0;) { - Item* const item = images.getUnchecked(i); + const Item* const item = images.getUnchecked(i); if (item->hashCode == hashCode) return item->image; @@ -69,7 +67,7 @@ public: } } - void timerCallback() + void timerCallback() override { const uint32 now = Time::getApproximateMillisecondCounter(); @@ -94,6 +92,15 @@ public: stopTimer(); } + void releaseUnusedImages() + { + const ScopedLock sl (lock); + + for (int i = images.size(); --i >= 0;) + if (images.getUnchecked(i)->image.getReferenceCount() <= 1) + images.remove (i); + } + struct Item { Image image; @@ -101,7 +108,7 @@ public: uint32 lastUseTime; }; - int cacheTimeout; + unsigned int cacheTimeout; juce_DeclareSingleton_SingleThreaded_Minimal (ImageCache::Pimpl); @@ -109,7 +116,7 @@ private: OwnedArray images; CriticalSection lock; - JUCE_DECLARE_NON_COPYABLE (Pimpl); + JUCE_DECLARE_NON_COPYABLE (Pimpl) }; juce_ImplementSingleton_SingleThreaded (ImageCache::Pimpl); @@ -159,5 +166,11 @@ Image ImageCache::getFromMemory (const void* imageData, const int dataSize) void ImageCache::setCacheTimeout (const int millisecs) { - Pimpl::getInstance()->cacheTimeout = millisecs; + jassert (millisecs >= 0); + Pimpl::getInstance()->cacheTimeout = (unsigned int) millisecs; +} + +void ImageCache::releaseUnusedImages() +{ + Pimpl::getInstance()->releaseUnusedImages(); } diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.h b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.h index 8ce7270..c424395 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.h +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageCache.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IMAGECACHE_JUCEHEADER__ -#define __JUCE_IMAGECACHE_JUCEHEADER__ - -#include "juce_Image.h" +#ifndef JUCE_IMAGECACHE_H_INCLUDED +#define JUCE_IMAGECACHE_H_INCLUDED //============================================================================== @@ -109,6 +106,10 @@ public: */ static void setCacheTimeout (int millisecs); + /** Releases any images in the cache that aren't being referenced by active + Image objects. + */ + static void releaseUnusedImages(); private: //============================================================================== @@ -118,7 +119,7 @@ private: ImageCache(); ~ImageCache(); - JUCE_DECLARE_NON_COPYABLE (ImageCache); + JUCE_DECLARE_NON_COPYABLE (ImageCache) }; -#endif // __JUCE_IMAGECACHE_JUCEHEADER__ +#endif // JUCE_IMAGECACHE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp index 03cbaf3..78e4fb8 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -241,4 +240,51 @@ void ImageConvolutionKernel::applyToImage (Image& destImage, } } } + else if (destData.pixelStride == 1) + { + for (int y = area.getY(); y < bottom; ++y) + { + uint8* dest = line; + line += destData.lineStride; + + for (int x = area.getX(); x < right; ++x) + { + float c1 = 0; + + for (int yy = 0; yy < size; ++yy) + { + const int sy = y + yy - (size >> 1); + + if (sy >= srcData.height) + break; + + if (sy >= 0) + { + int sx = x - (size >> 1); + const uint8* src = srcData.getPixelPointer (sx, sy); + + for (int xx = 0; xx < size; ++xx) + { + if (sx >= srcData.width) + break; + + if (sx >= 0) + { + const float kernelMult = values [xx + yy * size]; + c1 += kernelMult * *src++; + } + else + { + src += 3; + } + + ++sx; + } + } + } + + *dest++ = (uint8) roundToInt (c1); + } + } + } } diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.h b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.h index 2293d3b..885dc6b 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.h +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageConvolutionKernel.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ -#define __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ - -#include "juce_Image.h" +#ifndef JUCE_IMAGECONVOLUTIONKERNEL_H_INCLUDED +#define JUCE_IMAGECONVOLUTIONKERNEL_H_INCLUDED //============================================================================== @@ -107,8 +104,8 @@ private: HeapBlock values; const int size; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageConvolutionKernel); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageConvolutionKernel) }; -#endif // __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ +#endif // JUCE_IMAGECONVOLUTIONKERNEL_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.cpp b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.cpp index 1f6ef55..a1cfd6a 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.cpp +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.cpp @@ -1,57 +1,76 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +struct DefaultImageFormats +{ + static ImageFileFormat** get() + { + static DefaultImageFormats formats; + return formats.formats; + } + +private: + DefaultImageFormats() noexcept + { + formats[0] = &png; + formats[1] = &jpg; + formats[2] = &gif; + formats[3] = nullptr; + } + + PNGImageFormat png; + JPEGImageFormat jpg; + GIFImageFormat gif; + + ImageFileFormat* formats[4]; +}; + ImageFileFormat* ImageFileFormat::findImageFormatForStream (InputStream& input) { - struct DefaultImageFormats - { - PNGImageFormat png; - JPEGImageFormat jpg; - GIFImageFormat gif; - }; - - static DefaultImageFormats defaultImageFormats; - - ImageFileFormat* formats[] = { &defaultImageFormats.png, - &defaultImageFormats.jpg, - &defaultImageFormats.gif }; - const int64 streamPos = input.getPosition(); - for (int i = 0; i < numElementsInArray (formats); ++i) + for (ImageFileFormat** i = DefaultImageFormats::get(); *i != nullptr; ++i) { - const bool found = formats[i]->canUnderstand (input); + const bool found = (*i)->canUnderstand (input); input.setPosition (streamPos); if (found) - return formats[i]; + return *i; } return nullptr; } +ImageFileFormat* ImageFileFormat::findImageFormatForFileExtension (const File& file) +{ + for (ImageFileFormat** i = DefaultImageFormats::get(); *i != nullptr; ++i) + if ((*i)->usesFileExtension (file)) + return *i; + + return nullptr; +} + //============================================================================== Image ImageFileFormat::loadFrom (InputStream& input) { diff --git a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.h b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.h index b7cadd1..76a13d2 100644 --- a/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.h +++ b/JuceLibraryCode/modules/juce_graphics/images/juce_ImageFileFormat.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ -#define __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ - -#include "juce_Image.h" +#ifndef JUCE_IMAGEFILEFORMAT_H_INCLUDED +#define JUCE_IMAGEFILEFORMAT_H_INCLUDED //============================================================================== @@ -57,16 +54,21 @@ public: */ virtual String getFormatName() = 0; - /** Returns true if the given stream seems to contain data that this format - understands. + /** 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. + Note that this will advance the stream and leave it in a new position, so if you're + planning on re-using it, you may want to rewind it after calling this method. + @see decodeImage */ virtual bool canUnderstand (InputStream& input) = 0; + /** Returns true if this format uses the file extension of the given file. */ + virtual bool usesFileExtension (const File& possibleFile) = 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 @@ -92,16 +94,19 @@ public: OutputStream& destStream) = 0; //============================================================================== - /** Tries the built-in decoders to see if it can find one to read this stream. - + /** Tries the built-in formats 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); + /** Looks for a format that can handle the given file extension. + There are currently built-in formats for PNG, JPEG and GIF formats. + The object that is returned should not be deleted by the caller. + */ + static ImageFileFormat* findImageFormatForFileExtension (const File& file); + //============================================================================== /** Tries to load an image from a stream. @@ -130,7 +135,6 @@ public: */ static Image loadFrom (const void* rawData, size_t numBytesOfData); - }; //============================================================================== @@ -147,10 +151,11 @@ public: ~PNGImageFormat(); //============================================================================== - String getFormatName(); - bool canUnderstand (InputStream& input); - Image decodeImage (InputStream& input); - bool writeImageToStream (const Image& sourceImage, OutputStream& destStream); + String getFormatName() override; + bool usesFileExtension (const File&) override; + bool canUnderstand (InputStream&) override; + Image decodeImage (InputStream&) override; + bool writeImageToStream (const Image&, OutputStream&) override; }; @@ -176,10 +181,11 @@ public: void setQuality (float newQuality); //============================================================================== - String getFormatName(); - bool canUnderstand (InputStream& input); - Image decodeImage (InputStream& input); - bool writeImageToStream (const Image& sourceImage, OutputStream& destStream); + String getFormatName() override; + bool usesFileExtension (const File&) override; + bool canUnderstand (InputStream&) override; + Image decodeImage (InputStream&) override; + bool writeImageToStream (const Image&, OutputStream&) override; private: float quality; @@ -199,11 +205,12 @@ public: ~GIFImageFormat(); //============================================================================== - String getFormatName(); - bool canUnderstand (InputStream& input); - Image decodeImage (InputStream& input); - bool writeImageToStream (const Image& sourceImage, OutputStream& destStream); + String getFormatName() override; + bool usesFileExtension (const File&) override; + bool canUnderstand (InputStream&) override; + Image decodeImage (InputStream&) override; + bool writeImageToStream (const Image&, OutputStream&) override; }; -#endif // __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ +#endif // JUCE_IMAGEFILEFORMAT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp b/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp index e5d7646..627d565 100644 --- a/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp +++ b/JuceLibraryCode/modules/juce_graphics/juce_graphics.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_GRAPHICS_MODULE_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_GRAPHICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -44,6 +43,11 @@ #import #elif JUCE_WINDOWS + #if JUCE_MINGW && JUCE_USE_DIRECTWRITE + #warning "DirectWrite not currently implemented with mingw..." + #undef JUCE_USE_DIRECTWRITE + #endif + #if JUCE_USE_DIRECTWRITE /* If you hit a compile error trying to include these files, you may need to update your version of the Windows SDK to the latest one. The DirectWrite and Direct2D @@ -53,22 +57,45 @@ #include #endif + #if JUCE_MINGW + #include + #endif + #elif JUCE_IOS #import #import + #if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2 + #error "JUCE no longer supports targets earlier than iOS 3.2" + #endif + #elif JUCE_LINUX - #include - #include FT_FREETYPE_H - #undef SIZEOF + #ifndef JUCE_USE_FREETYPE + #define JUCE_USE_FREETYPE 1 + #endif + + #if ! JUCE_USE_FREETYPE_AMALGAMATED + #include + #include FT_FREETYPE_H + #endif +#endif + +#if JUCE_USE_FREETYPE && JUCE_USE_FREETYPE_AMALGAMATED + #include "native/freetype/FreeTypeAmalgam.h" +#endif + +#undef SIZEOF + +#if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER + #define JUCE_USING_COREIMAGE_LOADER 1 +#else + #define JUCE_USING_COREIMAGE_LOADER 0 #endif //============================================================================== namespace juce { -// START_AUTOINCLUDE colour/*.cpp, geometry/*.cpp, placement/*.cpp, contexts/*.cpp, images/*.cpp, -// image_formats/*.cpp, fonts/*.cpp, effects/*.cpp #include "colour/juce_Colour.cpp" #include "colour/juce_ColourGradient.cpp" #include "colour/juce_Colours.cpp" @@ -78,8 +105,6 @@ namespace juce #include "geometry/juce_Path.cpp" #include "geometry/juce_PathIterator.cpp" #include "geometry/juce_PathStrokeType.cpp" -#include "geometry/juce_RectangleList.cpp" -#include "placement/juce_Justification.cpp" #include "placement/juce_RectanglePlacement.cpp" #include "contexts/juce_GraphicsContext.cpp" #include "contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp" @@ -92,31 +117,33 @@ namespace juce #include "image_formats/juce_JPEGLoader.cpp" #include "image_formats/juce_PNGLoader.cpp" #include "fonts/juce_AttributedString.cpp" +#include "fonts/juce_Typeface.cpp" #include "fonts/juce_CustomTypeface.cpp" #include "fonts/juce_Font.cpp" #include "fonts/juce_GlyphArrangement.cpp" #include "fonts/juce_TextLayout.cpp" -#include "fonts/juce_Typeface.cpp" #include "effects/juce_DropShadowEffect.cpp" #include "effects/juce_GlowEffect.cpp" -// END_AUTOINCLUDE + +#if JUCE_USE_FREETYPE + #include "native/juce_freetype_Fonts.cpp" +#endif //============================================================================== #if JUCE_MAC || JUCE_IOS #include "../juce_core/native/juce_osx_ObjCHelpers.h" - #include "../juce_core/native/juce_mac_ObjCSuffix.h" #include "native/juce_mac_CoreGraphicsHelpers.h" #include "native/juce_mac_Fonts.mm" #include "native/juce_mac_CoreGraphicsContext.mm" #elif JUCE_WINDOWS #include "../juce_core/native/juce_win32_ComSmartPtr.h" - #if JUCE_DIRECT2D - #include "native/juce_win32_Direct2DGraphicsContext.cpp" - #endif #include "native/juce_win32_DirectWriteTypeface.cpp" #include "native/juce_win32_DirectWriteTypeLayout.cpp" #include "native/juce_win32_Fonts.cpp" + #if JUCE_DIRECT2D + #include "native/juce_win32_Direct2DGraphicsContext.cpp" + #endif #elif JUCE_LINUX #include "native/juce_linux_Fonts.cpp" @@ -127,5 +154,18 @@ namespace juce #include "native/juce_android_Fonts.cpp" #endif - } + +//============================================================================== +#if JUCE_USE_FREETYPE && JUCE_USE_FREETYPE_AMALGAMATED + #undef PIXEL_MASK + #undef ZLIB_VERSION + #undef Z_ASCII + #undef ZEXTERN + #undef ZEXPORT + + extern "C" + { + #include "native/freetype/FreeTypeAmalgam.c" + } +#endif diff --git a/JuceLibraryCode/modules/juce_graphics/juce_graphics.h b/JuceLibraryCode/modules/juce_graphics/juce_graphics.h index cd08b4c..9a5a015 100644 --- a/JuceLibraryCode/modules/juce_graphics/juce_graphics.h +++ b/JuceLibraryCode/modules/juce_graphics/juce_graphics.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_GRAPHICS_MODULE_JUCEHEADER__ // %% -#define __JUCE_GRAPHICS_MODULE_JUCEHEADER__ +#ifndef JUCE_GRAPHICS_H_INCLUDED // %% +#define JUCE_GRAPHICS_H_INCLUDED #include "../juce_core/juce_core.h" #include "../juce_events/juce_events.h" @@ -65,112 +64,50 @@ namespace juce { -// START_AUTOINCLUDE colour, geometry, placement, contexts, images, -// image_formats, fonts, effects -#ifndef __JUCE_COLOUR_JUCEHEADER__ - #include "colour/juce_Colour.h" -#endif -#ifndef __JUCE_COLOURGRADIENT_JUCEHEADER__ - #include "colour/juce_ColourGradient.h" -#endif -#ifndef __JUCE_COLOURS_JUCEHEADER__ - #include "colour/juce_Colours.h" -#endif -#ifndef __JUCE_FILLTYPE_JUCEHEADER__ - #include "colour/juce_FillType.h" -#endif -#ifndef __JUCE_PIXELFORMATS_JUCEHEADER__ - #include "colour/juce_PixelFormats.h" -#endif -#ifndef __JUCE_AFFINETRANSFORM_JUCEHEADER__ - #include "geometry/juce_AffineTransform.h" -#endif -#ifndef __JUCE_BORDERSIZE_JUCEHEADER__ - #include "geometry/juce_BorderSize.h" -#endif -#ifndef __JUCE_EDGETABLE_JUCEHEADER__ - #include "geometry/juce_EdgeTable.h" -#endif -#ifndef __JUCE_LINE_JUCEHEADER__ - #include "geometry/juce_Line.h" -#endif -#ifndef __JUCE_PATH_JUCEHEADER__ - #include "geometry/juce_Path.h" -#endif -#ifndef __JUCE_PATHITERATOR_JUCEHEADER__ - #include "geometry/juce_PathIterator.h" -#endif -#ifndef __JUCE_PATHSTROKETYPE_JUCEHEADER__ - #include "geometry/juce_PathStrokeType.h" -#endif -#ifndef __JUCE_POINT_JUCEHEADER__ - #include "geometry/juce_Point.h" -#endif -#ifndef __JUCE_RECTANGLE_JUCEHEADER__ - #include "geometry/juce_Rectangle.h" -#endif -#ifndef __JUCE_RECTANGLELIST_JUCEHEADER__ - #include "geometry/juce_RectangleList.h" -#endif -#ifndef __JUCE_JUSTIFICATION_JUCEHEADER__ - #include "placement/juce_Justification.h" -#endif -#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ - #include "placement/juce_RectanglePlacement.h" -#endif -#ifndef __JUCE_GRAPHICSCONTEXT_JUCEHEADER__ - #include "contexts/juce_GraphicsContext.h" -#endif -#ifndef __JUCE_LOWLEVELGRAPHICSCONTEXT_JUCEHEADER__ - #include "contexts/juce_LowLevelGraphicsContext.h" -#endif -#ifndef __JUCE_LOWLEVELGRAPHICSPOSTSCRIPTRENDERER_JUCEHEADER__ - #include "contexts/juce_LowLevelGraphicsPostScriptRenderer.h" -#endif -#ifndef __JUCE_LOWLEVELGRAPHICSSOFTWARERENDERER_JUCEHEADER__ - #include "contexts/juce_LowLevelGraphicsSoftwareRenderer.h" -#endif -#ifndef __JUCE_IMAGE_JUCEHEADER__ - #include "images/juce_Image.h" -#endif -#ifndef __JUCE_IMAGECACHE_JUCEHEADER__ - #include "images/juce_ImageCache.h" -#endif -#ifndef __JUCE_IMAGECONVOLUTIONKERNEL_JUCEHEADER__ - #include "images/juce_ImageConvolutionKernel.h" -#endif -#ifndef __JUCE_IMAGEFILEFORMAT_JUCEHEADER__ - #include "images/juce_ImageFileFormat.h" -#endif -#ifndef __JUCE_ATTRIBUTEDSTRING_JUCEHEADER__ - #include "fonts/juce_AttributedString.h" -#endif -#ifndef __JUCE_CUSTOMTYPEFACE_JUCEHEADER__ - #include "fonts/juce_CustomTypeface.h" -#endif -#ifndef __JUCE_FONT_JUCEHEADER__ - #include "fonts/juce_Font.h" -#endif -#ifndef __JUCE_GLYPHARRANGEMENT_JUCEHEADER__ - #include "fonts/juce_GlyphArrangement.h" -#endif -#ifndef __JUCE_TEXTLAYOUT_JUCEHEADER__ - #include "fonts/juce_TextLayout.h" -#endif -#ifndef __JUCE_TYPEFACE_JUCEHEADER__ - #include "fonts/juce_Typeface.h" -#endif -#ifndef __JUCE_DROPSHADOWEFFECT_JUCEHEADER__ - #include "effects/juce_DropShadowEffect.h" -#endif -#ifndef __JUCE_GLOWEFFECT_JUCEHEADER__ - #include "effects/juce_GlowEffect.h" -#endif -#ifndef __JUCE_IMAGEEFFECTFILTER_JUCEHEADER__ - #include "effects/juce_ImageEffectFilter.h" -#endif -// END_AUTOINCLUDE +class Image; +class AffineTransform; +class Path; +class Font; +class Graphics; +class FillType; +class LowLevelGraphicsContext; + +#include "geometry/juce_AffineTransform.h" +#include "geometry/juce_Point.h" +#include "geometry/juce_Line.h" +#include "geometry/juce_Rectangle.h" +#include "placement/juce_Justification.h" +#include "geometry/juce_Path.h" +#include "geometry/juce_RectangleList.h" +#include "colour/juce_PixelFormats.h" +#include "colour/juce_Colour.h" +#include "colour/juce_ColourGradient.h" +#include "colour/juce_Colours.h" +#include "geometry/juce_BorderSize.h" +#include "geometry/juce_EdgeTable.h" +#include "geometry/juce_PathIterator.h" +#include "geometry/juce_PathStrokeType.h" +#include "placement/juce_RectanglePlacement.h" +#include "images/juce_ImageCache.h" +#include "images/juce_ImageConvolutionKernel.h" +#include "images/juce_ImageFileFormat.h" +#include "fonts/juce_AttributedString.h" +#include "fonts/juce_Typeface.h" +#include "fonts/juce_Font.h" +#include "fonts/juce_GlyphArrangement.h" +#include "fonts/juce_TextLayout.h" +#include "fonts/juce_CustomTypeface.h" +#include "contexts/juce_GraphicsContext.h" +#include "contexts/juce_LowLevelGraphicsContext.h" +#include "images/juce_Image.h" +#include "colour/juce_FillType.h" +#include "native/juce_RenderingHelpers.h" +#include "contexts/juce_LowLevelGraphicsSoftwareRenderer.h" +#include "contexts/juce_LowLevelGraphicsPostScriptRenderer.h" +#include "effects/juce_ImageEffectFilter.h" +#include "effects/juce_DropShadowEffect.h" +#include "effects/juce_GlowEffect.h" } -#endif // __JUCE_GRAPHICS_JUCEHEADER__ +#endif // JUCE_GRAPHICS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/juce_graphics.mm b/JuceLibraryCode/modules/juce_graphics/juce_graphics.mm index 22772e4..b36b0ed 100644 --- a/JuceLibraryCode/modules/juce_graphics/juce_graphics.mm +++ b/JuceLibraryCode/modules/juce_graphics/juce_graphics.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_graphics/juce_module_info b/JuceLibraryCode/modules/juce_graphics/juce_module_info index 05ba43a..29c06b2 100644 --- a/JuceLibraryCode/modules/juce_graphics/juce_module_info +++ b/JuceLibraryCode/modules/juce_graphics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_graphics", "name": "JUCE graphics classes", - "version": "2.0.21", + "version": "3.0.6", "description": "Classes for 2D vector graphics, image loading/saving, font handling, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", @@ -24,6 +24,7 @@ "effects/*", "native/*" ], - "OSXFrameworks": "Cocoa", - "iOSFrameworks": "CoreGraphics CoreText QuartzCore" + "OSXFrameworks": "Cocoa QuartzCore", + "iOSFrameworks": "CoreGraphics CoreText QuartzCore", + "LinuxLibs": "X11 Xinerama Xext freetype" } diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h b/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h index f58638b..533d002 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_RenderingHelpers.h @@ -1,30 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_RENDERINGHELPERS_JUCEHEADER__ -#define __JUCE_RENDERINGHELPERS_JUCEHEADER__ +#ifndef JUCE_RENDERINGHELPERS_H_INCLUDED +#define JUCE_RENDERINGHELPERS_H_INCLUDED + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4127) // "expression is constant" warning +#endif namespace RenderingHelpers { @@ -35,113 +39,111 @@ namespace RenderingHelpers class TranslationOrTransform { public: - TranslationOrTransform (int xOffset_, int yOffset_) noexcept - : xOffset (xOffset_), yOffset (yOffset_), isOnlyTranslated (true) + TranslationOrTransform (Point origin) noexcept + : offset (origin), isOnlyTranslated (true), isRotated (false) { } TranslationOrTransform (const TranslationOrTransform& other) noexcept - : complexTransform (other.complexTransform), - xOffset (other.xOffset), yOffset (other.yOffset), - isOnlyTranslated (other.isOnlyTranslated) + : complexTransform (other.complexTransform), offset (other.offset), + isOnlyTranslated (other.isOnlyTranslated), isRotated (other.isRotated) { } AffineTransform getTransform() const noexcept { - return isOnlyTranslated ? AffineTransform::translation ((float) xOffset, (float) yOffset) + return isOnlyTranslated ? AffineTransform::translation (offset) : complexTransform; } AffineTransform getTransformWith (const AffineTransform& userTransform) const noexcept { - return isOnlyTranslated ? userTransform.translated ((float) xOffset, (float) yOffset) + return isOnlyTranslated ? userTransform.translated (offset) : userTransform.followedBy (complexTransform); } - void setOrigin (const int x, const int y) noexcept + void setOrigin (Point delta) noexcept { if (isOnlyTranslated) - { - xOffset += x; - yOffset += y; - } + offset += delta; else - { - complexTransform = AffineTransform::translation ((float) x, (float) y) + complexTransform = AffineTransform::translation (delta) .followedBy (complexTransform); - } } void addTransform (const AffineTransform& t) noexcept { - if (isOnlyTranslated - && t.isOnlyTranslation() - && isIntegerTranslation (t)) + if (isOnlyTranslated && t.isOnlyTranslation()) { - xOffset += (int) t.getTranslationX(); - yOffset += (int) t.getTranslationY(); - } - else - { - complexTransform = getTransformWith (t); - isOnlyTranslated = false; + const int tx = (int) (t.getTranslationX() * 256.0f); + const int ty = (int) (t.getTranslationY() * 256.0f); + + if (((tx | ty) & 0xf8) == 0) + { + offset += Point (tx >> 8, ty >> 8); + return; + } } + + complexTransform = getTransformWith (t); + isOnlyTranslated = false; + isRotated = (complexTransform.mat01 != 0 || complexTransform.mat10 != 0 + || complexTransform.mat00 < 0 || complexTransform.mat11 < 0); } - float getScaleFactor() const noexcept + float getPhysicalPixelScaleFactor() const noexcept { - return isOnlyTranslated ? 1.0f : complexTransform.getScaleFactor(); + return isOnlyTranslated ? 1.0f : std::abs (complexTransform.getScaleFactor()); } - void moveOriginInDeviceSpace (const int dx, const int dy) noexcept + void moveOriginInDeviceSpace (Point delta) noexcept { if (isOnlyTranslated) - { - xOffset += dx; - yOffset += dy; - } + offset += delta; else - { - complexTransform = complexTransform.translated ((float) dx, (float) dy); - } + complexTransform = complexTransform.translated (delta); + } + + Rectangle translated (const Rectangle& r) const noexcept + { + jassert (isOnlyTranslated); + return r + offset; + } + + Rectangle translated (const Rectangle& r) const noexcept + { + jassert (isOnlyTranslated); + return r + offset.toFloat(); + } + + template + RectangleOrPoint transformed (const RectangleOrPoint& r) const noexcept + { + jassert (! isOnlyTranslated); + return r.transformedBy (complexTransform); } template - Rectangle translated (const Rectangle& r) const noexcept + Rectangle deviceSpaceToUserSpace (const Rectangle& r) const noexcept { - jassert (isOnlyTranslated); - return r.translated (static_cast (xOffset), - static_cast (yOffset)); - } - - Rectangle deviceSpaceToUserSpace (const Rectangle& r) const noexcept - { - return isOnlyTranslated ? r.translated (-xOffset, -yOffset) - : r.toFloat().transformed (complexTransform.inverted()).getSmallestIntegerContainer(); + return isOnlyTranslated ? r - offset + : r.transformedBy (complexTransform.inverted()); } AffineTransform complexTransform; - int xOffset, yOffset; - bool isOnlyTranslated; - -private: - static inline bool isIntegerTranslation (const AffineTransform& t) noexcept - { - const int tx = (int) (t.getTranslationX() * 256.0f); - const int ty = (int) (t.getTranslationY() * 256.0f); - return ((tx | ty) & 0xf8) == 0; - } + Point offset; + bool isOnlyTranslated, isRotated; }; //============================================================================== +/** Holds a cache of recently-used glyph objects of some type. */ template class GlyphCache : private DeletedAtShutdown { public: GlyphCache() { - addNewGlyphSlots (120); + reset(); } ~GlyphCache() @@ -160,74 +162,97 @@ public: } //============================================================================== - void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, float x, float y) + void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, Point pos) { - ++accessCounter; - CachedGlyphType* glyph = nullptr; + if (ReferenceCountedObjectPtr glyph = findOrCreateGlyph (font, glyphNumber)) + { + glyph->lastAccessCount = ++accessCounter; + glyph->draw (target, pos); + } + } - const ScopedReadLock srl (lock); + void reset() + { + const ScopedLock sl (lock); + glyphs.clear(); + addNewGlyphSlots (120); + hits.set (0); + misses.set (0); + } - for (int i = glyphs.size(); --i >= 0;) +private: + friend struct ContainerDeletePolicy; + ReferenceCountedArray glyphs; + Atomic accessCounter, hits, misses; + CriticalSection lock; + + ReferenceCountedObjectPtr findOrCreateGlyph (const Font& font, int glyphNumber) + { + const ScopedLock sl (lock); + + if (CachedGlyphType* g = findExistingGlyph (font, glyphNumber)) + { + ++hits; + return g; + } + + ++misses; + CachedGlyphType* g = getGlyphForReuse(); + jassert (g != nullptr); + g->generate (font, glyphNumber); + return g; + } + + CachedGlyphType* findExistingGlyph (const Font& font, int glyphNumber) const + { + for (int i = 0; i < glyphs.size(); ++i) { CachedGlyphType* const g = glyphs.getUnchecked (i); if (g->glyph == glyphNumber && g->font == font) - { - glyph = g; - ++hits; - break; - } + return g; } - if (glyph == nullptr) - { - ++misses; - const ScopedWriteLock swl (lock); - - if (hits.value + misses.value > glyphs.size() * 16) - { - if (misses.value * 2 > hits.value) - addNewGlyphSlots (32); - - hits.set (0); - misses.set (0); - glyph = glyphs.getLast(); - } - else - { - glyph = findLeastRecentlyUsedGlyph(); - } - - jassert (glyph != nullptr); - glyph->generate (font, glyphNumber); - } - - glyph->lastAccessCount = accessCounter.value; - glyph->draw (target, x, y); + return nullptr; } -private: - friend class OwnedArray ; - OwnedArray glyphs; - Atomic accessCounter, hits, misses; - ReadWriteLock lock; + CachedGlyphType* getGlyphForReuse() + { + if (hits.value + misses.value > glyphs.size() * 16) + { + if (misses.value * 2 > hits.value) + addNewGlyphSlots (32); + + hits.set (0); + misses.set (0); + } + + if (CachedGlyphType* g = findLeastRecentlyUsedGlyph()) + return g; + + addNewGlyphSlots (32); + return glyphs.getLast(); + } void addNewGlyphSlots (int num) { + glyphs.ensureStorageAllocated (glyphs.size() + num); + while (--num >= 0) glyphs.add (new CachedGlyphType()); } CachedGlyphType* findLeastRecentlyUsedGlyph() const noexcept { - CachedGlyphType* oldest = glyphs.getLast(); - int oldestCounter = oldest->lastAccessCount; + CachedGlyphType* oldest = nullptr; + int oldestCounter = std::numeric_limits::max(); for (int i = glyphs.size() - 1; --i >= 0;) { CachedGlyphType* const glyph = glyphs.getUnchecked(i); - if (glyph->lastAccessCount <= oldestCounter) + if (glyph->lastAccessCount <= oldestCounter + && glyph->getReferenceCount() == 1) { oldestCounter = glyph->lastAccessCount; oldest = glyph; @@ -243,23 +268,24 @@ private: return g; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphCache) }; //============================================================================== +/** Caches a glyph as an edge-table. */ template -class CachedGlyphEdgeTable +class CachedGlyphEdgeTable : public ReferenceCountedObject { public: CachedGlyphEdgeTable() : glyph (0), lastAccessCount (0) {} - void draw (RendererType& state, float x, const float y) const + void draw (RendererType& state, Point pos) const { if (snapToIntegerCoordinate) - x = std::floor (x + 0.5f); + pos.x = std::floor (pos.x + 0.5f); if (edgeTable != nullptr) - state.fillEdgeTable (*edgeTable, x, roundToInt (y)); + state.fillEdgeTable (*edgeTable, pos.x, roundToInt (pos.y)); } void generate (const Font& newFont, const int glyphNumber) @@ -271,11 +297,8 @@ public: const float fontHeight = font.getHeight(); edgeTable = typeface->getEdgeTableForGlyph (glyphNumber, - AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) - #if JUCE_MAC || JUCE_IOS - .translated (0.0f, -0.5f) - #endif - ); + AffineTransform::scale (fontHeight * font.getHorizontalScale(), + fontHeight), fontHeight); } Font font; @@ -283,66 +306,15 @@ public: bool snapToIntegerCoordinate; private: - ScopedPointer edgeTable; + ScopedPointer edgeTable; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CachedGlyphEdgeTable) }; //============================================================================== -template -class SavedStateStack -{ -public: - SavedStateStack (StateObjectType* const initialState) noexcept - : currentState (initialState) - {} - - inline StateObjectType* operator->() const noexcept { return currentState; } - inline StateObjectType& operator*() const noexcept { return *currentState; } - - void save() - { - stack.add (new StateObjectType (*currentState)); - } - - void restore() - { - StateObjectType* const top = stack.getLast(); - - if (top != nullptr) - { - currentState = top; - stack.removeLast (1, false); - } - else - { - jassertfalse; // trying to pop with an empty stack! - } - } - - void beginTransparencyLayer (float opacity) - { - save(); - currentState = currentState->beginTransparencyLayer (opacity); - } - - void endTransparencyLayer() - { - const ScopedPointer finishedTransparencyLayer (currentState); - restore(); - currentState->endTransparencyLayer (*finishedTransparencyLayer); - } - -private: - ScopedPointer currentState; - OwnedArray stack; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedStateStack); -}; - -//============================================================================== -// Calculates the alpha values and positions for rendering the edges of a non-pixel -// aligned rectangle. +/** Calculates the alpha values and positions for rendering the edges of a + non-pixel-aligned rectangle. +*/ struct FloatRectangleRasterisingInfo { FloatRectangleRasterisingInfo (const Rectangle& area) @@ -428,6 +400,2281 @@ struct FloatRectangleRasterisingInfo int topAlpha, leftAlpha, bottomAlpha, rightAlpha; // alpha of each anti-aliased edge }; +//============================================================================== +/** Contains classes for calculating the colour of pixels within various types of gradient. */ +namespace GradientPixelIterators +{ + /** Iterates the colour of pixels in a linear gradient */ + class Linear + { + public: + Linear (const ColourGradient& gradient, const AffineTransform& transform, + const PixelARGB* const colours, const int numColours) + : lookupTable (colours), + numEntries (numColours) + { + jassert (numColours >= 0); + Point p1 (gradient.point1); + Point p2 (gradient.point2); + + if (! transform.isIdentity()) + { + const Line l (p2, p1); + Point p3 = l.getPointAlongLine (0.0f, 100.0f); + + p1.applyTransform (transform); + p2.applyTransform (transform); + p3.applyTransform (transform); + + p2 = Line (p2, p3).findNearestPointTo (p1); + } + + vertical = std::abs (p1.x - p2.x) < 0.001f; + horizontal = std::abs (p1.y - p2.y) < 0.001f; + + if (vertical) + { + scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.y - p1.y)); + start = roundToInt (p1.y * scale); + } + else if (horizontal) + { + scale = roundToInt ((numEntries << (int) numScaleBits) / (double) (p2.x - p1.x)); + start = roundToInt (p1.x * scale); + } + else + { + grad = (p2.getY() - p1.y) / (double) (p1.x - p2.x); + yTerm = p1.getY() - p1.x / grad; + scale = roundToInt ((numEntries << (int) numScaleBits) / (yTerm * grad - (p2.y * grad - p2.x))); + grad *= scale; + } + } + + forcedinline void setY (const int y) noexcept + { + if (vertical) + linePix = lookupTable [jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)]; + else if (! horizontal) + start = roundToInt ((y - yTerm) * grad); + } + + inline PixelARGB getPixel (const int x) const noexcept + { + return vertical ? linePix + : lookupTable [jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)]; + } + + private: + const PixelARGB* const lookupTable; + const int numEntries; + PixelARGB linePix; + int start, scale; + double grad, yTerm; + bool vertical, horizontal; + enum { numScaleBits = 12 }; + + JUCE_DECLARE_NON_COPYABLE (Linear) + }; + + //============================================================================== + /** Iterates the colour of pixels in a circular radial gradient */ + class Radial + { + public: + Radial (const ColourGradient& gradient, const AffineTransform&, + const PixelARGB* const colours, const int numColours) + : lookupTable (colours), + numEntries (numColours), + gx1 (gradient.point1.x), + gy1 (gradient.point1.y) + { + jassert (numColours >= 0); + const Point diff (gradient.point1 - gradient.point2); + maxDist = diff.x * diff.x + diff.y * diff.y; + invScale = numEntries / std::sqrt (maxDist); + jassert (roundToInt (std::sqrt (maxDist) * invScale) <= numEntries); + } + + forcedinline void setY (const int y) noexcept + { + dy = y - gy1; + dy *= dy; + } + + inline PixelARGB getPixel (const int px) const noexcept + { + double x = px - gx1; + x *= x; + x += dy; + + return lookupTable [x >= maxDist ? numEntries : roundToInt (std::sqrt (x) * invScale)]; + } + + protected: + const PixelARGB* const lookupTable; + const int numEntries; + const double gx1, gy1; + double maxDist, invScale, dy; + + JUCE_DECLARE_NON_COPYABLE (Radial) + }; + + //============================================================================== + /** Iterates the colour of pixels in a skewed radial gradient */ + class TransformedRadial : public Radial + { + public: + TransformedRadial (const ColourGradient& gradient, const AffineTransform& transform, + const PixelARGB* const colours, const int numColours) + : Radial (gradient, transform, colours, numColours), + inverseTransform (transform.inverted()) + { + tM10 = inverseTransform.mat10; + tM00 = inverseTransform.mat00; + } + + forcedinline void setY (const int y) noexcept + { + lineYM01 = inverseTransform.mat01 * y + inverseTransform.mat02 - gx1; + lineYM11 = inverseTransform.mat11 * y + inverseTransform.mat12 - gy1; + } + + inline PixelARGB getPixel (const int px) const noexcept + { + double x = px; + const double y = tM10 * x + lineYM11; + x = tM00 * x + lineYM01; + x *= x; + x += y * y; + + if (x >= maxDist) + return lookupTable [numEntries]; + + return lookupTable [jmin (numEntries, roundToInt (std::sqrt (x) * invScale))]; + } + + private: + double tM10, tM00, lineYM01, lineYM11; + const AffineTransform inverseTransform; + + JUCE_DECLARE_NON_COPYABLE (TransformedRadial) + }; } -#endif // __JUCE_RENDERINGHELPERS_JUCEHEADER__ +#define JUCE_PERFORM_PIXEL_OP_LOOP(op) \ +{ \ + const int destStride = destData.pixelStride; \ + do { dest->op; dest = addBytesToPointer (dest, destStride); } while (--width > 0); \ +} + +//============================================================================== +/** Contains classes for filling edge tables with various fill types. */ +namespace EdgeTableFillers +{ + /** Fills an edge-table with a solid colour. */ + template + class SolidColour + { + public: + SolidColour (const Image::BitmapData& image, const PixelARGB colour) + : destData (image), sourceColour (colour) + { + if (sizeof (PixelType) == 3 && destData.pixelStride == sizeof (PixelType)) + { + areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen() + && sourceColour.getGreen() == sourceColour.getBlue(); + filler[0].set (sourceColour); + filler[1].set (sourceColour); + filler[2].set (sourceColour); + filler[3].set (sourceColour); + } + } + + forcedinline void setEdgeTableYPos (const int y) noexcept + { + linePixels = (PixelType*) destData.getLinePointer (y); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept + { + if (replaceExisting) + getPixel (x)->set (sourceColour); + else + getPixel (x)->blend (sourceColour, (uint32) alphaLevel); + } + + forcedinline void handleEdgeTablePixelFull (const int x) const noexcept + { + if (replaceExisting) + getPixel (x)->set (sourceColour); + else + getPixel (x)->blend (sourceColour); + } + + forcedinline void handleEdgeTableLine (const int x, const int width, const int alphaLevel) const noexcept + { + PixelARGB p (sourceColour); + p.multiplyAlpha (alphaLevel); + + PixelType* dest = getPixel (x); + + if (replaceExisting || p.getAlpha() >= 0xff) + replaceLine (dest, p, width); + else + blendLine (dest, p, width); + } + + forcedinline void handleEdgeTableLineFull (const int x, const int width) const noexcept + { + PixelType* dest = getPixel (x); + + if (replaceExisting || sourceColour.getAlpha() >= 0xff) + replaceLine (dest, sourceColour, width); + else + blendLine (dest, sourceColour, width); + } + + private: + const Image::BitmapData& destData; + PixelType* linePixels; + PixelARGB sourceColour; + PixelRGB filler [4]; + bool areRGBComponentsEqual; + + forcedinline PixelType* getPixel (const int x) const noexcept + { + return addBytesToPointer (linePixels, x * destData.pixelStride); + } + + inline void blendLine (PixelType* dest, const PixelARGB colour, int width) const noexcept + { + JUCE_PERFORM_PIXEL_OP_LOOP (blend (colour)) + } + + forcedinline void replaceLine (PixelRGB* dest, const PixelARGB colour, int width) const noexcept + { + if (destData.pixelStride == sizeof (*dest)) + { + if (areRGBComponentsEqual) // if all the component values are the same, we can cheat.. + { + memset (dest, colour.getRed(), (size_t) width * 3); + } + else + { + if (width >> 5) + { + const int* const intFiller = reinterpret_cast (filler); + + while (width > 8 && (((pointer_sized_int) dest) & 7) != 0) + { + dest->set (colour); + ++dest; + --width; + } + + while (width > 4) + { + int* d = reinterpret_cast (dest); + *d++ = intFiller[0]; + *d++ = intFiller[1]; + *d++ = intFiller[2]; + dest = reinterpret_cast (d); + width -= 4; + } + } + + while (--width >= 0) + { + dest->set (colour); + ++dest; + } + } + } + else + { + JUCE_PERFORM_PIXEL_OP_LOOP (set (colour)) + } + } + + forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB colour, int width) const noexcept + { + if (destData.pixelStride == sizeof (*dest)) + memset (dest, colour.getAlpha(), (size_t) width); + else + JUCE_PERFORM_PIXEL_OP_LOOP (setAlpha (colour.getAlpha())) + } + + forcedinline void replaceLine (PixelARGB* dest, const PixelARGB colour, int width) const noexcept + { + JUCE_PERFORM_PIXEL_OP_LOOP (set (colour)) + } + + JUCE_DECLARE_NON_COPYABLE (SolidColour) + }; + + //============================================================================== + /** Fills an edge-table with a gradient. */ + template + class Gradient : public GradientType + { + public: + Gradient (const Image::BitmapData& dest, const ColourGradient& gradient, const AffineTransform& transform, + const PixelARGB* const colours, const int numColours) + : GradientType (gradient, transform, colours, numColours - 1), + destData (dest) + { + } + + forcedinline void setEdgeTableYPos (const int y) noexcept + { + linePixels = (PixelType*) destData.getLinePointer (y); + GradientType::setY (y); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) const noexcept + { + getPixel (x)->blend (GradientType::getPixel (x), (uint32) alphaLevel); + } + + forcedinline void handleEdgeTablePixelFull (const int x) const noexcept + { + getPixel (x)->blend (GradientType::getPixel (x)); + } + + void handleEdgeTableLine (int x, int width, const int alphaLevel) const noexcept + { + PixelType* dest = getPixel (x); + + if (alphaLevel < 0xff) + JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++), (uint32) alphaLevel)) + else + JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++))) + } + + void handleEdgeTableLineFull (int x, int width) const noexcept + { + PixelType* dest = getPixel (x); + JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++))) + } + + private: + const Image::BitmapData& destData; + PixelType* linePixels; + + forcedinline PixelType* getPixel (const int x) const noexcept + { + return addBytesToPointer (linePixels, x * destData.pixelStride); + } + + JUCE_DECLARE_NON_COPYABLE (Gradient) + }; + + //============================================================================== + /** Fills an edge-table with a non-transformed image. */ + template + class ImageFill + { + public: + ImageFill (const Image::BitmapData& dest, const Image::BitmapData& src, + const int alpha, const int x, const int y) + : destData (dest), + srcData (src), + extraAlpha (alpha + 1), + xOffset (repeatPattern ? negativeAwareModulo (x, src.width) - src.width : x), + yOffset (repeatPattern ? negativeAwareModulo (y, src.height) - src.height : y) + { + } + + forcedinline void setEdgeTableYPos (int y) noexcept + { + linePixels = (DestPixelType*) destData.getLinePointer (y); + + y -= yOffset; + if (repeatPattern) + { + jassert (y >= 0); + y %= srcData.height; + } + + sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y); + } + + forcedinline void handleEdgeTablePixel (const int x, int alphaLevel) const noexcept + { + alphaLevel = (alphaLevel * extraAlpha) >> 8; + + getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) alphaLevel); + } + + forcedinline void handleEdgeTablePixelFull (const int x) const noexcept + { + getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) extraAlpha); + } + + void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept + { + DestPixelType* dest = getDestPixel (x); + alphaLevel = (alphaLevel * extraAlpha) >> 8; + x -= xOffset; + + if (repeatPattern) + { + if (alphaLevel < 0xfe) + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) alphaLevel)) + else + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width))) + } + else + { + jassert (x >= 0 && x + width <= srcData.width); + + if (alphaLevel < 0xfe) + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) alphaLevel)) + else + copyRow (dest, getSrcPixel (x), width); + } + } + + void handleEdgeTableLineFull (int x, int width) const noexcept + { + DestPixelType* dest = getDestPixel (x); + x -= xOffset; + + if (repeatPattern) + { + if (extraAlpha < 0xfe) + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) extraAlpha)) + else + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width))) + } + else + { + jassert (x >= 0 && x + width <= srcData.width); + + if (extraAlpha < 0xfe) + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) extraAlpha)) + else + copyRow (dest, getSrcPixel (x), width); + } + } + + void clipEdgeTableLine (EdgeTable& et, int x, int y, int width) + { + jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width); + SrcPixelType* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset); + uint8* mask = (uint8*) (s + x - xOffset); + + if (sizeof (SrcPixelType) == sizeof (PixelARGB)) + mask += PixelARGB::indexA; + + et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width); + } + + private: + const Image::BitmapData& destData; + const Image::BitmapData& srcData; + const int extraAlpha, xOffset, yOffset; + DestPixelType* linePixels; + SrcPixelType* sourceLineStart; + + forcedinline DestPixelType* getDestPixel (int const x) const noexcept + { + return addBytesToPointer (linePixels, x * destData.pixelStride); + } + + forcedinline SrcPixelType const* getSrcPixel (int const x) const noexcept + { + return addBytesToPointer (sourceLineStart, x * srcData.pixelStride); + } + + forcedinline void copyRow (DestPixelType* dest, SrcPixelType const* src, int width) const noexcept + { + const int destStride = destData.pixelStride; + const int srcStride = srcData.pixelStride; + + if (destStride == srcStride + && srcData.pixelFormat == Image::RGB + && destData.pixelFormat == Image::RGB) + { + memcpy (dest, src, (size_t) (width * srcStride)); + } + else + { + do + { + dest->blend (*src); + dest = addBytesToPointer (dest, destStride); + src = addBytesToPointer (src, srcStride); + } while (--width > 0); + } + } + + JUCE_DECLARE_NON_COPYABLE (ImageFill) + }; + + //============================================================================== + /** Fills an edge-table with a transformed image. */ + template + class TransformedImageFill + { + public: + TransformedImageFill (const Image::BitmapData& dest, const Image::BitmapData& src, + const AffineTransform& transform, const int alpha, const Graphics::ResamplingQuality q) + : interpolator (transform, + q != Graphics::lowResamplingQuality ? 0.5f : 0.0f, + q != Graphics::lowResamplingQuality ? -128 : 0), + destData (dest), + srcData (src), + extraAlpha (alpha + 1), + quality (q), + maxX (src.width - 1), + maxY (src.height - 1), + scratchSize (2048) + { + scratchBuffer.malloc (scratchSize); + } + + forcedinline void setEdgeTableYPos (const int newY) noexcept + { + y = newY; + linePixels = (DestPixelType*) destData.getLinePointer (newY); + } + + forcedinline void handleEdgeTablePixel (const int x, const int alphaLevel) noexcept + { + SrcPixelType p; + generate (&p, x, 1); + + getDestPixel (x)->blend (p, (uint32) (alphaLevel * extraAlpha) >> 8); + } + + forcedinline void handleEdgeTablePixelFull (const int x) noexcept + { + SrcPixelType p; + generate (&p, x, 1); + + getDestPixel (x)->blend (p, (uint32) extraAlpha); + } + + void handleEdgeTableLine (const int x, int width, int alphaLevel) noexcept + { + if (width > (int) scratchSize) + { + scratchSize = (size_t) width; + scratchBuffer.malloc (scratchSize); + } + + SrcPixelType* span = scratchBuffer; + generate (span, x, width); + + DestPixelType* dest = getDestPixel (x); + alphaLevel *= extraAlpha; + alphaLevel >>= 8; + + if (alphaLevel < 0xfe) + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*span++, (uint32) alphaLevel)) + else + JUCE_PERFORM_PIXEL_OP_LOOP (blend (*span++)) + } + + forcedinline void handleEdgeTableLineFull (const int x, int width) noexcept + { + handleEdgeTableLine (x, width, 255); + } + + void clipEdgeTableLine (EdgeTable& et, int x, int y_, int width) + { + if (width > (int) scratchSize) + { + scratchSize = (size_t) width; + scratchBuffer.malloc (scratchSize); + } + + y = y_; + generate (scratchBuffer.getData(), x, width); + + et.clipLineToMask (x, y_, + reinterpret_cast (scratchBuffer.getData()) + SrcPixelType::indexA, + sizeof (SrcPixelType), width); + } + + private: + forcedinline DestPixelType* getDestPixel (const int x) const noexcept + { + return addBytesToPointer (linePixels, x * destData.pixelStride); + } + + //============================================================================== + template + void generate (PixelType* dest, const int x, int numPixels) noexcept + { + this->interpolator.setStartOfLine ((float) x, (float) y, numPixels); + + do + { + int hiResX, hiResY; + this->interpolator.next (hiResX, hiResY); + + int loResX = hiResX >> 8; + int loResY = hiResY >> 8; + + if (repeatPattern) + { + loResX = negativeAwareModulo (loResX, srcData.width); + loResY = negativeAwareModulo (loResY, srcData.height); + } + + if (quality != Graphics::lowResamplingQuality) + { + if (isPositiveAndBelow (loResX, maxX)) + { + if (isPositiveAndBelow (loResY, maxY)) + { + // In the centre of the image.. + render4PixelAverage (dest, this->srcData.getPixelPointer (loResX, loResY), + hiResX & 255, hiResY & 255); + ++dest; + continue; + } + + if (! repeatPattern) + { + // At a top or bottom edge.. + if (loResY < 0) + render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, 0), hiResX & 255); + else + render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, maxY), hiResX & 255); + + ++dest; + continue; + } + } + else + { + if (isPositiveAndBelow (loResY, maxY) && ! repeatPattern) + { + // At a left or right hand edge.. + if (loResX < 0) + render2PixelAverageY (dest, this->srcData.getPixelPointer (0, loResY), hiResY & 255); + else + render2PixelAverageY (dest, this->srcData.getPixelPointer (maxX, loResY), hiResY & 255); + + ++dest; + continue; + } + } + } + + if (! repeatPattern) + { + if (loResX < 0) loResX = 0; + if (loResY < 0) loResY = 0; + if (loResX > maxX) loResX = maxX; + if (loResY > maxY) loResY = maxY; + } + + dest->set (*(const PixelType*) this->srcData.getPixelPointer (loResX, loResY)); + ++dest; + + } while (--numPixels > 0); + } + + //============================================================================== + void render4PixelAverage (PixelARGB* const dest, const uint8* src, const int subPixelX, const int subPixelY) noexcept + { + uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 }; + + uint32 weight = (uint32) ((256 - subPixelX) * (256 - subPixelY)); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + src += this->srcData.pixelStride; + + weight = (uint32) (subPixelX * (256 - subPixelY)); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + src += this->srcData.lineStride; + + weight = (uint32) (subPixelX * subPixelY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + src -= this->srcData.pixelStride; + + weight = (uint32) ((256 - subPixelX) * subPixelY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16), + (uint8) (c[PixelARGB::indexR] >> 16), + (uint8) (c[PixelARGB::indexG] >> 16), + (uint8) (c[PixelARGB::indexB] >> 16)); + } + + void render2PixelAverageX (PixelARGB* const dest, const uint8* src, const uint32 subPixelX) noexcept + { + uint32 c[4] = { 128, 128, 128, 128 }; + + uint32 weight = 256 - subPixelX; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + src += this->srcData.pixelStride; + + weight = subPixelX; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8), + (uint8) (c[PixelARGB::indexR] >> 8), + (uint8) (c[PixelARGB::indexG] >> 8), + (uint8) (c[PixelARGB::indexB] >> 8)); + } + + void render2PixelAverageY (PixelARGB* const dest, const uint8* src, const uint32 subPixelY) noexcept + { + uint32 c[4] = { 128, 128, 128, 128 }; + + uint32 weight = 256 - subPixelY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + src += this->srcData.lineStride; + + weight = subPixelY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + c[3] += weight * src[3]; + + dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8), + (uint8) (c[PixelARGB::indexR] >> 8), + (uint8) (c[PixelARGB::indexG] >> 8), + (uint8) (c[PixelARGB::indexB] >> 8)); + } + + //============================================================================== + void render4PixelAverage (PixelRGB* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept + { + uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 }; + + uint32 weight = (256 - subPixelX) * (256 - subPixelY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + src += this->srcData.pixelStride; + + weight = subPixelX * (256 - subPixelY); + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + src += this->srcData.lineStride; + + weight = subPixelX * subPixelY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + src -= this->srcData.pixelStride; + + weight = (256 - subPixelX) * subPixelY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + dest->setARGB ((uint8) 255, + (uint8) (c[PixelRGB::indexR] >> 16), + (uint8) (c[PixelRGB::indexG] >> 16), + (uint8) (c[PixelRGB::indexB] >> 16)); + } + + void render2PixelAverageX (PixelRGB* const dest, const uint8* src, const uint32 subPixelX) noexcept + { + uint32 c[3] = { 128, 128, 128 }; + + const uint32 weight = 256 - subPixelX; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + src += this->srcData.pixelStride; + + c[0] += subPixelX * src[0]; + c[1] += subPixelX * src[1]; + c[2] += subPixelX * src[2]; + + dest->setARGB ((uint8) 255, + (uint8) (c[PixelRGB::indexR] >> 8), + (uint8) (c[PixelRGB::indexG] >> 8), + (uint8) (c[PixelRGB::indexB] >> 8)); + } + + void render2PixelAverageY (PixelRGB* const dest, const uint8* src, const uint32 subPixelY) noexcept + { + uint32 c[3] = { 128, 128, 128 }; + + const uint32 weight = 256 - subPixelY; + c[0] += weight * src[0]; + c[1] += weight * src[1]; + c[2] += weight * src[2]; + + src += this->srcData.lineStride; + + c[0] += subPixelY * src[0]; + c[1] += subPixelY * src[1]; + c[2] += subPixelY * src[2]; + + dest->setARGB ((uint8) 255, + (uint8) (c[PixelRGB::indexR] >> 8), + (uint8) (c[PixelRGB::indexG] >> 8), + (uint8) (c[PixelRGB::indexB] >> 8)); + } + + //============================================================================== + void render4PixelAverage (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX, const uint32 subPixelY) noexcept + { + uint32 c = 256 * 128; + c += src[0] * ((256 - subPixelX) * (256 - subPixelY)); + src += this->srcData.pixelStride; + c += src[1] * (subPixelX * (256 - subPixelY)); + src += this->srcData.lineStride; + c += src[1] * (subPixelX * subPixelY); + src -= this->srcData.pixelStride; + + c += src[0] * ((256 - subPixelX) * subPixelY); + + *((uint8*) dest) = (uint8) (c >> 16); + } + + void render2PixelAverageX (PixelAlpha* const dest, const uint8* src, const uint32 subPixelX) noexcept + { + uint32 c = 128; + c += src[0] * (256 - subPixelX); + src += this->srcData.pixelStride; + c += src[0] * subPixelX; + *((uint8*) dest) = (uint8) (c >> 8); + } + + void render2PixelAverageY (PixelAlpha* const dest, const uint8* src, const uint32 subPixelY) noexcept + { + uint32 c = 128; + c += src[0] * (256 - subPixelY); + src += this->srcData.lineStride; + c += src[0] * subPixelY; + *((uint8*) dest) = (uint8) (c >> 8); + } + + //============================================================================== + class TransformedImageSpanInterpolator + { + public: + TransformedImageSpanInterpolator (const AffineTransform& transform, + const float offsetFloat, const int offsetInt) noexcept + : inverseTransform (transform.inverted()), + pixelOffset (offsetFloat), pixelOffsetInt (offsetInt) + {} + + void setStartOfLine (float sx, float sy, const int numPixels) noexcept + { + jassert (numPixels > 0); + + sx += pixelOffset; + sy += pixelOffset; + float x1 = sx, y1 = sy; + sx += numPixels; + inverseTransform.transformPoints (x1, y1, sx, sy); + + xBresenham.set ((int) (x1 * 256.0f), (int) (sx * 256.0f), numPixels, pixelOffsetInt); + yBresenham.set ((int) (y1 * 256.0f), (int) (sy * 256.0f), numPixels, pixelOffsetInt); + } + + void next (int& px, int& py) noexcept + { + px = xBresenham.n; xBresenham.stepToNext(); + py = yBresenham.n; yBresenham.stepToNext(); + } + + private: + class BresenhamInterpolator + { + public: + BresenhamInterpolator() noexcept {} + + void set (const int n1, const int n2, const int steps, const int offsetInt) noexcept + { + numSteps = steps; + step = (n2 - n1) / numSteps; + remainder = modulo = (n2 - n1) % numSteps; + n = n1 + offsetInt; + + if (modulo <= 0) + { + modulo += numSteps; + remainder += numSteps; + --step; + } + + modulo -= numSteps; + } + + forcedinline void stepToNext() noexcept + { + modulo += remainder; + n += step; + + if (modulo > 0) + { + modulo -= numSteps; + ++n; + } + } + + int n; + + private: + int numSteps, step, modulo, remainder; + }; + + const AffineTransform inverseTransform; + BresenhamInterpolator xBresenham, yBresenham; + const float pixelOffset; + const int pixelOffsetInt; + + JUCE_DECLARE_NON_COPYABLE (TransformedImageSpanInterpolator) + }; + + //============================================================================== + TransformedImageSpanInterpolator interpolator; + const Image::BitmapData& destData; + const Image::BitmapData& srcData; + const int extraAlpha; + const Graphics::ResamplingQuality quality; + const int maxX, maxY; + int y; + DestPixelType* linePixels; + HeapBlock scratchBuffer; + size_t scratchSize; + + JUCE_DECLARE_NON_COPYABLE (TransformedImageFill) + }; + + + //============================================================================== + template + void renderImageTransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, + const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) + { + switch (destData.pixelFormat) + { + case Image::ARGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + } + break; + + case Image::RGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + } + break; + + default: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + default: + if (tiledFill) { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + else { TransformedImageFill r (destData, srcData, transform, alpha, quality); iter.iterate (r); } + break; + } + break; + } + } + + template + void renderImageUntransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, const int alpha, int x, int y, bool tiledFill) + { + switch (destData.pixelFormat) + { + case Image::ARGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + + case Image::RGB: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + + default: + switch (srcData.pixelFormat) + { + case Image::ARGB: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + case Image::RGB: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + default: + if (tiledFill) { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + else { ImageFill r (destData, srcData, alpha, x, y); iter.iterate (r); } + break; + } + break; + } + } + + template + void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, const PixelARGB fillColour, const bool replaceContents, DestPixelType*) + { + if (replaceContents) + { + EdgeTableFillers::SolidColour r (destData, fillColour); + iter.iterate (r); + } + else + { + EdgeTableFillers::SolidColour r (destData, fillColour); + iter.iterate (r); + } + } + + template + void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform, + const PixelARGB* const lookupTable, const int numLookupEntries, const bool isIdentity, DestPixelType*) + { + if (g.isRadial) + { + if (isIdentity) + { + EdgeTableFillers::Gradient renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + else + { + EdgeTableFillers::Gradient renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + } + else + { + EdgeTableFillers::Gradient renderer (destData, g, transform, lookupTable, numLookupEntries); + iter.iterate (renderer); + } + } +} + +//============================================================================== +template +struct ClipRegions +{ + class Base : public SingleThreadedReferenceCountedObject + { + public: + Base() {} + virtual ~Base() {} + + typedef ReferenceCountedObjectPtr Ptr; + + virtual Ptr clone() const = 0; + virtual Ptr applyClipTo (const Ptr& target) const = 0; + + virtual Ptr clipToRectangle (const Rectangle&) = 0; + virtual Ptr clipToRectangleList (const RectangleList&) = 0; + virtual Ptr excludeClipRectangle (const Rectangle&) = 0; + virtual Ptr clipToPath (const Path&, const AffineTransform&) = 0; + virtual Ptr clipToEdgeTable (const EdgeTable& et) = 0; + virtual Ptr clipToImageAlpha (const Image&, const AffineTransform&, const Graphics::ResamplingQuality) = 0; + virtual void translate (Point delta) = 0; + + virtual bool clipRegionIntersects (const Rectangle&) const = 0; + virtual Rectangle getClipBounds() const = 0; + + virtual void fillRectWithColour (SavedStateType&, const Rectangle&, const PixelARGB colour, bool replaceContents) const = 0; + virtual void fillRectWithColour (SavedStateType&, const Rectangle&, const PixelARGB colour) const = 0; + virtual void fillAllWithColour (SavedStateType&, const PixelARGB colour, bool replaceContents) const = 0; + virtual void fillAllWithGradient (SavedStateType&, ColourGradient&, const AffineTransform&, bool isIdentity) const = 0; + virtual void renderImageTransformed (SavedStateType&, const Image&, const int alpha, const AffineTransform&, Graphics::ResamplingQuality, bool tiledFill) const = 0; + virtual void renderImageUntransformed (SavedStateType&, const Image&, const int alpha, int x, int y, bool tiledFill) const = 0; + }; + + //============================================================================== + class EdgeTableRegion : public Base + { + public: + EdgeTableRegion (const EdgeTable& e) : edgeTable (e) {} + EdgeTableRegion (const Rectangle& r) : edgeTable (r) {} + EdgeTableRegion (const Rectangle& r) : edgeTable (r) {} + EdgeTableRegion (const RectangleList& r) : edgeTable (r) {} + EdgeTableRegion (const RectangleList& r) : edgeTable (r) {} + EdgeTableRegion (const Rectangle& bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {} + EdgeTableRegion (const EdgeTableRegion& other) : Base(), edgeTable (other.edgeTable) {} + + typedef typename Base::Ptr Ptr; + + Ptr clone() const { return new EdgeTableRegion (*this); } + Ptr applyClipTo (const Ptr& target) const { return target->clipToEdgeTable (edgeTable); } + + Ptr clipToRectangle (const Rectangle& r) + { + edgeTable.clipToRectangle (r); + return edgeTable.isEmpty() ? nullptr : this; + } + + Ptr clipToRectangleList (const RectangleList& r) + { + RectangleList inverse (edgeTable.getMaximumBounds()); + + if (inverse.subtract (r)) + for (const Rectangle* i = inverse.begin(), * const e = inverse.end(); i != e; ++i) + edgeTable.excludeRectangle (*i); + + return edgeTable.isEmpty() ? nullptr : this; + } + + Ptr excludeClipRectangle (const Rectangle& r) + { + edgeTable.excludeRectangle (r); + return edgeTable.isEmpty() ? nullptr : this; + } + + Ptr clipToPath (const Path& p, const AffineTransform& transform) + { + EdgeTable et (edgeTable.getMaximumBounds(), p, transform); + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? nullptr : this; + } + + Ptr clipToEdgeTable (const EdgeTable& et) + { + edgeTable.clipToEdgeTable (et); + return edgeTable.isEmpty() ? nullptr : this; + } + + Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) + { + const Image::BitmapData srcData (image, Image::BitmapData::readOnly); + + if (transform.isOnlyTranslation()) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + const int tx = (int) (transform.getTranslationX() * 256.0f); + const int ty = (int) (transform.getTranslationY() * 256.0f); + + if (quality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0) + { + const int imageX = ((tx + 128) >> 8); + const int imageY = ((ty + 128) >> 8); + + if (image.getFormat() == Image::ARGB) + straightClipImage (srcData, imageX, imageY, (PixelARGB*) 0); + else + straightClipImage (srcData, imageX, imageY, (PixelAlpha*) 0); + + return edgeTable.isEmpty() ? nullptr : this; + } + } + + if (transform.isSingularity()) + return Ptr(); + + { + Path p; + p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height); + EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform); + edgeTable.clipToEdgeTable (et2); + } + + if (! edgeTable.isEmpty()) + { + if (image.getFormat() == Image::ARGB) + transformedClipImage (srcData, transform, quality, (PixelARGB*) 0); + else + transformedClipImage (srcData, transform, quality, (PixelAlpha*) 0); + } + + return edgeTable.isEmpty() ? nullptr : this; + } + + void translate (Point delta) + { + edgeTable.translate ((float) delta.x, delta.y); + } + + bool clipRegionIntersects (const Rectangle& r) const + { + return edgeTable.getMaximumBounds().intersects (r); + } + + Rectangle getClipBounds() const + { + return edgeTable.getMaximumBounds(); + } + + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour, bool replaceContents) const + { + const Rectangle totalClip (edgeTable.getMaximumBounds()); + const Rectangle clipped (totalClip.getIntersection (area)); + + if (! clipped.isEmpty()) + { + EdgeTableRegion et (clipped); + et.edgeTable.clipToEdgeTable (edgeTable); + state.fillWithSolidColour (et.edgeTable, colour, replaceContents); + } + } + + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour) const + { + const Rectangle totalClip (edgeTable.getMaximumBounds().toFloat()); + const Rectangle clipped (totalClip.getIntersection (area)); + + if (! clipped.isEmpty()) + { + EdgeTableRegion et (clipped); + et.edgeTable.clipToEdgeTable (edgeTable); + state.fillWithSolidColour (et.edgeTable, colour, false); + } + } + + void fillAllWithColour (SavedStateType& state, const PixelARGB colour, bool replaceContents) const + { + state.fillWithSolidColour (edgeTable, colour, replaceContents); + } + + void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + { + state.fillWithGradient (edgeTable, gradient, transform, isIdentity); + } + + void renderImageTransformed (SavedStateType& state, const Image& src, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const + { + state.renderImageTransformed (edgeTable, src, alpha, transform, quality, tiledFill); + } + + void renderImageUntransformed (SavedStateType& state, const Image& src, const int alpha, int x, int y, bool tiledFill) const + { + state.renderImageUntransformed (edgeTable, src, alpha, x, y, tiledFill); + } + + EdgeTable edgeTable; + + private: + template + void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, const Graphics::ResamplingQuality quality, const SrcPixelType*) + { + EdgeTableFillers::TransformedImageFill renderer (srcData, srcData, transform, 255, quality); + + for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y) + renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(), + edgeTable.getMaximumBounds().getWidth()); + } + + template + void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*) + { + Rectangle r (imageX, imageY, srcData.width, srcData.height); + edgeTable.clipToRectangle (r); + + EdgeTableFillers::ImageFill renderer (srcData, srcData, 255, imageX, imageY); + + for (int y = 0; y < r.getHeight(); ++y) + renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth()); + } + + EdgeTableRegion& operator= (const EdgeTableRegion&); + }; + + //============================================================================== + class RectangleListRegion : public Base + { + public: + RectangleListRegion (const Rectangle& r) : clip (r) {} + RectangleListRegion (const RectangleList& r) : clip (r) {} + RectangleListRegion (const RectangleListRegion& other) : Base(), clip (other.clip) {} + + typedef typename Base::Ptr Ptr; + + Ptr clone() const { return new RectangleListRegion (*this); } + Ptr applyClipTo (const Ptr& target) const { return target->clipToRectangleList (clip); } + + Ptr clipToRectangle (const Rectangle& r) + { + clip.clipTo (r); + return clip.isEmpty() ? nullptr : this; + } + + Ptr clipToRectangleList (const RectangleList& r) + { + clip.clipTo (r); + return clip.isEmpty() ? nullptr : this; + } + + Ptr excludeClipRectangle (const Rectangle& r) + { + clip.subtract (r); + return clip.isEmpty() ? nullptr : this; + } + + Ptr clipToPath (const Path& p, const AffineTransform& transform) { return toEdgeTable()->clipToPath (p, transform); } + Ptr clipToEdgeTable (const EdgeTable& et) { return toEdgeTable()->clipToEdgeTable (et); } + + Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, const Graphics::ResamplingQuality quality) + { + return toEdgeTable()->clipToImageAlpha (image, transform, quality); + } + + void translate (Point delta) { clip.offsetAll (delta); } + bool clipRegionIntersects (const Rectangle& r) const { return clip.intersects (r); } + Rectangle getClipBounds() const { return clip.getBounds(); } + + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour, bool replaceContents) const + { + SubRectangleIterator iter (clip, area); + state.fillWithSolidColour (iter, colour, replaceContents); + } + + void fillRectWithColour (SavedStateType& state, const Rectangle& area, const PixelARGB colour) const + { + SubRectangleIteratorFloat iter (clip, area); + state.fillWithSolidColour (iter, colour, false); + } + + void fillAllWithColour (SavedStateType& state, const PixelARGB colour, bool replaceContents) const + { + state.fillWithSolidColour (*this, colour, replaceContents); + } + + void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const + { + state.fillWithGradient (*this, gradient, transform, isIdentity); + } + + void renderImageTransformed (SavedStateType& state, const Image& src, const int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const + { + state.renderImageTransformed (*this, src, alpha, transform, quality, tiledFill); + } + + void renderImageUntransformed (SavedStateType& state, const Image& src, const int alpha, int x, int y, bool tiledFill) const + { + state.renderImageUntransformed (*this, src, alpha, x, y, tiledFill); + } + + RectangleList clip; + + //============================================================================== + template + void iterate (Renderer& r) const noexcept + { + for (const Rectangle* i = clip.begin(), * const e = clip.end(); i != e; ++i) + { + const int x = i->getX(); + const int w = i->getWidth(); + jassert (w > 0); + const int bottom = i->getBottom(); + + for (int y = i->getY(); y < bottom; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTableLineFull (x, w); + } + } + } + + private: + //============================================================================== + class SubRectangleIterator + { + public: + SubRectangleIterator (const RectangleList& clipList, const Rectangle& clipBounds) + : clip (clipList), area (clipBounds) + {} + + template + void iterate (Renderer& r) const noexcept + { + for (const Rectangle* i = clip.begin(), * const e = clip.end(); i != e; ++i) + { + const Rectangle rect (i->getIntersection (area)); + + if (! rect.isEmpty()) + { + const int x = rect.getX(); + const int w = rect.getWidth(); + const int bottom = rect.getBottom(); + + for (int y = rect.getY(); y < bottom; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTableLineFull (x, w); + } + } + } + } + + private: + const RectangleList& clip; + const Rectangle area; + + JUCE_DECLARE_NON_COPYABLE (SubRectangleIterator) + }; + + //============================================================================== + class SubRectangleIteratorFloat + { + public: + SubRectangleIteratorFloat (const RectangleList& clipList, const Rectangle& clipBounds) noexcept + : clip (clipList), area (clipBounds) + { + } + + template + void iterate (Renderer& r) const noexcept + { + const RenderingHelpers::FloatRectangleRasterisingInfo f (area); + + for (const Rectangle* i = clip.begin(), * const e = clip.end(); i != e; ++i) + { + const int clipLeft = i->getX(); + const int clipRight = i->getRight(); + const int clipTop = i->getY(); + const int clipBottom = i->getBottom(); + + if (f.totalBottom > clipTop && f.totalTop < clipBottom + && f.totalRight > clipLeft && f.totalLeft < clipRight) + { + if (f.isOnePixelWide()) + { + if (f.topAlpha != 0 && f.totalTop >= clipTop) + { + r.setEdgeTableYPos (f.totalTop); + r.handleEdgeTablePixel (f.left, f.topAlpha); + } + + const int endY = jmin (f.bottom, clipBottom); + for (int y = jmax (clipTop, f.top); y < endY; ++y) + { + r.setEdgeTableYPos (y); + r.handleEdgeTablePixelFull (f.left); + } + + if (f.bottomAlpha != 0 && f.bottom < clipBottom) + { + r.setEdgeTableYPos (f.bottom); + r.handleEdgeTablePixel (f.left, f.bottomAlpha); + } + } + else + { + const int clippedLeft = jmax (f.left, clipLeft); + const int clippedWidth = jmin (f.right, clipRight) - clippedLeft; + const bool doLeftAlpha = f.leftAlpha != 0 && f.totalLeft >= clipLeft; + const bool doRightAlpha = f.rightAlpha != 0 && f.right < clipRight; + + if (f.topAlpha != 0 && f.totalTop >= clipTop) + { + r.setEdgeTableYPos (f.totalTop); + + if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getTopLeftCornerAlpha()); + if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.topAlpha); + if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getTopRightCornerAlpha()); + } + + const int endY = jmin (f.bottom, clipBottom); + for (int y = jmax (clipTop, f.top); y < endY; ++y) + { + r.setEdgeTableYPos (y); + + if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.leftAlpha); + if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth); + if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.rightAlpha); + } + + if (f.bottomAlpha != 0 && f.bottom < clipBottom) + { + r.setEdgeTableYPos (f.bottom); + + if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getBottomLeftCornerAlpha()); + if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.bottomAlpha); + if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getBottomRightCornerAlpha()); + } + } + } + } + } + + private: + const RectangleList& clip; + const Rectangle& area; + + JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat) + }; + + Ptr toEdgeTable() const { return new EdgeTableRegion (clip); } + + RectangleListRegion& operator= (const RectangleListRegion&); + }; +}; + +//============================================================================== +template +class SavedStateBase +{ +public: + typedef typename ClipRegions::Base BaseRegionType; + typedef typename ClipRegions::EdgeTableRegion EdgeTableRegionType; + typedef typename ClipRegions::RectangleListRegion RectangleListRegionType; + + SavedStateBase (const Rectangle& initialClip) + : clip (new RectangleListRegionType (initialClip)), transform (Point()), + interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f) + { + } + + SavedStateBase (const RectangleList& clipList, Point origin) + : clip (new RectangleListRegionType (clipList)), transform (origin), + interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f) + { + } + + SavedStateBase (const SavedStateBase& other) + : clip (other.clip), transform (other.transform), fillType (other.fillType), + interpolationQuality (other.interpolationQuality), + transparencyLayerAlpha (other.transparencyLayerAlpha) + { + } + + SavedStateType& getThis() noexcept { return *static_cast (this); } + + bool clipToRectangle (const Rectangle& r) + { + if (clip != nullptr) + { + if (transform.isOnlyTranslated) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToRectangle (transform.translated (r)); + } + else if (! transform.isRotated) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToRectangle (transform.transformed (r)); + } + else + { + Path p; + p.addRectangle (r); + clipToPath (p, AffineTransform::identity); + } + } + + return clip != nullptr; + } + + bool clipToRectangleList (const RectangleList& r) + { + if (clip != nullptr) + { + if (transform.isOnlyTranslated) + { + cloneClipIfMultiplyReferenced(); + RectangleList offsetList (r); + offsetList.offsetAll (transform.offset.x, transform.offset.y); + clip = clip->clipToRectangleList (offsetList); + } + else if (! transform.isRotated) + { + cloneClipIfMultiplyReferenced(); + RectangleList scaledList; + + for (const Rectangle* i = r.begin(), * const e = r.end(); i != e; ++i) + scaledList.add (transform.transformed (*i)); + + clip = clip->clipToRectangleList (scaledList); + } + else + { + clipToPath (r.toPath(), AffineTransform::identity); + } + } + + return clip != nullptr; + } + + static Rectangle getLargestIntegerWithin (Rectangle r) + { + const int x1 = (int) std::ceil (r.getX()); + const int y1 = (int) std::ceil (r.getY()); + const int x2 = (int) std::floor (r.getRight()); + const int y2 = (int) std::floor (r.getBottom()); + + return Rectangle (x1, y1, x2 - x1, y2 - y1); + } + + bool excludeClipRectangle (const Rectangle& r) + { + if (clip != nullptr) + { + cloneClipIfMultiplyReferenced(); + + if (transform.isOnlyTranslated) + { + clip = clip->excludeClipRectangle (getLargestIntegerWithin (transform.translated (r.toFloat()))); + } + else if (! transform.isRotated) + { + clip = clip->excludeClipRectangle (getLargestIntegerWithin (transform.transformed (r.toFloat()))); + } + else + { + Path p; + p.addRectangle (r.toFloat()); + p.applyTransform (transform.complexTransform); + p.addRectangle (clip->getClipBounds().toFloat()); + p.setUsingNonZeroWinding (false); + clip = clip->clipToPath (p, AffineTransform::identity); + } + } + + return clip != nullptr; + } + + void clipToPath (const Path& p, const AffineTransform& t) + { + if (clip != nullptr) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToPath (p, transform.getTransformWith (t)); + } + } + + void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t) + { + if (clip != nullptr) + { + if (sourceImage.hasAlphaChannel()) + { + cloneClipIfMultiplyReferenced(); + clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t), interpolationQuality); + } + else + { + Path p; + p.addRectangle (sourceImage.getBounds()); + clipToPath (p, t); + } + } + } + + bool clipRegionIntersects (const Rectangle& r) const + { + if (clip != nullptr) + { + if (transform.isOnlyTranslated) + return clip->clipRegionIntersects (transform.translated (r)); + + return getClipBounds().intersects (r); + } + + return false; + } + + Rectangle getClipBounds() const + { + return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds()) + : Rectangle(); + } + + void setFillType (const FillType& newFill) + { + fillType = newFill; + } + + void fillTargetRect (const Rectangle& r, const bool replaceContents) + { + if (fillType.isColour()) + { + clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB(), replaceContents); + } + else + { + const Rectangle clipped (clip->getClipBounds().getIntersection (r)); + + if (! clipped.isEmpty()) + fillShape (new RectangleListRegionType (clipped), false); + } + } + + void fillTargetRect (const Rectangle& r) + { + if (fillType.isColour()) + { + clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB()); + } + else + { + const Rectangle clipped (clip->getClipBounds().toFloat().getIntersection (r)); + + if (! clipped.isEmpty()) + fillShape (new EdgeTableRegionType (clipped), false); + } + } + + template + void fillRectAsPath (const Rectangle& r) + { + Path p; + p.addRectangle (r); + fillPath (p, AffineTransform::identity); + } + + void fillRect (const Rectangle& r, const bool replaceContents) + { + if (clip != nullptr) + { + if (transform.isOnlyTranslated) + { + fillTargetRect (transform.translated (r), replaceContents); + } + else if (! transform.isRotated) + { + fillTargetRect (transform.transformed (r), replaceContents); + } + else + { + jassert (! replaceContents); // not implemented.. + fillRectAsPath (r); + } + } + } + + void fillRect (const Rectangle& r) + { + if (clip != nullptr) + { + if (transform.isOnlyTranslated) + fillTargetRect (transform.translated (r)); + else if (! transform.isRotated) + fillTargetRect (transform.transformed (r)); + else + fillRectAsPath (r); + } + } + + void fillRectList (const RectangleList& list) + { + if (clip != nullptr) + { + if (! transform.isRotated) + { + RectangleList transformed (list); + + if (transform.isOnlyTranslated) + transformed.offsetAll (transform.offset.toFloat()); + else + transformed.transformAll (transform.getTransform()); + + fillShape (new EdgeTableRegionType (transformed), false); + } + else + { + fillPath (list.toPath(), AffineTransform::identity); + } + } + } + + void fillPath (const Path& path, const AffineTransform& t) + { + if (clip != nullptr) + fillShape (new EdgeTableRegionType (clip->getClipBounds(), path, transform.getTransformWith (t)), false); + } + + void fillEdgeTable (const EdgeTable& edgeTable, const float x, const int y) + { + if (clip != nullptr) + { + EdgeTableRegionType* edgeTableClip = new EdgeTableRegionType (edgeTable); + edgeTableClip->edgeTable.translate (x, y); + + if (fillType.isColour()) + { + float brightness = fillType.colour.getBrightness() - 0.5f; + + if (brightness > 0.0f) + edgeTableClip->edgeTable.multiplyLevels (1.0f + 1.6f * brightness); + } + + fillShape (edgeTableClip, false); + } + } + + void drawLine (const Line& line) + { + Path p; + p.addLineSegment (line, 1.0f); + fillPath (p, AffineTransform::identity); + } + + void drawImage (const Image& sourceImage, const AffineTransform& trans) + { + if (clip != nullptr && ! fillType.colour.isTransparent()) + renderImage (sourceImage, trans, nullptr); + } + + static bool isOnlyTranslationAllowingError (const AffineTransform& t) + { + return (std::abs (t.mat01) < 0.002) + && (std::abs (t.mat10) < 0.002) + && (std::abs (t.mat00 - 1.0f) < 0.002) + && (std::abs (t.mat11 - 1.0f) < 0.002); + } + + void renderImage (const Image& sourceImage, const AffineTransform& trans, + const BaseRegionType* const tiledFillClipRegion) + { + const AffineTransform t (transform.getTransformWith (trans)); + + const int alpha = fillType.colour.getAlpha(); + + if (isOnlyTranslationAllowingError (t)) + { + // If our translation doesn't involve any distortion, just use a simple blit.. + int tx = (int) (t.getTranslationX() * 256.0f); + int ty = (int) (t.getTranslationY() * 256.0f); + + if (interpolationQuality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0) + { + tx = ((tx + 128) >> 8); + ty = ((ty + 128) >> 8); + + if (tiledFillClipRegion != nullptr) + { + tiledFillClipRegion->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, true); + } + else + { + Rectangle area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight()); + area = area.getIntersection (getThis().getMaximumBounds()); + + if (! area.isEmpty()) + if (typename BaseRegionType::Ptr c = clip->applyClipTo (new EdgeTableRegionType (area))) + c->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, false); + } + + return; + } + } + + if (! t.isSingularity()) + { + if (tiledFillClipRegion != nullptr) + { + tiledFillClipRegion->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, true); + } + else + { + Path p; + p.addRectangle (sourceImage.getBounds()); + + typename BaseRegionType::Ptr c (clip->clone()); + c = c->clipToPath (p, t); + + if (c != nullptr) + c->renderImageTransformed (getThis(), sourceImage, alpha, t, interpolationQuality, false); + } + } + } + + void fillShape (typename BaseRegionType::Ptr shapeToFill, const bool replaceContents) + { + jassert (clip != nullptr); + + shapeToFill = clip->applyClipTo (shapeToFill); + + if (shapeToFill != nullptr) + { + if (fillType.isGradient()) + { + jassert (! replaceContents); // that option is just for solid colours + + ColourGradient g2 (*(fillType.gradient)); + g2.multiplyOpacity (fillType.getOpacity()); + AffineTransform t (transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f)); + + const bool isIdentity = t.isOnlyTranslation(); + + if (isIdentity) + { + // If our translation doesn't involve any distortion, we can speed it up.. + g2.point1.applyTransform (t); + g2.point2.applyTransform (t); + t = AffineTransform::identity; + } + + shapeToFill->fillAllWithGradient (getThis(), g2, t, isIdentity); + } + else if (fillType.isTiledImage()) + { + renderImage (fillType.image, fillType.transform, shapeToFill); + } + else + { + shapeToFill->fillAllWithColour (getThis(), fillType.colour.getPixelARGB(), replaceContents); + } + } + } + + void cloneClipIfMultiplyReferenced() + { + if (clip->getReferenceCount() > 1) + clip = clip->clone(); + } + + typename BaseRegionType::Ptr clip; + RenderingHelpers::TranslationOrTransform transform; + FillType fillType; + Graphics::ResamplingQuality interpolationQuality; + float transparencyLayerAlpha; +}; + +//============================================================================== +class SoftwareRendererSavedState : public SavedStateBase +{ + typedef SavedStateBase BaseClass; + +public: + SoftwareRendererSavedState (const Image& im, const Rectangle& clipBounds) + : BaseClass (clipBounds), image (im) + { + } + + SoftwareRendererSavedState (const Image& im, const RectangleList& clipList, Point origin) + : BaseClass (clipList, origin), image (im) + { + } + + SoftwareRendererSavedState (const SoftwareRendererSavedState& other) + : BaseClass (other), image (other.image), font (other.font) + { + } + + SoftwareRendererSavedState* beginTransparencyLayer (float opacity) + { + SoftwareRendererSavedState* s = new SoftwareRendererSavedState (*this); + + if (clip != nullptr) + { + const Rectangle layerBounds (clip->getClipBounds()); + + s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true); + s->transparencyLayerAlpha = opacity; + s->transform.moveOriginInDeviceSpace (-layerBounds.getPosition()); + s->cloneClipIfMultiplyReferenced(); + s->clip->translate (-layerBounds.getPosition()); + } + + return s; + } + + void endTransparencyLayer (SoftwareRendererSavedState& finishedLayerState) + { + if (clip != nullptr) + { + const Rectangle layerBounds (clip->getClipBounds()); + + const ScopedPointer g (image.createLowLevelContext()); + g->setOpacity (finishedLayerState.transparencyLayerAlpha); + g->drawImage (finishedLayerState.image, AffineTransform::translation (layerBounds.getPosition())); + } + } + + typedef GlyphCache, SoftwareRendererSavedState> GlyphCacheType; + + static void clearGlyphCache() + { + GlyphCacheType::getInstance().reset(); + } + + //============================================================================== + void drawGlyph (int glyphNumber, const AffineTransform& trans) + { + if (clip != nullptr) + { + if (trans.isOnlyTranslation() && ! transform.isRotated) + { + GlyphCacheType& cache = GlyphCacheType::getInstance(); + + Point pos (trans.getTranslationX(), trans.getTranslationY()); + + if (transform.isOnlyTranslated) + { + cache.drawGlyph (*this, font, glyphNumber, pos + transform.offset.toFloat()); + } + else + { + pos = transform.transformed (pos); + + Font f (font); + f.setHeight (font.getHeight() * transform.complexTransform.mat11); + + const float xScale = transform.complexTransform.mat00 / transform.complexTransform.mat11; + if (std::abs (xScale - 1.0f) > 0.01f) + f.setHorizontalScale (xScale); + + cache.drawGlyph (*this, f, glyphNumber, pos); + } + } + else + { + const float fontHeight = font.getHeight(); + + AffineTransform t (transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight) + .followedBy (trans))); + + const ScopedPointer et (font.getTypeface()->getEdgeTableForGlyph (glyphNumber, t, fontHeight)); + + if (et != nullptr) + fillShape (new EdgeTableRegionType (*et), false); + } + } + } + + Rectangle getMaximumBounds() const { return image.getBounds(); } + + //============================================================================== + template + void renderImageTransformed (IteratorType& iter, const Image& src, const int alpha, const AffineTransform& trans, Graphics::ResamplingQuality quality, bool tiledFill) const + { + Image::BitmapData destData (image, Image::BitmapData::readWrite); + const Image::BitmapData srcData (src, Image::BitmapData::readOnly); + EdgeTableFillers::renderImageTransformed (iter, destData, srcData, alpha, trans, quality, tiledFill); + } + + template + void renderImageUntransformed (IteratorType& iter, const Image& src, const int alpha, int x, int y, bool tiledFill) const + { + Image::BitmapData destData (image, Image::BitmapData::readWrite); + const Image::BitmapData srcData (src, Image::BitmapData::readOnly); + EdgeTableFillers::renderImageUntransformed (iter, destData, srcData, alpha, x, y, tiledFill); + } + + template + void fillWithSolidColour (IteratorType& iter, const PixelARGB colour, bool replaceContents) const + { + Image::BitmapData destData (image, Image::BitmapData::readWrite); + + switch (destData.pixelFormat) + { + case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) 0); break; + case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) 0); break; + default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) 0); break; + } + } + + template + void fillWithGradient (IteratorType& iter, ColourGradient& gradient, const AffineTransform& trans, bool isIdentity) const + { + HeapBlock lookupTable; + const int numLookupEntries = gradient.createLookupTable (trans, lookupTable); + jassert (numLookupEntries > 0); + + Image::BitmapData destData (image, Image::BitmapData::readWrite); + + switch (destData.pixelFormat) + { + case Image::ARGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) 0); break; + case Image::RGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) 0); break; + default: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) 0); break; + } + } + + //============================================================================== + Image image; + Font font; + +private: + SoftwareRendererSavedState& operator= (const SoftwareRendererSavedState&); +}; + +//============================================================================== +template +class SavedStateStack +{ +public: + SavedStateStack (StateObjectType* const initialState) noexcept + : currentState (initialState) + {} + + SavedStateStack() noexcept {} + + void initialise (StateObjectType* state) + { + currentState = state; + } + + inline StateObjectType* operator->() const noexcept { return currentState; } + inline StateObjectType& operator*() const noexcept { return *currentState; } + + void save() + { + stack.add (new StateObjectType (*currentState)); + } + + void restore() + { + if (StateObjectType* const top = stack.getLast()) + { + currentState = top; + stack.removeLast (1, false); + } + else + { + jassertfalse; // trying to pop with an empty stack! + } + } + + void beginTransparencyLayer (float opacity) + { + save(); + currentState = currentState->beginTransparencyLayer (opacity); + } + + void endTransparencyLayer() + { + const ScopedPointer finishedTransparencyLayer (currentState); + restore(); + currentState->endTransparencyLayer (*finishedTransparencyLayer); + } + +private: + ScopedPointer currentState; + OwnedArray stack; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedStateStack) +}; + +//============================================================================== +template +class StackBasedLowLevelGraphicsContext : public LowLevelGraphicsContext +{ +public: + bool isVectorDevice() const override { return false; } + void setOrigin (Point o) override { stack->transform.setOrigin (o); } + void addTransform (const AffineTransform& t) override { stack->transform.addTransform (t); } + float getPhysicalPixelScaleFactor() override { return stack->transform.getPhysicalPixelScaleFactor(); } + Rectangle getClipBounds() const override { return stack->getClipBounds(); } + bool isClipEmpty() const override { return stack->clip == nullptr; } + bool clipRegionIntersects (const Rectangle& r) override { return stack->clipRegionIntersects (r); } + bool clipToRectangle (const Rectangle& r) override { return stack->clipToRectangle (r); } + bool clipToRectangleList (const RectangleList& r) override { return stack->clipToRectangleList (r); } + void excludeClipRectangle (const Rectangle& r) override { stack->excludeClipRectangle (r); } + void clipToPath (const Path& path, const AffineTransform& t) override { stack->clipToPath (path, t); } + void clipToImageAlpha (const Image& im, const AffineTransform& t) override { stack->clipToImageAlpha (im, t); } + void saveState() override { stack.save(); } + void restoreState() override { stack.restore(); } + void beginTransparencyLayer (float opacity) override { stack.beginTransparencyLayer (opacity); } + void endTransparencyLayer() override { stack.endTransparencyLayer(); } + void setFill (const FillType& fillType) override { stack->setFillType (fillType); } + void setOpacity (float newOpacity) override { stack->fillType.setOpacity (newOpacity); } + void setInterpolationQuality (Graphics::ResamplingQuality quality) override { stack->interpolationQuality = quality; } + void fillRect (const Rectangle& r, bool replace) override { stack->fillRect (r, replace); } + void fillRect (const Rectangle& r) override { stack->fillRect (r); } + void fillRectList (const RectangleList& list) override { stack->fillRectList (list); } + void fillPath (const Path& path, const AffineTransform& t) override { stack->fillPath (path, t); } + void drawImage (const Image& im, const AffineTransform& t) override { stack->drawImage (im, t); } + void drawGlyph (int glyphNumber, const AffineTransform& t) override { stack->drawGlyph (glyphNumber, t); } + void drawLine (const Line& line) override { stack->drawLine (line); } + void setFont (const Font& newFont) override { stack->font = newFont; } + const Font& getFont() override { return stack->font; } + +protected: + StackBasedLowLevelGraphicsContext (SavedStateType* initialState) : stack (initialState) {} + StackBasedLowLevelGraphicsContext() {} + + RenderingHelpers::SavedStateStack stack; +}; + +} + +#if JUCE_MSVC + #pragma warning (pop) +#endif + +#endif // JUCE_RENDERINGHELPERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp index a04c522..29702a2 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp @@ -1,51 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -//============================================================================== -#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ - STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \ - STATICMETHOD (createFromFile, "createFromFile", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ - -DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface"); -#undef JNI_CLASS_MEMBERS - - -//============================================================================== -StringArray Font::findAllTypefaceNames() -{ - StringArray results; - - Array fonts; - File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, "*.ttf"); - - for (int i = 0; i < fonts.size(); ++i) - results.add (fonts.getReference(i).getFileNameWithoutExtension()); - - return results; -} - struct DefaultFontNames { DefaultFontNames() @@ -56,6 +32,15 @@ struct DefaultFontNames { } + String getRealFontName (const String& faceName) const + { + if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans; + if (faceName == Font::getDefaultSerifFontName()) return defaultSerif; + if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed; + + return faceName; + } + String defaultSans, defaultSerif, defaultFixed, defaultFallback; }; @@ -63,59 +48,133 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; - String faceName (font.getTypefaceName()); - - if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; - else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; - else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; - Font f (font); - f.setTypefaceName (faceName); + f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName())); return Typeface::createSystemTypefaceFor (f); } +//============================================================================== +#if JUCE_USE_FREETYPE + +StringArray FTTypefaceList::getDefaultFontDirectories() +{ + return StringArray ("/system/fonts"); +} + +Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) +{ + return new FreeTypeTypeface (font); +} + +void Typeface::scanFolderForFonts (const File& folder) +{ + FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName())); +} + +StringArray Font::findAllTypefaceNames() +{ + return FTTypefaceList::getInstance()->findAllFamilyNames(); +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + return FTTypefaceList::getInstance()->findAllTypefaceStyles (family); +} + +bool TextLayout::createNativeLayout (const AttributedString&) +{ + return false; +} + +#else + +//============================================================================== +#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ + STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \ + STATICMETHOD (createFromFile, "createFromFile", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \ + +DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface"); +#undef JNI_CLASS_MEMBERS + +//============================================================================== +StringArray Font::findAllTypefaceNames() +{ + StringArray results; + + Array fonts; + File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, "*.ttf"); + + for (int i = 0; i < fonts.size(); ++i) + results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension() + .upToLastOccurrenceOf ("-", false, false)); + + return results; +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + StringArray results ("Regular"); + + Array fonts; + File ("/system/fonts").findChildFiles (fonts, File::findFiles, false, family + "-*.ttf"); + + for (int i = 0; i < fonts.size(); ++i) + results.addIfNotAlreadyThere (fonts.getReference(i).getFileNameWithoutExtension() + .fromLastOccurrenceOf ("-", false, false)); + + return results; +} + +const float referenceFontSize = 256.0f; +const float referenceFontToUnits = 1.0f / referenceFontSize; + //============================================================================== class AndroidTypeface : public Typeface { public: AndroidTypeface (const Font& font) - : Typeface (font.getTypefaceName()), - ascent (0), - descent (0) + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), + ascent (0), descent (0), heightToPointsFactor (1.0f) { - jint flags = 0; - if (font.isBold()) flags = 1; - if (font.isItalic()) flags += 2; + JNIEnv* const env = getEnv(); - JNIEnv* env = getEnv(); + const bool isBold = style.contains ("Bold"); + const bool isItalic = style.contains ("Italic"); - File fontFile (File ("/system/fonts").getChildFile (name).withFileExtension (".ttf")); + File fontFile (getFontFile (name, style)); + + if (! fontFile.exists()) + fontFile = findFontFile (name, isBold, isItalic); if (fontFile.exists()) typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.createFromFile, javaString (fontFile.getFullPathName()).get())); else typeface = GlobalRef (env->CallStaticObjectMethod (TypefaceClass, TypefaceClass.create, - javaString (getName()).get(), flags)); + javaString (getName()).get(), + (isBold ? 1 : 0) + (isItalic ? 2 : 0))); rect = GlobalRef (env->NewObject (RectClass, RectClass.constructor, 0, 0, 0, 0)); paint = GlobalRef (GraphicsHelpers::createPaint (Graphics::highResamplingQuality)); const LocalRef ignored (paint.callObjectMethod (Paint.setTypeface, typeface.get())); - const float standardSize = 256.0f; - paint.callVoidMethod (Paint.setTextSize, standardSize); - ascent = std::abs (paint.callFloatMethod (Paint.ascent)) / standardSize; - descent = paint.callFloatMethod (Paint.descent) / standardSize; + paint.callVoidMethod (Paint.setTextSize, referenceFontSize); - const float height = ascent + descent; - unitsToHeightScaleFactor = 1.0f / 256.0f; + const float fullAscent = std::abs (paint.callFloatMethod (Paint.ascent)); + const float fullDescent = paint.callFloatMethod (Paint.descent); + const float totalHeight = fullAscent + fullDescent; + + ascent = fullAscent / totalHeight; + descent = fullDescent / totalHeight; + heightToPointsFactor = referenceFontSize / totalHeight; } - float getAscent() const { return ascent; } - float getDescent() const { return descent; } + float getAscent() const override { return ascent; } + float getDescent() const override { return descent; } + float getHeightToPointsFactor() const override { return heightToPointsFactor; } - float getStringWidth (const String& text) + float getStringWidth (const String& text) override { JNIEnv* env = getEnv(); const int numChars = text.length(); @@ -131,10 +190,10 @@ public: for (int i = 0; i < numDone; ++i) x += localWidths[i]; - return x * unitsToHeightScaleFactor; + return x * referenceFontToUnits; } - void getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) + void getGlyphPositions (const String& text, Array& glyphs, Array& xOffsets) override { JNIEnv* env = getEnv(); const int numChars = text.length(); @@ -155,20 +214,20 @@ public: { glyphs.add ((int) s.getAndAdvance()); x += localWidths[i]; - xOffsets.add (x * unitsToHeightScaleFactor); + xOffsets.add (x * referenceFontToUnits); } } - bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) + bool getOutlineForGlyph (int /*glyphNumber*/, Path& /*destPath*/) override { return false; } - EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t) + EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& t, float /*fontHeight*/) override { JNIEnv* env = getEnv(); - jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (unitsToHeightScaleFactor, unitsToHeightScaleFactor).followedBy (t)); + jobject matrix = GraphicsHelpers::createMatrix (env, AffineTransform::scale (referenceFontToUnits).followedBy (t)); jintArray maskData = (jintArray) android.activity.callObjectMethod (JuceAppActivity.renderGlyph, (jchar) glyphNumber, paint.get(), matrix, rect.get()); env->DeleteLocalRef (matrix); @@ -209,10 +268,45 @@ public: } GlobalRef typeface, paint, rect; - float ascent, descent, unitsToHeightScaleFactor; + float ascent, descent, heightToPointsFactor; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface); + static File findFontFile (const String& family, + const bool bold, const bool italic) + { + File file; + + if (bold || italic) + { + String suffix; + if (bold) suffix = "Bold"; + if (italic) suffix << "Italic"; + + file = getFontFile (family, suffix); + + if (file.exists()) + return file; + } + + file = getFontFile (family, "Regular"); + + if (! file.exists()) + file = getFontFile (family, String()); + + return file; + } + + static File getFontFile (const String& family, const String& style) + { + String path ("/system/fonts/" + family); + + if (style.isNotEmpty()) + path << '-' << style; + + return File (path + ".ttf"); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidTypeface) }; //============================================================================== @@ -221,7 +315,20 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) return new AndroidTypeface (font); } +Typeface::Ptr Typeface::createSystemTypefaceFor (const void*, size_t) +{ + jassertfalse; // not yet implemented! + return nullptr; +} + +void Typeface::scanFolderForFonts (const File&) +{ + jassertfalse; // not available unless using FreeType +} + bool TextLayout::createNativeLayout (const AttributedString&) { return false; } + +#endif diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_android_GraphicsContext.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_android_GraphicsContext.cpp index 0793958..9382c1d 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_android_GraphicsContext.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_android_GraphicsContext.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -55,7 +54,7 @@ namespace GraphicsHelpers } } -ImagePixelData* NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const +ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { return SoftwareImageType().create (format, width, height, clearImage); } diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp new file mode 100644 index 0000000..43a6436 --- /dev/null +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp @@ -0,0 +1,455 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +struct FTLibWrapper : public ReferenceCountedObject +{ + FTLibWrapper() : library (0) + { + if (FT_Init_FreeType (&library) != 0) + { + library = 0; + DBG ("Failed to initialize FreeType"); + } + } + + ~FTLibWrapper() + { + if (library != 0) + FT_Done_FreeType (library); + } + + FT_Library library; + + typedef ReferenceCountedObjectPtr Ptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTLibWrapper) +}; + +//============================================================================== +struct FTFaceWrapper : public ReferenceCountedObject +{ + FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex) + : face (0), library (ftLib) + { + if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0) + face = 0; + } + + FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const void* data, size_t dataSize, int faceIndex) + : face (0), library (ftLib), savedFaceData (data, dataSize) + { + if (FT_New_Memory_Face (ftLib->library, (const FT_Byte*) savedFaceData.getData(), + (FT_Long) savedFaceData.getSize(), faceIndex, &face) != 0) + face = 0; + } + + ~FTFaceWrapper() + { + if (face != 0) + FT_Done_Face (face); + } + + FT_Face face; + FTLibWrapper::Ptr library; + MemoryBlock savedFaceData; + + typedef ReferenceCountedObjectPtr Ptr; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper) +}; + +//============================================================================== +class FTTypefaceList : private DeletedAtShutdown +{ +public: + FTTypefaceList() : library (new FTLibWrapper()) + { + scanFontPaths (getDefaultFontDirectories()); + } + + ~FTTypefaceList() + { + clearSingletonInstance(); + } + + //============================================================================== + struct KnownTypeface + { + KnownTypeface (const File& f, const int index, const FTFaceWrapper& face) + : file (f), + family (face.face->family_name), + style (face.face->style_name), + faceIndex (index), + isMonospaced ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0), + isSansSerif (isFaceSansSerif (family)) + { + } + + const File file; + const String family, style; + const int faceIndex; + const bool isMonospaced, isSansSerif; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownTypeface) + }; + + //============================================================================== + static FTFaceWrapper::Ptr selectUnicodeCharmap (FTFaceWrapper* face) + { + if (face != nullptr) + if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0) + FT_Set_Charmap (face->face, face->face->charmaps[0]); + + return face; + } + + FTFaceWrapper::Ptr createFace (const void* data, size_t dataSize, int index) + { + return selectUnicodeCharmap (new FTFaceWrapper (library, data, dataSize, index)); + } + + FTFaceWrapper::Ptr createFace (const File& file, int index) + { + return selectUnicodeCharmap (new FTFaceWrapper (library, file, index)); + } + + FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle) + { + const KnownTypeface* ftFace = matchTypeface (fontName, fontStyle); + + if (ftFace == nullptr) ftFace = matchTypeface (fontName, "Regular"); + if (ftFace == nullptr) ftFace = matchTypeface (fontName, String()); + + if (ftFace != nullptr) + return createFace (ftFace->file, ftFace->faceIndex); + + return nullptr; + } + + //============================================================================== + StringArray findAllFamilyNames() const + { + StringArray s; + + for (int i = 0; i < faces.size(); ++i) + s.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + + return s; + } + + static int indexOfRegularStyle (const StringArray& styles) + { + int i = styles.indexOf ("Regular", true); + + if (i < 0) + for (i = 0; i < styles.size(); ++i) + if (! (styles[i].containsIgnoreCase ("Bold") || styles[i].containsIgnoreCase ("Italic"))) + break; + + return i; + } + + StringArray findAllTypefaceStyles (const String& family) const + { + StringArray s; + + for (int i = 0; i < faces.size(); ++i) + { + const KnownTypeface* const face = faces.getUnchecked(i); + + if (face->family == family) + s.addIfNotAlreadyThere (face->style); + } + + // try to get a regular style to be first in the list + const int regular = indexOfRegularStyle (s); + if (regular > 0) + s.strings.swap (0, regular); + + return s; + } + + void scanFontPaths (const StringArray& paths) + { + for (int i = 0; i < paths.size(); ++i) + { + DirectoryIterator iter (File::getCurrentWorkingDirectory() + .getChildFile (paths[i]), true); + + while (iter.next()) + if (iter.getFile().hasFileExtension ("ttf;pfb;pcf;otf")) + scanFont (iter.getFile()); + } + } + + void getMonospacedNames (StringArray& monoSpaced) const + { + for (int i = 0; i < faces.size(); ++i) + if (faces.getUnchecked(i)->isMonospaced) + monoSpaced.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + } + + void getSerifNames (StringArray& serif) const + { + for (int i = 0; i < faces.size(); ++i) + if (! faces.getUnchecked(i)->isSansSerif) + serif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + } + + void getSansSerifNames (StringArray& sansSerif) const + { + for (int i = 0; i < faces.size(); ++i) + if (faces.getUnchecked(i)->isSansSerif) + sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); + } + + juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList); + +private: + FTLibWrapper::Ptr library; + OwnedArray faces; + + static StringArray getDefaultFontDirectories(); + + void scanFont (const File& file) + { + int faceIndex = 0; + int numFaces = 0; + + do + { + FTFaceWrapper face (library, file, faceIndex); + + if (face.face != 0) + { + if (faceIndex == 0) + numFaces = face.face->num_faces; + + if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) + faces.add (new KnownTypeface (file, faceIndex, face)); + } + + ++faceIndex; + } + while (faceIndex < numFaces); + } + + const KnownTypeface* matchTypeface (const String& familyName, const String& style) const noexcept + { + for (int i = 0; i < faces.size(); ++i) + { + const KnownTypeface* const face = faces.getUnchecked(i); + + if (face->family == familyName + && (face->style.equalsIgnoreCase (style) || style.isEmpty())) + return face; + } + + return nullptr; + } + + static bool isFaceSansSerif (const String& family) + { + static const char* sansNames[] = { "Sans", "Verdana", "Arial", "Ubuntu" }; + + for (int i = 0; i < numElementsInArray (sansNames); ++i) + if (family.containsIgnoreCase (sansNames[i])) + return true; + + return false; + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTTypefaceList) +}; + +juce_ImplementSingleton_SingleThreaded (FTTypefaceList) + + +//============================================================================== +class FreeTypeTypeface : public CustomTypeface +{ +public: + FreeTypeTypeface (const Font& font) + : faceWrapper (FTTypefaceList::getInstance()->createFace (font.getTypefaceName(), + font.getTypefaceStyle())) + { + if (faceWrapper != nullptr) + initialiseCharacteristics (font.getTypefaceName(), + font.getTypefaceStyle()); + } + + FreeTypeTypeface (const void* data, size_t dataSize) + : faceWrapper (FTTypefaceList::getInstance()->createFace (data, dataSize, 0)) + { + if (faceWrapper != nullptr) + initialiseCharacteristics (faceWrapper->face->family_name, + faceWrapper->face->style_name); + } + + void initialiseCharacteristics (const String& name, const String& style) + { + setCharacteristics (name, style, + faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), + L' '); + } + + bool loadGlyphIfPossible (const juce_wchar character) + { + if (faceWrapper != nullptr) + { + FT_Face face = faceWrapper->face; + const unsigned int glyphIndex = FT_Get_Char_Index (face, character); + + if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING) == 0 + && face->glyph->format == ft_glyph_format_outline) + { + const float scale = 1.0f / (float) (face->ascender - face->descender); + Path destShape; + + if (getGlyphShape (destShape, face->glyph->outline, scale)) + { + addGlyph (character, destShape, face->glyph->metrics.horiAdvance * scale); + + if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) + addKerning (face, character, glyphIndex); + + return true; + } + } + } + + return false; + } + +private: + FTFaceWrapper::Ptr faceWrapper; + + bool getGlyphShape (Path& destShape, const FT_Outline& outline, const float scaleX) + { + const float scaleY = -scaleX; + const short* const contours = outline.contours; + const char* const tags = outline.tags; + const FT_Vector* const points = outline.points; + + for (int c = 0; c < outline.n_contours; ++c) + { + const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1; + const int endPoint = contours[c]; + + for (int p = startPoint; p <= endPoint; ++p) + { + const float x = scaleX * points[p].x; + const float y = scaleY * points[p].y; + + if (p == startPoint) + { + if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) + { + float x2 = scaleX * points [endPoint].x; + float y2 = scaleY * points [endPoint].y; + + if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On) + { + x2 = (x + x2) * 0.5f; + y2 = (y + y2) * 0.5f; + } + + destShape.startNewSubPath (x2, y2); + } + else + { + destShape.startNewSubPath (x, y); + } + } + + if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On) + { + if (p != startPoint) + destShape.lineTo (x, y); + } + else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) + { + const int nextIndex = (p == endPoint) ? startPoint : p + 1; + float x2 = scaleX * points [nextIndex].x; + float y2 = scaleY * points [nextIndex].y; + + if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic) + { + x2 = (x + x2) * 0.5f; + y2 = (y + y2) * 0.5f; + } + else + { + ++p; + } + + destShape.quadraticTo (x, y, x2, y2); + } + else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic) + { + const int next1 = p + 1; + const int next2 = (p == (endPoint - 1)) ? startPoint : (p + 2); + + if (p >= endPoint + || FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic + || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) + return false; + + const float x2 = scaleX * points [next1].x; + const float y2 = scaleY * points [next1].y; + const float x3 = scaleX * points [next2].x; + const float y3 = scaleY * points [next2].y; + + destShape.cubicTo (x, y, x2, y2, x3, y3); + p += 2; + } + } + + destShape.closeSubPath(); + } + + return true; + } + + void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex) + { + const float height = (float) (face->ascender - face->descender); + + uint32 rightGlyphIndex; + uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); + + while (rightGlyphIndex != 0) + { + FT_Vector kerning; + + if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0 + && kerning.x != 0) + addKerningPair (character, rightCharCode, kerning.x / height); + + rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); + } + } + + JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface) +}; diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp index 7132441..0869ec7 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp @@ -1,466 +1,97 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -struct FTLibWrapper : public ReferenceCountedObject +StringArray FTTypefaceList::getDefaultFontDirectories() { - FTLibWrapper() : library (0) - { - if (FT_Init_FreeType (&library) != 0) - { - library = 0; - DBG ("Failed to initialize FreeType"); - } - } - - ~FTLibWrapper() - { - if (library != 0) - FT_Done_FreeType (library); - } - - FT_Library library; - - typedef ReferenceCountedObjectPtr Ptr; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTLibWrapper); -}; - -//============================================================================== -struct FTFaceWrapper : public ReferenceCountedObject -{ - FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex) - : face (0), library (ftLib) - { - if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0) - face = 0; - } - - ~FTFaceWrapper() - { - if (face != 0) - FT_Done_Face (face); - } - - FT_Face face; - FTLibWrapper::Ptr library; - - typedef ReferenceCountedObjectPtr Ptr; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper); -}; - -//============================================================================== -class LinuxFontFileIterator -{ -public: - LinuxFontFileIterator() - : index (0) - { - fontDirs.addTokens (CharPointer_UTF8 (getenv ("JUCE_FONT_PATH")), ";,", String::empty); - fontDirs.removeEmptyStrings (true); - - if (fontDirs.size() == 0) - { - const ScopedPointer fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf"))); - - if (fontsInfo != nullptr) - { - forEachXmlChildElementWithTagName (*fontsInfo, e, "dir") - { - fontDirs.add (e->getAllSubText().trim()); - } - } - } - - if (fontDirs.size() == 0) - fontDirs.add ("/usr/X11R6/lib/X11/fonts"); - - fontDirs.removeEmptyStrings (true); - } - - bool next() - { - if (iter != nullptr) - { - while (iter->next()) - if (getFile().hasFileExtension ("ttf;pfb;pcf")) - return true; - } - - if (index >= fontDirs.size()) - return false; - - iter = new DirectoryIterator (fontDirs [index++], true); - return next(); - } - - File getFile() const { jassert (iter != nullptr); return iter->getFile(); } - -private: StringArray fontDirs; - int index; - ScopedPointer iter; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxFontFileIterator); -}; + fontDirs.addTokens (String (CharPointer_UTF8 (getenv ("JUCE_FONT_PATH"))), ";,", ""); + fontDirs.removeEmptyStrings (true); -//============================================================================== -class FTTypefaceList : public DeletedAtShutdown -{ -public: - FTTypefaceList() - : library (new FTLibWrapper()) + if (fontDirs.size() == 0) { - LinuxFontFileIterator fontFileIterator; + const ScopedPointer fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf"))); - while (fontFileIterator.next()) + if (fontsInfo != nullptr) { - int faceIndex = 0; - int numFaces = 0; - - do + forEachXmlChildElementWithTagName (*fontsInfo, e, "dir") { - FTFaceWrapper face (library, fontFileIterator.getFile(), faceIndex); + String fontPath (e->getAllSubText().trim()); - if (face.face != 0) + if (fontPath.isNotEmpty()) { - if (faceIndex == 0) - numFaces = face.face->num_faces; - - if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0) - faces.add (new KnownTypeface (fontFileIterator.getFile(), faceIndex, face)); - } - - ++faceIndex; - } - while (faceIndex < numFaces); - } - } - - ~FTTypefaceList() - { - clearSingletonInstance(); - } - - //============================================================================== - struct KnownTypeface - { - KnownTypeface (const File& file_, const int faceIndex_, const FTFaceWrapper& face) - : file (file_), - family (face.face->family_name), - faceIndex (faceIndex_), - isBold ((face.face->style_flags & FT_STYLE_FLAG_BOLD) != 0), - isItalic ((face.face->style_flags & FT_STYLE_FLAG_ITALIC) != 0), - isMonospaced ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0), - isSansSerif (isFaceSansSerif (family)) - { - } - - const File file; - const String family; - const int faceIndex; - const bool isBold, isItalic, isMonospaced, isSansSerif; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownTypeface); - }; - - //============================================================================== - FTFaceWrapper::Ptr createFace (const String& fontName, const bool bold, const bool italic) - { - const KnownTypeface* ftFace = matchTypeface (fontName, bold, italic); - - if (ftFace == nullptr) - { - ftFace = matchTypeface (fontName, ! bold, italic); - - if (ftFace == nullptr) - { - ftFace = matchTypeface (fontName, bold, ! italic); - - if (ftFace == nullptr) - ftFace = matchTypeface (fontName, ! bold, ! italic); - } - } - - if (ftFace != nullptr) - { - FTFaceWrapper::Ptr face (new FTFaceWrapper (library, ftFace->file, ftFace->faceIndex)); - - if (face->face != 0) - { - // If there isn't a unicode charmap then select the first one. - if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0) - FT_Set_Charmap (face->face, face->face->charmaps[0]); - - return face; - } - } - - return nullptr; - } - - //============================================================================== - void getFamilyNames (StringArray& familyNames) const - { - for (int i = 0; i < faces.size(); i++) - familyNames.addIfNotAlreadyThere (faces.getUnchecked(i)->family); - } - - void getMonospacedNames (StringArray& monoSpaced) const - { - for (int i = 0; i < faces.size(); i++) - if (faces.getUnchecked(i)->isMonospaced) - monoSpaced.addIfNotAlreadyThere (faces.getUnchecked(i)->family); - } - - void getSerifNames (StringArray& serif) const - { - for (int i = 0; i < faces.size(); i++) - if (! faces.getUnchecked(i)->isSansSerif) - serif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); - } - - void getSansSerifNames (StringArray& sansSerif) const - { - for (int i = 0; i < faces.size(); i++) - if (faces.getUnchecked(i)->isSansSerif) - sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family); - } - - juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList); - -private: - FTLibWrapper::Ptr library; - OwnedArray faces; - - const KnownTypeface* matchTypeface (const String& familyName, const bool wantBold, const bool wantItalic) const noexcept - { - for (int i = 0; i < faces.size(); ++i) - { - const KnownTypeface* const face = faces.getUnchecked(i); - - if (face->family == familyName - && face->isBold == wantBold - && face->isItalic == wantItalic) - return face; - } - - return nullptr; - } - - static bool isFaceSansSerif (const String& family) - { - const char* sansNames[] = { "Sans", "Verdana", "Arial", "Ubuntu" }; - - for (int i = 0; i < numElementsInArray (sansNames); ++i) - if (family.containsIgnoreCase (sansNames[i])) - return true; - - return false; - } - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTTypefaceList); -}; - -juce_ImplementSingleton_SingleThreaded (FTTypefaceList) - - -//============================================================================== -class FreeTypeTypeface : public CustomTypeface -{ -public: - FreeTypeTypeface (const Font& font) - : faceWrapper (FTTypefaceList::getInstance() - ->createFace (font.getTypefaceName(), font.isBold(), font.isItalic())) - { - if (faceWrapper != nullptr) - { - setCharacteristics (font.getTypefaceName(), - faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), - font.isBold(), font.isItalic(), - L' '); - } - else - { - DBG ("Failed to create typeface: " << font.getTypefaceName() << " " - << (font.isBold() ? 'B' : ' ') << (font.isItalic() ? 'I' : ' ')); - } - } - - bool loadGlyphIfPossible (const juce_wchar character) - { - if (faceWrapper != nullptr) - { - FT_Face face = faceWrapper->face; - const unsigned int glyphIndex = FT_Get_Char_Index (face, character); - - if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) == 0 - && face->glyph->format == ft_glyph_format_outline) - { - const float scale = 1.0f / (float) (face->ascender - face->descender); - Path destShape; - - if (getGlyphShape (destShape, face->glyph->outline, scale)) - { - addGlyph (character, destShape, face->glyph->metrics.horiAdvance * scale); - - if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0) - addKerning (face, character, glyphIndex); - - return true; - } - } - } - - return false; - } - -private: - FTFaceWrapper::Ptr faceWrapper; - - bool getGlyphShape (Path& destShape, const FT_Outline& outline, const float scaleX) - { - const float scaleY = -scaleX; - const short* const contours = outline.contours; - const char* const tags = outline.tags; - const FT_Vector* const points = outline.points; - - for (int c = 0; c < outline.n_contours; ++c) - { - const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1; - const int endPoint = contours[c]; - - for (int p = startPoint; p <= endPoint; ++p) - { - const float x = scaleX * points[p].x; - const float y = scaleY * points[p].y; - - if (p == startPoint) - { - if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) + if (e->getStringAttribute ("prefix") == "xdg") { - float x2 = scaleX * points [endPoint].x; - float y2 = scaleY * points [endPoint].y; + String xdgDataHome (SystemStats::getEnvironmentVariable ("XDG_DATA_HOME", String())); - if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On) - { - x2 = (x + x2) * 0.5f; - y2 = (y + y2) * 0.5f; - } + if (xdgDataHome.trimStart().isEmpty()) + xdgDataHome = "~/.local/share"; - destShape.startNewSubPath (x2, y2); - } - else - { - destShape.startNewSubPath (x, y); - } - } - - if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On) - { - if (p != startPoint) - destShape.lineTo (x, y); - } - else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic) - { - const int nextIndex = (p == endPoint) ? startPoint : p + 1; - float x2 = scaleX * points [nextIndex].x; - float y2 = scaleY * points [nextIndex].y; - - if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic) - { - x2 = (x + x2) * 0.5f; - y2 = (y + y2) * 0.5f; - } - else - { - ++p; + fontPath = File (xdgDataHome).getChildFile (fontPath).getFullPathName(); } - destShape.quadraticTo (x, y, x2, y2); - } - else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic) - { - const int next1 = p + 1; - const int next2 = (p == (endPoint - 1)) ? startPoint : (p + 2); - - if (p >= endPoint - || FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic - || FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On) - return false; - - const float x2 = scaleX * points [next1].x; - const float y2 = scaleY * points [next1].y; - const float x3 = scaleX * points [next2].x; - const float y3 = scaleY * points [next2].y; - - destShape.cubicTo (x, y, x2, y2, x3, y3); - p += 2; + fontDirs.add (fontPath); } } - - destShape.closeSubPath(); - } - - return true; - } - - void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex) - { - const float height = (float) (face->ascender - face->descender); - - uint32 rightGlyphIndex; - uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex); - - while (rightGlyphIndex != 0) - { - FT_Vector kerning; - - if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0 - && kerning.x != 0) - addKerningPair (character, rightCharCode, kerning.x / height); - - rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex); } } - JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface); -}; + if (fontDirs.size() == 0) + fontDirs.add ("/usr/X11R6/lib/X11/fonts"); + + fontDirs.removeDuplicates (false); + return fontDirs; +} -//============================================================================== Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return new FreeTypeTypeface (font); } +Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) +{ + return new FreeTypeTypeface (data, dataSize); +} + +void Typeface::scanFolderForFonts (const File& folder) +{ + FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName())); +} + StringArray Font::findAllTypefaceNames() { - StringArray s; - FTTypefaceList::getInstance()->getFamilyNames (s); - s.sort (true); - return s; + return FTTypefaceList::getInstance()->findAllFamilyNames(); +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + return FTTypefaceList::getInstance()->findAllTypefaceStyles (family); +} + +bool TextLayout::createNativeLayout (const AttributedString&) +{ + return false; } //============================================================================== @@ -473,6 +104,15 @@ struct DefaultFontNames { } + String getRealFontName (const String& faceName) const + { + if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans; + if (faceName == Font::getDefaultSerifFontName()) return defaultSerif; + if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed; + + return faceName; + } + String defaultSans, defaultSerif, defaultFixed; private: @@ -480,17 +120,16 @@ private: { const StringArray choices (choicesArray); - int j; - for (j = 0; j < choices.size(); ++j) + for (int j = 0; j < choices.size(); ++j) if (names.contains (choices[j], true)) return choices[j]; - for (j = 0; j < choices.size(); ++j) + for (int j = 0; j < choices.size(); ++j) for (int i = 0; i < names.size(); ++i) if (names[i].startsWithIgnoreCase (choices[j])) return names[i]; - for (j = 0; j < choices.size(); ++j) + for (int j = 0; j < choices.size(); ++j) for (int i = 0; i < names.size(); ++i) if (names[i].containsIgnoreCase (choices[j])) return names[i]; @@ -503,7 +142,8 @@ private: StringArray allFonts; FTTypefaceList::getInstance()->getSansSerifNames (allFonts); - const char* targets[] = { "Verdana", "Bitstream Vera Sans", "Luxi Sans", "Sans", 0 }; + static const char* targets[] = { "Verdana", "Bitstream Vera Sans", "Luxi Sans", + "Liberation Sans", "DejaVu Sans", "Sans", nullptr }; return pickBestFont (allFonts, targets); } @@ -512,7 +152,8 @@ private: StringArray allFonts; FTTypefaceList::getInstance()->getSerifNames (allFonts); - const char* targets[] = { "Bitstream Vera Serif", "Times", "Nimbus Roman", "Serif", 0 }; + static const char* targets[] = { "Bitstream Vera Serif", "Times", "Nimbus Roman", + "Liberation Serif", "DejaVu Serif", "Serif", nullptr }; return pickBestFont (allFonts, targets); } @@ -521,29 +162,19 @@ private: StringArray allFonts; FTTypefaceList::getInstance()->getMonospacedNames (allFonts); - const char* targets[] = { "Bitstream Vera Sans Mono", "Courier", "Sans Mono", "Mono", 0 }; + static const char* targets[] = { "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Sans Mono", + "Liberation Mono", "Courier", "DejaVu Mono", "Mono", nullptr }; return pickBestFont (allFonts, targets); } - JUCE_DECLARE_NON_COPYABLE (DefaultFontNames); + JUCE_DECLARE_NON_COPYABLE (DefaultFontNames) }; Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; - String faceName (font.getTypefaceName()); - - if (faceName == getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; - else if (faceName == getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; - else if (faceName == getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; - Font f (font); - f.setTypefaceName (faceName); + f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName())); return Typeface::createSystemTypefaceFor (f); } - -bool TextLayout::createNativeLayout (const AttributedString&) -{ - return false; -} diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h index f701ea8..bf26ce8 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.h @@ -1,82 +1,81 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MAC_COREGRAPHICSCONTEXT_JUCEHEADER__ -#define __JUCE_MAC_COREGRAPHICSCONTEXT_JUCEHEADER__ +#ifndef JUCE_MAC_COREGRAPHICSCONTEXT_H_INCLUDED +#define JUCE_MAC_COREGRAPHICSCONTEXT_H_INCLUDED //============================================================================== class CoreGraphicsContext : public LowLevelGraphicsContext { public: - CoreGraphicsContext (CGContextRef context_, const float flipHeight_); + CoreGraphicsContext (CGContextRef context, const float flipHeight, const float targetScale); ~CoreGraphicsContext(); //============================================================================== - bool isVectorDevice() const { return false; } + bool isVectorDevice() const override { return false; } - void setOrigin (int x, int y); - void addTransform (const AffineTransform& transform); - float getScaleFactor(); - bool clipToRectangle (const Rectangle& r); - bool clipToRectangleList (const RectangleList& clipRegion); - void excludeClipRectangle (const Rectangle& r); - void clipToPath (const Path& path, const AffineTransform& transform); - void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform); - bool clipRegionIntersects (const Rectangle& r); - Rectangle getClipBounds() const; - bool isClipEmpty() const; + void setOrigin (Point) override; + void addTransform (const AffineTransform&) override; + float getPhysicalPixelScaleFactor() override; + bool clipToRectangle (const Rectangle&) override; + bool clipToRectangleList (const RectangleList&) override; + void excludeClipRectangle (const Rectangle&) override; + void clipToPath (const Path&, const AffineTransform&) override; + void clipToImageAlpha (const Image&, const AffineTransform&) override; + bool clipRegionIntersects (const Rectangle&) override; + Rectangle getClipBounds() const override; + bool isClipEmpty() const override; //============================================================================== - void saveState(); - void restoreState(); - void beginTransparencyLayer (float opacity); - void endTransparencyLayer(); + void saveState() override; + void restoreState() override; + void beginTransparencyLayer (float opacity) override; + void endTransparencyLayer() override; //============================================================================== - void setFill (const FillType& fillType); - void setOpacity (float newOpacity); - void setInterpolationQuality (Graphics::ResamplingQuality quality); + void setFill (const FillType&) override; + void setOpacity (float) override; + void setInterpolationQuality (Graphics::ResamplingQuality) override; //============================================================================== - void fillRect (const Rectangle& r, const bool replaceExistingContents); - void fillCGRect (const CGRect& cgRect, const bool replaceExistingContents); - void fillPath (const Path& path, const AffineTransform& transform); - void drawImage (const Image& sourceImage, const AffineTransform& transform); + void fillRect (const Rectangle&, bool replaceExistingContents) override; + void fillRect (const Rectangle&) override; + void fillRectList (const RectangleList&) override; + void fillPath (const Path&, const AffineTransform&) override; + void drawImage (const Image& sourceImage, const AffineTransform&) override; //============================================================================== - void drawLine (const Line& line); - void drawVerticalLine (const int x, float top, float bottom); - void drawHorizontalLine (const int y, float left, float right); - void setFont (const Font& newFont); - const Font& getFont(); - void drawGlyph (int glyphNumber, const AffineTransform& transform); - bool drawTextLayout (const AttributedString& text, const Rectangle&); + void drawLine (const Line&) override; + void setFont (const Font&) override; + const Font& getFont() override; + void drawGlyph (int glyphNumber, const AffineTransform&) override; + bool drawTextLayout (const AttributedString&, const Rectangle&) override; private: CGContextRef context; const CGFloat flipHeight; + float targetScale; CGColorSpaceRef rgbColourSpace, greyColourSpace; CGFunctionCallbacks gradientCallbacks; mutable Rectangle lastClipRect; @@ -85,7 +84,7 @@ private: struct SavedState { SavedState(); - SavedState (const SavedState& other); + SavedState (const SavedState&); ~SavedState(); void setFill (const FillType& newFill); @@ -108,13 +107,15 @@ private: OwnedArray stateStack; void drawGradient(); - void createPath (const Path& path) const; - void createPath (const Path& path, const AffineTransform& transform) const; + void createPath (const Path&) const; + void createPath (const Path&, const AffineTransform&) const; void flip() const; - void applyTransform (const AffineTransform& transform) const; - void drawImage (const Image& sourceImage, const AffineTransform& transform, bool fillEntireClipAsTiles); + void applyTransform (const AffineTransform&) const; + void drawImage (const Image&, const AffineTransform&, bool fillEntireClipAsTiles); + bool clipToRectangleListWithoutTest (const RectangleList&); + void fillCGRect (const CGRect&, bool replaceExistingContents); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsContext); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsContext) }; -#endif // __JUCE_MAC_COREGRAPHICSCONTEXT_JUCEHEADER__ +#endif // JUCE_MAC_COREGRAPHICSCONTEXT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm index 454d13c..44e344f 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsContext.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -29,18 +28,18 @@ class CoreGraphicsImage : public ImagePixelData { public: - CoreGraphicsImage (const Image::PixelFormat format, const int width_, const int height_, const bool clearImage) - : ImagePixelData (format, width_, height_) + CoreGraphicsImage (const Image::PixelFormat format, const int w, const int h, const bool clearImage) + : ImagePixelData (format, w, h), cachedImageRef (0) { pixelStride = format == Image::RGB ? 3 : ((format == Image::ARGB) ? 4 : 1); lineStride = (pixelStride * jmax (1, width) + 3) & ~3; - imageData.allocate (lineStride * jmax (1, height), clearImage); + imageData.allocate ((size_t) (lineStride * jmax (1, height)), clearImage); CGColorSpaceRef colourSpace = (format == Image::SingleChannel) ? CGColorSpaceCreateDeviceGray() : CGColorSpaceCreateDeviceRGB(); - context = CGBitmapContextCreate (imageData, width, height, 8, lineStride, + context = CGBitmapContextCreate (imageData, (size_t) width, (size_t) height, 8, (size_t) lineStride, colourSpace, getCGImageFlags (format)); CGColorSpaceRelease (colourSpace); @@ -48,34 +47,63 @@ public: ~CoreGraphicsImage() { + freeCachedImageRef(); CGContextRelease (context); } - LowLevelGraphicsContext* createLowLevelContext() + LowLevelGraphicsContext* createLowLevelContext() override { - return new CoreGraphicsContext (context, height); + freeCachedImageRef(); + sendDataChangeMessage(); + return new CoreGraphicsContext (context, height, 1.0f); } - void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode) + void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override { bitmap.data = imageData + x * pixelStride + y * lineStride; bitmap.pixelFormat = pixelFormat; bitmap.lineStride = lineStride; bitmap.pixelStride = pixelStride; + + if (mode != Image::BitmapData::readOnly) + { + freeCachedImageRef(); + sendDataChangeMessage(); + } } - ImagePixelData* clone() + ImagePixelData* clone() override { CoreGraphicsImage* im = new CoreGraphicsImage (pixelFormat, width, height, false); - memcpy (im->imageData, imageData, lineStride * height); + memcpy (im->imageData, imageData, (size_t) (lineStride * height)); return im; } - ImageType* createType() const { return new NativeImageType(); } + ImageType* createType() const override { return new NativeImageType(); } //============================================================================== - static CGImageRef createImage (const Image& juceImage, const bool forAlpha, - CGColorSpaceRef colourSpace, const bool mustOutliveSource) + static CGImageRef getCachedImageRef (const Image& juceImage, CGColorSpaceRef colourSpace) + { + CoreGraphicsImage* const cgim = dynamic_cast (juceImage.getPixelData()); + + if (cgim != nullptr && cgim->cachedImageRef != 0) + { + CGImageRetain (cgim->cachedImageRef); + return cgim->cachedImageRef; + } + + CGImageRef ref = createImage (juceImage, colourSpace, false); + + if (cgim != nullptr) + { + CGImageRetain (ref); + cgim->cachedImageRef = ref; + } + + return ref; + } + + static CGImageRef createImage (const Image& juceImage, CGColorSpaceRef colourSpace, const bool mustOutliveSource) { const Image::BitmapData srcData (juceImage, Image::BitmapData::readOnly); CGDataProviderRef provider; @@ -88,11 +116,13 @@ public: } else { - provider = CGDataProviderCreateWithData (0, srcData.data, srcData.lineStride * srcData.height, 0); + provider = CGDataProviderCreateWithData (0, srcData.data, (size_t) (srcData.lineStride * srcData.height), 0); } - CGImageRef imageRef = CGImageCreate (srcData.width, srcData.height, - 8, srcData.pixelStride * 8, srcData.lineStride, + CGImageRef imageRef = CGImageCreate ((size_t) srcData.width, + (size_t) srcData.height, + 8, (size_t) srcData.pixelStride * 8, + (size_t) srcData.lineStride, colourSpace, getCGImageFlags (juceImage.getFormat()), provider, 0, true, kCGRenderingIntentDefault); @@ -102,10 +132,20 @@ public: //============================================================================== CGContextRef context; + CGImageRef cachedImageRef; HeapBlock imageData; int pixelStride, lineStride; private: + void freeCachedImageRef() + { + if (cachedImageRef != 0) + { + CGImageRelease (cachedImageRef); + cachedImageRef = 0; + } + } + static CGBitmapInfo getCGImageFlags (const Image::PixelFormat& format) { #if JUCE_BIG_ENDIAN @@ -115,18 +155,19 @@ private: #endif } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsImage); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreGraphicsImage) }; -ImagePixelData* NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const +ImagePixelData::Ptr NativeImageType::create (Image::PixelFormat format, int width, int height, bool clearImage) const { return new CoreGraphicsImage (format == Image::RGB ? Image::ARGB : format, width, height, clearImage); } //============================================================================== -CoreGraphicsContext::CoreGraphicsContext (CGContextRef context_, const float flipHeight_) - : context (context_), - flipHeight (flipHeight_), +CoreGraphicsContext::CoreGraphicsContext (CGContextRef c, const float h, const float scale) + : context (c), + flipHeight (h), + targetScale (scale), lastClipRectIsValid (false), state (new SavedState()) { @@ -152,27 +193,33 @@ CoreGraphicsContext::~CoreGraphicsContext() } //============================================================================== -void CoreGraphicsContext::setOrigin (int x, int y) +void CoreGraphicsContext::setOrigin (Point o) { - CGContextTranslateCTM (context, x, -y); + CGContextTranslateCTM (context, o.x, -o.y); if (lastClipRectIsValid) - lastClipRect.translate (-x, -y); + lastClipRect.translate (-o.x, -o.y); } void CoreGraphicsContext::addTransform (const AffineTransform& transform) { - applyTransform (AffineTransform::verticalFlip (flipHeight) + applyTransform (AffineTransform::verticalFlip ((float) flipHeight) .followedBy (transform) - .translated (0, -flipHeight) + .translated (0, (float) -flipHeight) .scaled (1.0f, -1.0f)); lastClipRectIsValid = false; + + jassert (getPhysicalPixelScaleFactor() > 0.0f); + jassert (getPhysicalPixelScaleFactor() > 0.0f); } -float CoreGraphicsContext::getScaleFactor() +float CoreGraphicsContext::getPhysicalPixelScaleFactor() { - CGAffineTransform t = CGContextGetCTM (context); - return (float) juce_hypot (t.a + t.c, t.b + t.d); + const CGAffineTransform t = CGContextGetCTM (context); + + return targetScale * (float) (juce_hypot (t.a, t.c) + juce_hypot (t.b, t.d)) / 2.0f; + +// return targetScale * (float) (t.a + t.d) / 2.0f; } bool CoreGraphicsContext::clipToRectangle (const Rectangle& r) @@ -192,38 +239,40 @@ bool CoreGraphicsContext::clipToRectangle (const Rectangle& r) return ! isClipEmpty(); } -bool CoreGraphicsContext::clipToRectangleList (const RectangleList& clipRegion) +bool CoreGraphicsContext::clipToRectangleListWithoutTest (const RectangleList& clipRegion) { if (clipRegion.isEmpty()) { - CGContextClipToRect (context, CGRectMake (0, 0, 0, 0)); + CGContextClipToRect (context, CGRectZero); lastClipRectIsValid = true; lastClipRect = Rectangle(); return false; } else { - const int numRects = clipRegion.getNumRectangles(); - + const size_t numRects = (size_t) clipRegion.getNumRectangles(); HeapBlock rects (numRects); - for (int i = 0; i < numRects; ++i) - { - const Rectangle& r = clipRegion.getRectangle(i); - rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()); - } + + int i = 0; + for (const Rectangle* r = clipRegion.begin(), * const e = clipRegion.end(); r != e; ++r) + rects[i++] = CGRectMake (r->getX(), flipHeight - r->getBottom(), r->getWidth(), r->getHeight()); CGContextClipToRects (context, rects, numRects); lastClipRectIsValid = false; - return ! isClipEmpty(); + return true; } } +bool CoreGraphicsContext::clipToRectangleList (const RectangleList& clipRegion) +{ + return clipToRectangleListWithoutTest (clipRegion) && ! isClipEmpty(); +} + void CoreGraphicsContext::excludeClipRectangle (const Rectangle& r) { - RectangleList remaining (getClipBounds()); + RectangleList remaining (getClipBounds()); remaining.subtract (r); - clipToRectangleList (remaining); - lastClipRectIsValid = false; + clipToRectangleListWithoutTest (remaining); } void CoreGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform) @@ -242,13 +291,13 @@ void CoreGraphicsContext::clipToImageAlpha (const Image& sourceImage, const Affi if (sourceImage.getFormat() != Image::SingleChannel) singleChannelImage = sourceImage.convertedToFormat (Image::SingleChannel); - CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, true, greyColourSpace, true); + CGImageRef image = CoreGraphicsImage::createImage (singleChannelImage, greyColourSpace, true); flip(); AffineTransform t (AffineTransform::verticalFlip (sourceImage.getHeight()).followedBy (transform)); applyTransform (t); - CGRect r = CGRectMake (0, 0, sourceImage.getWidth(), sourceImage.getHeight()); + CGRect r = convertToCGRect (sourceImage.getBounds()); CGContextClipToMask (context, r, image); applyTransform (t.inverted()); @@ -296,9 +345,7 @@ void CoreGraphicsContext::restoreState() { CGContextRestoreGState (context); - SavedState* const top = stateStack.getLast(); - - if (top != nullptr) + if (SavedState* const top = stateStack.getLast()) { state = top; stateStack.removeLast (1, false); @@ -355,6 +402,11 @@ void CoreGraphicsContext::fillRect (const Rectangle& r, const bool replaceE fillCGRect (CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()), replaceExistingContents); } +void CoreGraphicsContext::fillRect (const Rectangle& r) +{ + fillCGRect (CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()), false); +} + void CoreGraphicsContext::fillCGRect (const CGRect& cgRect, const bool replaceExistingContents) { if (replaceExistingContents) @@ -438,7 +490,7 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans { const int iw = sourceImage.getWidth(); const int ih = sourceImage.getHeight(); - CGImageRef image = CoreGraphicsImage::createImage (sourceImage, false, rgbColourSpace, false); + CGImageRef image = CoreGraphicsImage::getCachedImageRef (sourceImage, rgbColourSpace); CGContextSaveGState (context); CGContextSetAlpha (context, state->fillType.getOpacity()); @@ -485,7 +537,7 @@ void CoreGraphicsContext::drawImage (const Image& sourceImage, const AffineTrans CGContextDrawImage (context, imageRect, image); } - CGImageRelease (image); // (This causes a memory bug in iPhone sim 3.0 - try upgrading to a later version if you hit this) + CGImageRelease (image); // (This causes a memory bug in iOS sim 3.0 - try upgrading to a later version if you hit this) CGContextRestoreGState (context); } @@ -513,39 +565,31 @@ void CoreGraphicsContext::drawLine (const Line& line) } } -void CoreGraphicsContext::drawVerticalLine (const int x, float top, float bottom) +void CoreGraphicsContext::fillRectList (const RectangleList& list) { - if (state->fillType.isColour()) - { - #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 - CGContextFillRect (context, CGRectMake (x, flipHeight - bottom, 1.0f, bottom - top)); - #else - // On Leopard, unless both co-ordinates are non-integer, it disables anti-aliasing, so nudge - // the x co-ord slightly to trick it.. - CGContextFillRect (context, CGRectMake (x + 1.0f / 256.0f, flipHeight - bottom, 1.0f + 1.0f / 256.0f, bottom - top)); - #endif - } - else - { - fillCGRect (CGRectMake ((float) x, flipHeight - bottom, 1.0f, bottom - top), false); - } -} + HeapBlock rects ((size_t) list.getNumRectangles()); + + size_t num = 0; + for (const Rectangle* r = list.begin(), * const e = list.end(); r != e; ++r) + rects[num++] = CGRectMake (r->getX(), flipHeight - r->getBottom(), r->getWidth(), r->getHeight()); -void CoreGraphicsContext::drawHorizontalLine (const int y, float left, float right) -{ if (state->fillType.isColour()) { - #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 - CGContextFillRect (context, CGRectMake (left, flipHeight - (y + 1.0f), right - left, 1.0f)); - #else - // On Leopard, unless both co-ordinates are non-integer, it disables anti-aliasing, so nudge - // the x co-ord slightly to trick it.. - CGContextFillRect (context, CGRectMake (left, flipHeight - (y + (1.0f + 1.0f / 256.0f)), right - left, 1.0f + 1.0f / 256.0f)); - #endif + CGContextFillRects (context, rects, num); + } + else if (state->fillType.isGradient()) + { + CGContextSaveGState (context); + CGContextClipToRects (context, rects, num); + drawGradient(); + CGContextRestoreGState (context); } else { - fillCGRect (CGRectMake (left, flipHeight - (y + 1), right - left, 1.0f), false); + CGContextSaveGState (context); + CGContextClipToRects (context, rects, num); + drawImage (state->fillType.image, state->fillType.transform, true); + CGContextRestoreGState (context); } } @@ -556,13 +600,11 @@ void CoreGraphicsContext::setFont (const Font& newFont) state->fontRef = 0; state->font = newFont; - OSXTypeface* osxTypeface = dynamic_cast (state->font.getTypeface()); - - if (osxTypeface != nullptr) + if (OSXTypeface* osxTypeface = dynamic_cast (state->font.getTypeface())) { state->fontRef = osxTypeface->fontRef; CGContextSetFont (context, state->fontRef); - CGContextSetFontSize (context, state->font.getHeight() * osxTypeface->fontHeightToCGSizeFactor); + CGContextSetFontSize (context, state->font.getHeight() * osxTypeface->fontHeightToPointsFactor); state->fontTransform = osxTypeface->renderingTransform; state->fontTransform.a *= state->font.getHorizontalScale(); @@ -580,11 +622,16 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra { if (state->fontRef != 0 && state->fillType.isColour()) { + #if JUCE_CLANG + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + #endif + if (transform.isOnlyTranslation()) { CGContextSetTextMatrix (context, state->fontTransform); // have to set this each time, as it's not saved as part of the state - CGGlyph g = glyphNumber; + CGGlyph g = (CGGlyph) glyphNumber; CGContextShowGlyphsAtPoint (context, transform.getTranslationX(), flipHeight - roundToInt (transform.getTranslationY()), &g, 1); } @@ -598,11 +645,15 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra t.d = -t.d; CGContextSetTextMatrix (context, t); - CGGlyph g = glyphNumber; + CGGlyph g = (CGGlyph) glyphNumber; CGContextShowGlyphsAtPoint (context, 0, 0, &g, 1); CGContextRestoreGState (context); } + + #if JUCE_CLANG + #pragma clang diagnostic pop + #endif } else { @@ -618,9 +669,10 @@ void CoreGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& tra bool CoreGraphicsContext::drawTextLayout (const AttributedString& text, const Rectangle& area) { #if JUCE_CORETEXT_AVAILABLE - CoreTextTypeLayout::drawToCGContext (text, area, context, flipHeight); + CoreTextTypeLayout::drawToCGContext (text, area, context, (float) flipHeight); return true; #else + (void) text; (void) area; return false; #endif } @@ -634,10 +686,10 @@ CoreGraphicsContext::SavedState::SavedState() CoreGraphicsContext::SavedState::SavedState (const SavedState& other) : fillType (other.fillType), font (other.font), fontRef (other.fontRef), fontTransform (other.fontTransform), shading (0), - gradientLookupTable (other.numGradientLookupEntries), + gradientLookupTable ((size_t) other.numGradientLookupEntries), numGradientLookupEntries (other.numGradientLookupEntries) { - memcpy (gradientLookupTable, other.gradientLookupTable, sizeof (PixelARGB) * numGradientLookupEntries); + memcpy (gradientLookupTable, other.gradientLookupTable, sizeof (PixelARGB) * (size_t) numGradientLookupEntries); } CoreGraphicsContext::SavedState::~SavedState() @@ -665,7 +717,7 @@ CGShadingRef CoreGraphicsContext::SavedState::getShading (CoreGraphicsContext& o numGradientLookupEntries = g.createLookupTable (fillType.transform, gradientLookupTable) - 1; CGFunctionRef function = CGFunctionCreate (this, 1, 0, 4, 0, &(owner.gradientCallbacks)); - CGPoint p1 (CGPointMake (g.point1.x, g.point1.y)); + CGPoint p1 (convertToCGPoint (g.point1)); if (g.isRadial) { @@ -676,7 +728,7 @@ CGShadingRef CoreGraphicsContext::SavedState::getShading (CoreGraphicsContext& o else { shading = CGShadingCreateAxial (owner.rgbColourSpace, p1, - CGPointMake (g.point2.x, g.point2.y), + convertToCGPoint (g.point2), function, true, true); } @@ -788,53 +840,55 @@ Image juce_loadWithCoreImage (InputStream& input) MemoryBlock data; input.readIntoMemoryBlock (data, -1); - #if JUCE_IOS + #if JUCE_IOS JUCE_AUTORELEASEPOOL - UIImage* uiImage = [UIImage imageWithData: [NSData dataWithBytesNoCopy: data.getData() - length: data.getSize() - freeWhenDone: NO]]; - - if (uiImage != nil) + #endif { - CGImageRef loadedImage = uiImage.CGImage; - - #else - CGDataProviderRef provider = CGDataProviderCreateWithData (0, data.getData(), data.getSize(), 0); - CGImageSourceRef imageSource = CGImageSourceCreateWithDataProvider (provider, 0); - CGDataProviderRelease (provider); - - if (imageSource != 0) - { - CGImageRef loadedImage = CGImageSourceCreateImageAtIndex (imageSource, 0, 0); - CFRelease (imageSource); - #endif - - if (loadedImage != 0) + #if JUCE_IOS + if (UIImage* uiImage = [UIImage imageWithData: [NSData dataWithBytesNoCopy: data.getData() + length: data.getSize() + freeWhenDone: NO]]) { - CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo (loadedImage); - const bool hasAlphaChan = (alphaInfo != kCGImageAlphaNone - && alphaInfo != kCGImageAlphaNoneSkipLast - && alphaInfo != kCGImageAlphaNoneSkipFirst); + CGImageRef loadedImage = uiImage.CGImage; - Image image (NativeImageType().create (Image::ARGB, // (CoreImage doesn't work with 24-bit images) - (int) CGImageGetWidth (loadedImage), - (int) CGImageGetHeight (loadedImage), - hasAlphaChan)); + #else + CGDataProviderRef provider = CGDataProviderCreateWithData (0, data.getData(), data.getSize(), 0); + CGImageSourceRef imageSource = CGImageSourceCreateWithDataProvider (provider, 0); + CGDataProviderRelease (provider); - CoreGraphicsImage* const cgImage = dynamic_cast (image.getPixelData()); - jassert (cgImage != nullptr); // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsImage class should have been used. + if (imageSource != 0) + { + CGImageRef loadedImage = CGImageSourceCreateImageAtIndex (imageSource, 0, 0); + CFRelease (imageSource); + #endif - CGContextDrawImage (cgImage->context, CGRectMake (0, 0, image.getWidth(), image.getHeight()), loadedImage); - CGContextFlush (cgImage->context); + if (loadedImage != 0) + { + CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo (loadedImage); + const bool hasAlphaChan = (alphaInfo != kCGImageAlphaNone + && alphaInfo != kCGImageAlphaNoneSkipLast + && alphaInfo != kCGImageAlphaNoneSkipFirst); - #if ! JUCE_IOS - CFRelease (loadedImage); - #endif + Image image (NativeImageType().create (Image::ARGB, // (CoreImage doesn't work with 24-bit images) + (int) CGImageGetWidth (loadedImage), + (int) CGImageGetHeight (loadedImage), + hasAlphaChan)); - // Because it's impossible to create a truly 24-bit CG image, this flag allows a user - // to find out whether the file they just loaded the image from had an alpha channel or not. - image.getProperties()->set ("originalImageHadAlpha", hasAlphaChan); - return image; + CoreGraphicsImage* const cgImage = dynamic_cast (image.getPixelData()); + jassert (cgImage != nullptr); // if USE_COREGRAPHICS_RENDERING is set, the CoreGraphicsImage class should have been used. + + CGContextDrawImage (cgImage->context, convertToCGRect (image.getBounds()), loadedImage); + CGContextFlush (cgImage->context); + + #if ! JUCE_IOS + CFRelease (loadedImage); + #endif + + // Because it's impossible to create a truly 24-bit CG image, this flag allows a user + // to find out whether the file they just loaded the image from had an alpha channel or not. + image.getProperties()->set ("originalImageHadAlpha", hasAlphaChan); + return image; + } } } @@ -855,17 +909,19 @@ Image juce_createImageFromCIImage (CIImage* im, int w, int h) return Image (cgImage); } -CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, const bool forAlpha, - CGColorSpaceRef colourSpace, const bool mustOutliveSource) +CGImageRef juce_createCoreGraphicsImage (const Image& juceImage, CGColorSpaceRef colourSpace, + const bool mustOutliveSource) { - return CoreGraphicsImage::createImage (juceImage, forAlpha, colourSpace, mustOutliveSource); + return CoreGraphicsImage::createImage (juceImage, colourSpace, mustOutliveSource); } CGContextRef juce_getImageContext (const Image& image) { - CoreGraphicsImage* const cgi = dynamic_cast (image.getPixelData()); - jassert (cgi != nullptr); - return cgi != nullptr ? cgi->context : 0; + if (CoreGraphicsImage* const cgi = dynamic_cast (image.getPixelData())) + return cgi->context; + + jassertfalse; + return 0; } #endif diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h index 290d62f..ca6f699 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_CoreGraphicsHelpers.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MAC_COREGRAPHICSHELPERS_JUCEHEADER__ -#define __JUCE_MAC_COREGRAPHICSHELPERS_JUCEHEADER__ +#ifndef JUCE_MAC_COREGRAPHICSHELPERS_H_INCLUDED +#define JUCE_MAC_COREGRAPHICSHELPERS_H_INCLUDED //============================================================================== @@ -47,10 +46,15 @@ namespace { return CGRectMake ((CGFloat) r.getX(), (CGFloat) r.getY(), (CGFloat) r.getWidth(), (CGFloat) r.getHeight()); } + + template + CGPoint convertToCGPoint (const PointType& p) noexcept + { + return CGPointMake ((CGFloat) p.x, (CGFloat) p.y); + } } -extern CGImageRef juce_createCoreGraphicsImage (const Image&, const bool forAlpha, CGColorSpaceRef, const bool mustOutliveSource); - +extern CGImageRef juce_createCoreGraphicsImage (const Image&, CGColorSpaceRef, bool mustOutliveSource); extern CGContextRef juce_getImageContext (const Image&); -#endif // __JUCE_MAC_COREGRAPHICSHELPERS_JUCEHEADER__ +#endif // JUCE_MAC_COREGRAPHICSHELPERS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm index 5fe704c..81a29a8 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -1,87 +1,139 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if (JUCE_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 \ - && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5) \ - || (JUCE_IOS && defined (__IPHONE_3_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2) +#if (! defined (JUCE_CORETEXT_AVAILABLE)) \ + && (JUCE_IOS || (JUCE_MAC && MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4)) #define JUCE_CORETEXT_AVAILABLE 1 #endif +const float referenceFontSize = 1024.0f; + #if JUCE_CORETEXT_AVAILABLE +#if JUCE_MAC && MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5 +extern "C" +{ + void CTRunGetAdvances (CTRunRef, CFRange, CGSize buffer[]); + const CGSize* CTRunGetAdvancesPtr (CTRunRef); +} +#endif + +static CTFontRef getCTFontFromTypeface (const Font& f); + namespace CoreTextTypeLayout { - static CTFontRef createCTFont (const Font& font, const float fontSize, - const bool applyScaleFactor, bool& needsItalicTransform) + static String findBestAvailableStyle (const Font& font, CGAffineTransform& requiredTransform) { - CFStringRef cfName = font.getTypefaceName().toCFString(); - CTFontRef ctFontRef = CTFontCreateWithName (cfName, fontSize, nullptr); - CFRelease (cfName); + const StringArray availableStyles (Font::findAllTypefaceStyles (font.getTypefaceName())); + const String style (font.getTypefaceStyle()); - if (ctFontRef != nullptr) + if (! availableStyles.contains (style)) { - if (font.isItalic()) - { - CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr, - kCTFontItalicTrait, kCTFontItalicTrait); + if (font.isItalic()) // Fake-up an italic font if there isn't a real one. + requiredTransform = CGAffineTransformMake (1.0f, 0, 0.25f, 1.0f, 0, 0); - if (newFont != nullptr) - { - CFRelease (ctFontRef); - ctFontRef = newFont; - } - else - { - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - } - } - - if (font.isBold()) - { - CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr, - kCTFontBoldTrait, kCTFontBoldTrait); - if (newFont != nullptr) - { - CFRelease (ctFontRef); - ctFontRef = newFont; - } - } - - if (applyScaleFactor) - { - CGFontRef cgFontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); - const int totalHeight = std::abs (CGFontGetAscent (cgFontRef)) + std::abs (CGFontGetDescent (cgFontRef)); - const float factor = CGFontGetUnitsPerEm (cgFontRef) / (float) totalHeight; - CGFontRelease (cgFontRef); - - CTFontRef newFont = CTFontCreateCopyWithAttributes (ctFontRef, fontSize * factor, nullptr, nullptr); - CFRelease (ctFontRef); - ctFontRef = newFont; - } + return availableStyles[0]; } + return style; + } + + // Workaround for Apple bug in CTFontCreateWithFontDescriptor in Garageband/Logic on 10.6 + #if JUCE_MAC && ((! defined (MAC_OS_X_VERSION_10_7)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) + static CTFontRef getFontWithTrait (CTFontRef ctFontRef, CTFontSymbolicTraits trait) + { + if (CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr, trait, trait)) + { + CFRelease (ctFontRef); + return newFont; + } + + return ctFontRef; + } + + static CTFontRef useStyleFallbackIfNecessary (CTFontRef ctFontRef, CFStringRef cfFontFamily, + const float fontSizePoints, const Font& font) + { + CFStringRef cfActualFontFamily = (CFStringRef) CTFontCopyAttribute (ctFontRef, kCTFontFamilyNameAttribute); + + if (CFStringCompare (cfFontFamily, cfActualFontFamily, 0) != kCFCompareEqualTo) + { + CFRelease (ctFontRef); + ctFontRef = CTFontCreateWithName (cfFontFamily, fontSizePoints, nullptr); + + if (font.isItalic()) ctFontRef = getFontWithTrait (ctFontRef, kCTFontItalicTrait); + if (font.isBold()) ctFontRef = getFontWithTrait (ctFontRef, kCTFontBoldTrait); + } + + CFRelease (cfActualFontFamily); + return ctFontRef; + } + #endif + + static float getFontTotalHeight (CTFontRef font) + { + return std::abs ((float) CTFontGetAscent (font)) + std::abs ((float) CTFontGetDescent (font)); + } + + static float getHeightToPointsFactor (CTFontRef font) + { + return referenceFontSize / getFontTotalHeight (font); + } + + static CTFontRef getFontWithPointSize (CTFontRef font, float size) + { + CTFontRef newFont = CTFontCreateCopyWithAttributes (font, size, nullptr, nullptr); + CFRelease (font); + return newFont; + } + + static CTFontRef createCTFont (const Font& font, const float fontSizePoints, CGAffineTransform& transformRequired) + { + CFStringRef cfFontFamily = FontStyleHelpers::getConcreteFamilyName (font).toCFString(); + CFStringRef cfFontStyle = findBestAvailableStyle (font, transformRequired).toCFString(); + CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute }; + CFTypeRef values[] = { cfFontFamily, cfFontStyle }; + + CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, + (const void**) &values, + numElementsInArray (keys), + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease (cfFontStyle); + + CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes); + CFRelease (fontDescAttributes); + + CTFontRef ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, fontSizePoints, nullptr); + CFRelease (ctFontDescRef); + + #if JUCE_MAC && ((! defined (MAC_OS_X_VERSION_10_7)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7) + ctFontRef = useStyleFallbackIfNecessary (ctFontRef, cfFontFamily, fontSizePoints, font); + #endif + + CFRelease (cfFontFamily); + return ctFontRef; } @@ -93,7 +145,7 @@ namespace CoreTextTypeLayout { if (advances == nullptr) { - local.malloc (numGlyphs); + local.malloc ((size_t) numGlyphs); CTRunGetAdvances (run, CFRangeMake (0, 0), local); advances = local; } @@ -105,7 +157,7 @@ namespace CoreTextTypeLayout struct Glyphs { - Glyphs (CTRunRef run, const int numGlyphs) + Glyphs (CTRunRef run, const size_t numGlyphs) : glyphs (CTRunGetGlyphsPtr (run)) { if (glyphs == nullptr) @@ -122,7 +174,7 @@ namespace CoreTextTypeLayout struct Positions { - Positions (CTRunRef run, const int numGlyphs) + Positions (CTRunRef run, const size_t numGlyphs) : points (CTRunGetPositionsPtr (run)) { if (points == nullptr) @@ -137,6 +189,30 @@ namespace CoreTextTypeLayout HeapBlock local; }; + struct LineInfo + { + LineInfo (CTFrameRef frame, CTLineRef line, CFIndex lineIndex) + { + CTFrameGetLineOrigins (frame, CFRangeMake (lineIndex, 1), &origin); + CTLineGetTypographicBounds (line, &ascent, &descent, &leading); + } + + CGPoint origin; + CGFloat ascent, descent, leading; + }; + + static CTFontRef getOrCreateFont (const Font& f) + { + if (CTFontRef ctf = getCTFontFromTypeface (f)) + { + CFRetain (ctf); + return ctf; + } + + CGAffineTransform transform; + return createCTFont (f, referenceFontSize, transform); + } + //============================================================================== static CFAttributedStringRef createCFAttributedString (const AttributedString& text) { @@ -153,38 +229,39 @@ namespace CoreTextTypeLayout for (int i = 0; i < numCharacterAttributes; ++i) { - const AttributedString::Attribute* const attr = text.getAttribute (i); + const AttributedString::Attribute& attr = *text.getAttribute (i); - if (attr->range.getStart() > CFAttributedStringGetLength (attribString)) + if (attr.range.getStart() > CFAttributedStringGetLength (attribString)) continue; - Range range (attr->range); + Range range (attr.range); range.setEnd (jmin (range.getEnd(), (int) CFAttributedStringGetLength (attribString))); - if (attr->getFont() != nullptr) + if (const Font* const f = attr.getFont()) { - const Font& f = *attr->getFont(); - bool needsItalicTransform = false; - CTFontRef ctFontRef = createCTFont (f, f.getHeight(), true, needsItalicTransform); + if (CTFontRef ctFontRef = getOrCreateFont (*f)) + { + ctFontRef = getFontWithPointSize (ctFontRef, f->getHeight() * getHeightToPointsFactor (ctFontRef)); - CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), - kCTFontAttributeName, ctFontRef); - CFRelease (ctFontRef); + CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()), + kCTFontAttributeName, ctFontRef); + CFRelease (ctFontRef); + } } - if (attr->getColour() != nullptr) + if (const Colour* const col = attr.getColour()) { #if JUCE_IOS - const CGFloat components[] = { attr->getColour()->getFloatRed(), - attr->getColour()->getFloatGreen(), - attr->getColour()->getFloatBlue(), - attr->getColour()->getFloatAlpha() }; + const CGFloat components[] = { col->getFloatRed(), + col->getFloatGreen(), + col->getFloatBlue(), + col->getFloatAlpha() }; CGColorRef colour = CGColorCreate (rgbColourSpace, components); #else - CGColorRef colour = CGColorCreateGenericRGB (attr->getColour()->getFloatRed(), - attr->getColour()->getFloatGreen(), - attr->getColour()->getFloatBlue(), - attr->getColour()->getFloatAlpha()); + CGColorRef colour = CGColorCreateGenericRGB (col->getFloatRed(), + col->getFloatGreen(), + col->getFloatBlue(), + col->getFloatAlpha()); #endif CFAttributedStringSetAttribute (attribString, @@ -228,7 +305,7 @@ namespace CoreTextTypeLayout #endif }; - CTParagraphStyleRef ctParagraphStyleRef = CTParagraphStyleCreate (settings, numElementsInArray (settings)); + CTParagraphStyleRef ctParagraphStyleRef = CTParagraphStyleCreate (settings, (size_t) numElementsInArray (settings)); CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)), kCTParagraphStyleAttributeName, ctParagraphStyleRef); CFRelease (ctParagraphStyleRef); @@ -238,44 +315,83 @@ namespace CoreTextTypeLayout return attribString; } - static void drawToCGContext (const AttributedString& text, const Rectangle& area, - const CGContextRef& context, const float flipHeight) + static CTFrameRef createCTFrame (const AttributedString& text, CGRect bounds) { - CFAttributedStringRef attribString = CoreTextTypeLayout::createCFAttributedString (text); + CFAttributedStringRef attribString = createCFAttributedString (text); CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString (attribString); CFRelease (attribString); CGMutablePathRef path = CGPathCreateMutable(); - CGRect bounds = CGRectMake ((CGFloat) area.getX(), flipHeight - (CGFloat) area.getBottom(), - (CGFloat) area.getWidth(), (CGFloat) area.getHeight()); CGPathAddRect (path, nullptr, bounds); CTFrameRef frame = CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr); CFRelease (framesetter); CGPathRelease (path); - CTFrameDraw (frame, context); + return frame; + } + + static Range getLineVerticalRange (CTFrameRef frame, CFArrayRef lines, int lineIndex) + { + LineInfo info (frame, (CTLineRef) CFArrayGetValueAtIndex (lines, lineIndex), lineIndex); + return Range ((float) (info.origin.y - info.descent), + (float) (info.origin.y + info.ascent)); + } + + static float findCTFrameHeight (CTFrameRef frame) + { + CFArrayRef lines = CTFrameGetLines (frame); + const CFIndex numLines = CFArrayGetCount (lines); + + if (numLines == 0) + return 0; + + Range range (getLineVerticalRange (frame, lines, 0)); + + if (numLines > 1) + range = range.getUnionWith (getLineVerticalRange (frame, lines, (int) numLines - 1)); + + return range.getLength(); + } + + static void drawToCGContext (const AttributedString& text, const Rectangle& area, + const CGContextRef& context, const float flipHeight) + { + CTFrameRef frame = createCTFrame (text, CGRectMake ((CGFloat) area.getX(), flipHeight - (CGFloat) area.getBottom(), + (CGFloat) area.getWidth(), (CGFloat) area.getHeight())); + + const int verticalJustification = text.getJustification().getOnlyVerticalFlags(); + + if (verticalJustification == Justification::verticallyCentred + || verticalJustification == Justification::bottom) + { + float adjust = area.getHeight() - findCTFrameHeight (frame); + + if (verticalJustification == Justification::verticallyCentred) + adjust *= 0.5f; + + CGContextSaveGState (context); + CGContextTranslateCTM (context, 0, -adjust); + CTFrameDraw (frame, context); + CGContextRestoreGState (context); + } + else + { + CTFrameDraw (frame, context); + } + CFRelease (frame); } static void createLayout (TextLayout& glyphLayout, const AttributedString& text) { - CFAttributedStringRef attribString = CoreTextTypeLayout::createCFAttributedString (text); - CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString (attribString); - CFRelease (attribString); - - CGMutablePathRef path = CGPathCreateMutable(); - const CGRect bounds = CGRectMake (0, 0, glyphLayout.getWidth(), 1.0e6f); - CGPathAddRect (path, nullptr, bounds); - - CTFrameRef frame = CTFramesetterCreateFrame (framesetter, CFRangeMake(0, 0), path, nullptr); - CFRelease (framesetter); - CGPathRelease (path); + const CGFloat boundsHeight = 1.0e6f; + CTFrameRef frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight)); CFArrayRef lines = CTFrameGetLines (frame); const CFIndex numLines = CFArrayGetCount (lines); - glyphLayout.ensureStorageAllocated (numLines); + glyphLayout.ensureStorageAllocated ((int) numLines); for (CFIndex i = 0; i < numLines; ++i) { @@ -288,16 +404,14 @@ namespace CoreTextTypeLayout const CFIndex lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length - 1; const Range lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd); - CGPoint cgpLineOrigin; - CTFrameGetLineOrigins (frame, CFRangeMake(i, 1), &cgpLineOrigin); + LineInfo lineInfo (frame, line, i); - Point lineOrigin ((float) cgpLineOrigin.x, bounds.size.height - (float) cgpLineOrigin.y); - - CGFloat ascent, descent, leading; - CTLineGetTypographicBounds (line, &ascent, &descent, &leading); - - TextLayout::Line* const glyphLine = new TextLayout::Line (lineStringRange, lineOrigin, - (float) ascent, (float) descent, (float) leading, + TextLayout::Line* const glyphLine = new TextLayout::Line (lineStringRange, + Point ((float) lineInfo.origin.x, + (float) (boundsHeight - lineInfo.origin.y)), + (float) lineInfo.ascent, + (float) lineInfo.descent, + (float) lineInfo.leading, (int) numRuns); glyphLayout.addLine (glyphLine); @@ -318,16 +432,21 @@ namespace CoreTextTypeLayout if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void **) &ctRunFont)) { CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont); - CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, 1024, nullptr); - CGFontRef cgFontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); - CFRelease (ctFontRef); - const int totalHeight = std::abs (CGFontGetAscent (cgFontRef)) + std::abs (CGFontGetDescent (cgFontRef)); - const float fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (cgFontRef) / (float) totalHeight; - CGFontRelease (cgFontRef); - - glyphRun->font = Font (String::fromCFString (cfsFontName), - CTFontGetSize (ctRunFont) / fontHeightToCGSizeFactor, 0); // XXX bold/italic flags? + CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr); CFRelease (cfsFontName); + + const float fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef); + CFRelease (ctFontRef); + + CFStringRef cfsFontFamily = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute); + CFStringRef cfsFontStyle = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute); + + glyphRun->font = Font (String::fromCFString (cfsFontFamily), + String::fromCFString (cfsFontStyle), + (float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor)); + + CFRelease (cfsFontStyle); + CFRelease (cfsFontFamily); } CGColorRef cgRunColor; @@ -336,17 +455,20 @@ namespace CoreTextTypeLayout { const CGFloat* const components = CGColorGetComponents (cgRunColor); - glyphRun->colour = Colour::fromFloatRGBA (components[0], components[1], components[2], components[3]); + glyphRun->colour = Colour::fromFloatRGBA ((float) components[0], + (float) components[1], + (float) components[2], + (float) components[3]); } - const CoreTextTypeLayout::Glyphs glyphs (run, numGlyphs); - const CoreTextTypeLayout::Advances advances (run, numGlyphs); - const CoreTextTypeLayout::Positions positions (run, numGlyphs); + const Glyphs glyphs (run, (size_t) numGlyphs); + const Advances advances (run, numGlyphs); + const Positions positions (run, (size_t) numGlyphs); for (CFIndex k = 0; k < numGlyphs; ++k) - glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point (positions.points[k].x, - positions.points[k].y), - advances.advances[k].width)); + glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point ((float) positions.points[k].x, + (float) positions.points[k].y), + (float) advances.advances[k].width)); } } @@ -360,50 +482,89 @@ class OSXTypeface : public Typeface { public: OSXTypeface (const Font& font) - : Typeface (font.getTypefaceName()), + : Typeface (font.getTypefaceName(), + font.getTypefaceStyle()), fontRef (nullptr), - fontHeightToCGSizeFactor (1.0f), - renderingTransform (CGAffineTransformIdentity), ctFontRef (nullptr), + fontHeightToPointsFactor (1.0f), + renderingTransform (CGAffineTransformIdentity), + isMemoryFont (false), attributedStringAtts (nullptr), ascent (0.0f), unitsToHeightScaleFactor (0.0f) { - bool needsItalicTransform = false; - ctFontRef = CoreTextTypeLayout::createCTFont (font, 1024.0f, false, needsItalicTransform); + ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform); if (ctFontRef != nullptr) { - ascent = std::abs ((float) CTFontGetAscent (ctFontRef)); - const float totalSize = ascent + std::abs ((float) CTFontGetDescent (ctFontRef)); - ascent /= totalSize; - - pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); - - if (needsItalicTransform) - { - pathTransform = pathTransform.sheared (-0.15f, 0.0f); - renderingTransform.c = 0.15f; - } - fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); - - const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); - const float ctTotalHeight = abs (CTFontGetAscent (ctFontRef)) + abs (CTFontGetDescent (ctFontRef)); - unitsToHeightScaleFactor = 1.0f / ctTotalHeight; - fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; - - const short zero = 0; - CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero); - - CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName }; - CFTypeRef values[] = { ctFontRef, numberRef }; - attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease (numberRef); + initialiseMetrics(); } } + OSXTypeface (const void* data, size_t dataSize) + : Typeface (String(), String()), + fontRef (nullptr), + ctFontRef (nullptr), + fontHeightToPointsFactor (1.0f), + renderingTransform (CGAffineTransformIdentity), + isMemoryFont (true), + attributedStringAtts (nullptr), + ascent (0.0f), + unitsToHeightScaleFactor (0.0f) + { + CFDataRef cfData = CFDataCreate (kCFAllocatorDefault, (const UInt8*) data, (CFIndex) dataSize); + CGDataProviderRef provider = CGDataProviderCreateWithCFData (cfData); + CFRelease (cfData); + + fontRef = CGFontCreateWithDataProvider (provider); + CGDataProviderRelease (provider); + + if (fontRef != nullptr) + { + ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr); + + if (ctFontRef != nullptr) + { + if (CFStringRef fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey)) + { + name = String::fromCFString (fontName); + CFRelease (fontName); + } + + if (CFStringRef fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey)) + { + style = String::fromCFString (fontStyle); + CFRelease (fontStyle); + } + + initialiseMetrics(); + } + } + } + + void initialiseMetrics() + { + const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef)); + const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef)); + const float ctTotalHeight = ctAscent + ctDescent; + + ascent = ctAscent / ctTotalHeight; + unitsToHeightScaleFactor = 1.0f / ctTotalHeight; + pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); + + fontHeightToPointsFactor = referenceFontSize / ctTotalHeight; + + const short zero = 0; + CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero); + + CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName }; + CFTypeRef values[] = { ctFontRef, numberRef }; + attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease (numberRef); + } + ~OSXTypeface() { if (attributedStringAtts != nullptr) @@ -416,10 +577,11 @@ public: CFRelease (ctFontRef); } - float getAscent() const { return ascent; } - float getDescent() const { return 1.0f - ascent; } + float getAscent() const override { return ascent; } + float getDescent() const override { return 1.0f - ascent; } + float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; } - float getStringWidth (const String& text) + float getStringWidth (const String& text) override { float x = 0; @@ -452,7 +614,7 @@ public: return x; } - void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) override { xOffsets.add (0); @@ -473,7 +635,7 @@ public: CFIndex length = CTRunGetGlyphCount (run); const CoreTextTypeLayout::Advances advances (run, length); - const CoreTextTypeLayout::Glyphs glyphs (run, length); + const CoreTextTypeLayout::Glyphs glyphs (run, (size_t) length); for (int j = 0; j < length; ++j) { @@ -488,18 +650,7 @@ public: } } - EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) - { - Path path; - - if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) - return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), - path, transform); - - return nullptr; - } - - bool getOutlineForGlyph (int glyphNumber, Path& path) + bool getOutlineForGlyph (int glyphNumber, Path& path) override { jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty @@ -518,12 +669,14 @@ public: //============================================================================== CGFontRef fontRef; + CTFontRef ctFontRef; - float fontHeightToCGSizeFactor; + float fontHeightToPointsFactor; CGAffineTransform renderingTransform; + bool isMemoryFont; + private: - CTFontRef ctFontRef; CFDictionaryRef attributedStringAtts; float ascent, unitsToHeightScaleFactor; AffineTransform pathTransform; @@ -547,13 +700,102 @@ private: } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface) }; +CTFontRef getCTFontFromTypeface (const Font& f) +{ + if (OSXTypeface* tf = dynamic_cast (f.getTypeface())) + return tf->ctFontRef; + + return 0; +} + +StringArray Font::findAllTypefaceNames() +{ + StringArray names; + + #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 && ! JUCE_IOS + // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS + CFArrayRef fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames(); + + for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i) + { + const String family (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i))); + + if (! family.startsWithChar ('.')) // ignore fonts that start with a '.' + names.addIfNotAlreadyThere (family); + } + + CFRelease (fontFamilyArray); + #else + CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr); + CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef); + CFRelease (fontCollectionRef); + + for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i) + { + CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i); + CFStringRef cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute); + + names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily)); + + CFRelease (cfsFontFamily); + } + + CFRelease (fontDescriptorArray); + #endif + + names.sort (true); + return names; +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + if (FontStyleHelpers::isPlaceholderFamilyName (family)) + return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); + + StringArray results; + + CFStringRef cfsFontFamily = family.toCFString(); + CFStringRef keys[] = { kCTFontFamilyNameAttribute }; + CFTypeRef values[] = { cfsFontFamily }; + + CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease (cfsFontFamily); + + CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes); + CFRelease (fontDescAttributes); + + CFArrayRef fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks); + CFRelease (ctFontDescRef); + + CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr); + CFRelease (fontFamilyArray); + + CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef); + CFRelease (fontCollectionRef); + + if (fontDescriptorArray != nullptr) + { + for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i) + { + CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i); + CFStringRef cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute); + results.add (String::fromCFString (cfsFontStyle)); + CFRelease (cfsFontStyle); + } + + CFRelease (fontDescriptorArray); + } + + return results; +} + #else //============================================================================== -// The stuff that follows is a mash-up that supports pre-OSX 10.5 and pre-iOS 3.2 APIs. +// The stuff that follows is a mash-up that supports pre-OSX 10.5 APIs. // (Hopefully all of this can be ditched at some point in the future). //============================================================================== @@ -580,132 +822,49 @@ class OSXTypeface : public Typeface { public: OSXTypeface (const Font& font) - : Typeface (font.getTypefaceName()) + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()) { JUCE_AUTORELEASEPOOL - renderingTransform = CGAffineTransformIdentity; - - bool needsItalicTransform = false; - -#if JUCE_IOS - NSString* fontName = juceStringToNS (font.getTypefaceName()); - - if (font.isItalic() || font.isBold()) { - NSArray* familyFonts = [UIFont fontNamesForFamilyName: juceStringToNS (font.getTypefaceName())]; + renderingTransform = CGAffineTransformIdentity; - for (NSString* i in familyFonts) + NSDictionary* nsDict = [NSDictionary dictionaryWithObjectsAndKeys: + juceStringToNS (name), NSFontFamilyAttribute, + juceStringToNS (style), NSFontFaceAttribute, nil]; + + NSFontDescriptor* nsFontDesc = [NSFontDescriptor fontDescriptorWithFontAttributes: nsDict]; + nsFont = [NSFont fontWithDescriptor: nsFontDesc size: referenceFontSize]; + + [nsFont retain]; + + #if SUPPORT_ONLY_10_4_FONTS + initWithATSFont(); + #else + #if SUPPORT_10_4_FONTS + if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) { - const String fn (nsStringToJuce (i)); - const String afterDash (fn.fromFirstOccurrenceOf ("-", false, false)); - - const bool probablyBold = afterDash.containsIgnoreCase ("bold") || fn.endsWithIgnoreCase ("bold"); - const bool probablyItalic = afterDash.containsIgnoreCase ("oblique") - || afterDash.containsIgnoreCase ("italic") - || fn.endsWithIgnoreCase ("oblique") - || fn.endsWithIgnoreCase ("italic"); - - if (probablyBold == font.isBold() - && probablyItalic == font.isItalic()) - { - fontName = i; - needsItalicTransform = false; - break; - } - else if (probablyBold && (! probablyItalic) && probablyBold == font.isBold()) - { - fontName = i; - needsItalicTransform = true; // not ideal, so carry on in case we find a better one - } + initWithATSFont(); } + else + #endif + { + fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); - if (needsItalicTransform) - renderingTransform.c = 0.15f; + const float absAscent = std::abs ((float) CGFontGetAscent (fontRef)); + const float totalHeight = absAscent + std::abs ((float) CGFontGetDescent (fontRef)); + + ascent = absAscent / totalHeight; + unitsToHeightScaleFactor = 1.0f / totalHeight; + + const float nsFontAscent = std::abs ([nsFont ascender]); + const float nsFontDescent = std::abs ([nsFont descender]); + + fontHeightToPointsFactor = referenceFontSize / (nsFontAscent + nsFontDescent); + } + #endif + + pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); } - - fontRef = CGFontCreateWithFontName ((CFStringRef) fontName); - - if (fontRef == 0) - { - // Sometimes, UIFont manages to handle names that CGFontCreateWithFontName fails on... - UIFont* uiFont = [UIFont fontWithName: fontName size: 12]; - fontRef = CGFontCreateWithFontName ((CFStringRef) uiFont.fontName); - } - - const int ascender = abs (CGFontGetAscent (fontRef)); - const float totalHeight = ascender + abs (CGFontGetDescent (fontRef)); - ascent = ascender / totalHeight; - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / totalHeight; -#else - nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024]; - - if (font.isItalic()) - { - NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: nsFont - toHaveTrait: NSItalicFontMask]; - - if (newFont == nsFont) - needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. - - nsFont = newFont; - } - - if (font.isBold()) - nsFont = [[NSFontManager sharedFontManager] convertFont: nsFont toHaveTrait: NSBoldFontMask]; - - [nsFont retain]; - - ascent = std::abs ((float) [nsFont ascender]); - float totalSize = ascent + std::abs ((float) [nsFont descender]); - ascent /= totalSize; - - pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize); - - if (needsItalicTransform) - { - pathTransform = pathTransform.sheared (-0.15f, 0.0f); - renderingTransform.c = 0.15f; - } - - #if SUPPORT_ONLY_10_4_FONTS - ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - - if (atsFont == 0) - atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - - fontRef = CGFontCreateWithPlatformFont (&atsFont); - - const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]); - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToCGSizeFactor = 1024.0f / totalHeight; - #else - #if SUPPORT_10_4_FONTS - if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE) - { - ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - - if (atsFont == 0) - atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - - fontRef = CGFontCreateWithPlatformFont (&atsFont); - - const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]); - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToCGSizeFactor = 1024.0f / totalHeight; - } - else - #endif - { - fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]); - - const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef)); - unitsToHeightScaleFactor = 1.0f / totalHeight; - fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight; - } - #endif - -#endif } ~OSXTypeface() @@ -718,10 +877,32 @@ public: CGFontRelease (fontRef); } - float getAscent() const { return ascent; } - float getDescent() const { return 1.0f - ascent; } + #if SUPPORT_10_4_FONTS + void initWithATSFont() + { + ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); - float getStringWidth (const String& text) + if (atsFont == 0) + atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault); + + fontRef = CGFontCreateWithPlatformFont (&atsFont); + + const float absAscent = std::abs ([nsFont ascender]); + const float absDescent = std::abs ([nsFont descender]); + const float totalHeight = absAscent + absDescent; + + unitsToHeightScaleFactor = 1.0f / totalHeight; + fontHeightToPointsFactor = referenceFontSize / totalHeight; + ascent = absAscent / totalHeight; + } + #endif + + + float getAscent() const override { return ascent; } + float getDescent() const override { return 1.0f - ascent; } + float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; } + + float getStringWidth (const String& text) override { if (fontRef == 0 || text.isEmpty()) return 0; @@ -734,7 +915,7 @@ public: #if SUPPORT_ONLY_10_4_FONTS HeapBlock advances (length); - [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; + [nsFont getAdvancements: advances forGlyphs: reinterpret_cast (glyphs.getData()) count: length]; for (int i = 0; i < length; ++i) x += advances[i].width; @@ -762,7 +943,7 @@ public: return x * unitsToHeightScaleFactor; } - void getGlyphPositions (const String& text, Array & resultGlyphs, Array & xOffsets) + void getGlyphPositions (const String& text, Array& resultGlyphs, Array& xOffsets) override { xOffsets.add (0); @@ -820,18 +1001,7 @@ public: #endif } - EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) - { - Path path; - - if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) - return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), - path, transform); - - return nullptr; - } - - bool getOutlineForGlyph (int glyphNumber, Path& path) + bool getOutlineForGlyph (int glyphNumber, Path& path) override { #if JUCE_IOS return false; @@ -843,35 +1013,36 @@ public: jassert (path.isEmpty()); JUCE_AUTORELEASEPOOL - - NSBezierPath* bez = [NSBezierPath bezierPath]; - [bez moveToPoint: NSMakePoint (0, 0)]; - [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber - inFont: nsFont]; - - for (int i = 0; i < [bez elementCount]; ++i) { - NSPoint p[3]; - switch ([bez elementAtIndex: i associatedPoints: p]) - { - case NSMoveToBezierPathElement: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break; - case NSLineToBezierPathElement: path.lineTo ((float) p[0].x, (float) -p[0].y); break; - case NSCurveToBezierPathElement: path.cubicTo ((float) p[0].x, (float) -p[0].y, - (float) p[1].x, (float) -p[1].y, - (float) p[2].x, (float) -p[2].y); break; - case NSClosePathBezierPathElement: path.closeSubPath(); break; - default: jassertfalse; break; - } - } + NSBezierPath* bez = [NSBezierPath bezierPath]; + [bez moveToPoint: NSMakePoint (0, 0)]; + [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber + inFont: nsFont]; - path.applyTransform (pathTransform); + for (int i = 0; i < [bez elementCount]; ++i) + { + NSPoint p[3]; + switch ([bez elementAtIndex: i associatedPoints: p]) + { + case NSMoveToBezierPathElement: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break; + case NSLineToBezierPathElement: path.lineTo ((float) p[0].x, (float) -p[0].y); break; + case NSCurveToBezierPathElement: path.cubicTo ((float) p[0].x, (float) -p[0].y, + (float) p[1].x, (float) -p[1].y, + (float) p[2].x, (float) -p[2].y); break; + case NSClosePathBezierPathElement: path.closeSubPath(); break; + default: jassertfalse; break; + } + } + + path.applyTransform (pathTransform); + } return true; #endif } //============================================================================== CGFontRef fontRef; - float fontHeightToCGSizeFactor; + float fontHeightToPointsFactor; CGAffineTransform renderingTransform; private: @@ -910,16 +1081,16 @@ private: #endif } -#if ! SUPPORT_ONLY_10_4_FONTS + #if ! SUPPORT_ONLY_10_4_FONTS // Reads a CGFontRef's character map table to convert unicode into glyph numbers class CharToGlyphMapper { public: - CharToGlyphMapper (CGFontRef fontRef) + CharToGlyphMapper (CGFontRef cgFontRef) : segCount (0), endCode (0), startCode (0), idDelta (0), idRangeOffset (0), glyphIndexes (0) { - CFDataRef cmapTable = CGFontCopyTableForTag (fontRef, 'cmap'); + CFDataRef cmapTable = CGFontCopyTableForTag (cgFontRef, 'cmap'); if (cmapTable != 0) { @@ -984,8 +1155,8 @@ private: if (rangeOffset == 0) return delta + c; - else - return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i))); + + return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i))); } } @@ -1009,11 +1180,46 @@ private: }; ScopedPointer charToGlyphMapper; -#endif + #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface) }; +StringArray Font::findAllTypefaceNames() +{ + StringArray names; + + JUCE_AUTORELEASEPOOL + { + #if JUCE_IOS + for (NSString* name in [UIFont familyNames]) + #else + for (NSString* name in [[NSFontManager sharedFontManager] availableFontFamilies]) + #endif + names.add (nsStringToJuce (name)); + + names.sort (true); + } + + return names; +} + +StringArray Font::findAllTypefaceStyles (const String& family) +{ + if (FontStyleHelpers::isPlaceholderFamilyName (family)) + return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); + + StringArray results; + + JUCE_AUTORELEASEPOOL + { + for (NSArray* style in [[NSFontManager sharedFontManager] availableMembersOfFontFamily: juceStringToNS (family)]) + results.add (nsStringToJuce ((NSString*) [style objectAtIndex: 1])); + } + + return results; +} + #endif //============================================================================== @@ -1022,23 +1228,19 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) return new OSXTypeface (font); } -StringArray Font::findAllTypefaceNames() +Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) { - StringArray names; - - JUCE_AUTORELEASEPOOL - - #if JUCE_IOS - NSArray* fonts = [UIFont familyNames]; + #if JUCE_CORETEXT_AVAILABLE + return new OSXTypeface (data, dataSize); #else - NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies]; + jassertfalse; // You need CoreText enabled to use this feature! + return nullptr; #endif +} - for (unsigned int i = 0; i < [fonts count]; ++i) - names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); - - names.sort (true); - return names; +void Typeface::scanFolderForFonts (const File&) +{ + jassertfalse; // not implemented on this platform } struct DefaultFontNames @@ -1051,7 +1253,7 @@ struct DefaultFontNames #else : defaultSans ("Lucida Grande"), defaultSerif ("Times New Roman"), - defaultFixed ("Monaco"), + defaultFixed ("Menlo"), #endif defaultFallback ("Arial Unicode MS") { @@ -1064,24 +1266,56 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; - String faceName (font.getTypefaceName()); + Font newFont (font); + const String& faceName = font.getTypefaceName(); - if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; - else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; - else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; + if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); + else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); + else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed); - Font f (font); - f.setTypefaceName (faceName); - return Typeface::createSystemTypefaceFor (f); + if (font.getTypefaceStyle() == getDefaultStyle()) + newFont.setTypefaceStyle ("Regular"); + + return Typeface::createSystemTypefaceFor (newFont); } +#if JUCE_CORETEXT_AVAILABLE +static bool canAllTypefacesBeUsedInLayout (const AttributedString& text) +{ + const int numCharacterAttributes = text.getNumAttributes(); + + for (int i = 0; i < numCharacterAttributes; ++i) + { + if (const Font* const f = text.getAttribute (i)->getFont()) + { + if (OSXTypeface* tf = dynamic_cast (f->getTypeface())) + { + if (tf->isMemoryFont) + return false; + } + else if (dynamic_cast (f->getTypeface()) != nullptr) + { + return false; + } + } + } + + return true; +} +#endif + bool TextLayout::createNativeLayout (const AttributedString& text) { #if JUCE_CORETEXT_AVAILABLE - CoreTextTypeLayout::createLayout (*this, text); - return true; - #else + // Seems to be an unfathomable bug in CoreText which prevents the layout working with + // typefaces that were loaded from memory, so have to fallback if we hit any of those.. + if (canAllTypefacesBeUsedInLayout (text)) + { + CoreTextTypeLayout::createLayout (*this, text); + return true; + } + #endif + (void) text; return false; - #endif } diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp index c6803d5..e4356b7 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Direct2DGraphicsContext.cpp @@ -1,58 +1,27 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -class SharedD2DFactory : public DeletedAtShutdown -{ -public: - SharedD2DFactory() - { - jassertfalse; //xxx Direct2D support isn't ready for use yet! - - D2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED, d2dFactory.resetAndGetPointerAddress()); - DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED, __uuidof (IDWriteFactory), (IUnknown**) directWriteFactory.resetAndGetPointerAddress()); - - if (directWriteFactory != nullptr) - directWriteFactory->GetSystemFontCollection (systemFonts.resetAndGetPointerAddress()); - } - - ~SharedD2DFactory() - { - clearSingletonInstance(); - } - - juce_DeclareSingleton (SharedD2DFactory, false); - - ComSmartPtr d2dFactory; - ComSmartPtr directWriteFactory; - ComSmartPtr systemFonts; -}; - -juce_ImplementSingleton (SharedD2DFactory) - - -//============================================================================== class Direct2DLowLevelGraphicsContext : public LowLevelGraphicsContext { public: @@ -68,10 +37,12 @@ public: D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(); D2D1_HWND_RENDER_TARGET_PROPERTIES propsHwnd = D2D1::HwndRenderTargetProperties (hwnd, size); - HRESULT hr = SharedD2DFactory::getInstance()->d2dFactory->CreateHwndRenderTarget (props, propsHwnd, renderingTarget.resetAndGetPointerAddress()); - // xxx check for error - - hr = renderingTarget->CreateSolidColorBrush (D2D1::ColorF::ColorF (0.0f, 0.0f, 0.0f, 1.0f), colourBrush.resetAndGetPointerAddress()); + if (factories->d2dFactory != nullptr) + { + HRESULT hr = factories->d2dFactory->CreateHwndRenderTarget (props, propsHwnd, renderingTarget.resetAndGetPointerAddress()); + jassert (SUCCEEDED (hr)); (void) hr; + hr = renderingTarget->CreateSolidColorBrush (D2D1::ColorF::ColorF (0.0f, 0.0f, 0.0f, 1.0f), colourBrush.resetAndGetPointerAddress()); + } } ~Direct2DLowLevelGraphicsContext() @@ -110,21 +81,19 @@ public: bool isVectorDevice() const { return false; } - void setOrigin (int x, int y) + void setOrigin (Point o) { - currentState->origin.addXY (x, y); + addTransform (AffineTransform::translation ((float) o.x, (float) o.y)); } void addTransform (const AffineTransform& transform) { - //xxx todo - jassertfalse; + currentState->transform = transform.followedBy (currentState->transform); } - float getScaleFactor() + float getPhysicalPixelScaleFactor() { - jassertfalse; //xxx - return 1.0f; + return currentState->transform.getScaleFactor(); } bool clipToRectangle (const Rectangle& r) @@ -133,7 +102,7 @@ public: return ! isClipEmpty(); } - bool clipToRectangleList (const RectangleList& clipRegion) + bool clipToRectangleList (const RectangleList& clipRegion) { currentState->clipToRectList (rectListToPathGeometry (clipRegion)); return ! isClipEmpty(); @@ -146,24 +115,23 @@ public: void clipToPath (const Path& path, const AffineTransform& transform) { - currentState->clipToPath (pathToPathGeometry (path, transform, currentState->origin)); + currentState->clipToPath (pathToPathGeometry (path, transform)); } void clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform) { - currentState->clipToImage (sourceImage,transform); + currentState->clipToImage (sourceImage, transform); } bool clipRegionIntersects (const Rectangle& r) { - const Rectangle r2 (r + currentState->origin); - return currentState->clipRect.intersects (r2); + return currentState->clipRect.intersects (r.toFloat().transformed (currentState->transform).getSmallestIntegerContainer()); } Rectangle getClipBounds() const { // xxx could this take into account complex clip regions? - return currentState->clipRect - currentState->origin; + return currentState->clipRect.toFloat().transformed (currentState->transform.inverted()).getSmallestIntegerContainer(); } bool isClipEmpty() const @@ -184,7 +152,7 @@ public: currentState = states.getLast(); } - void beginTransparencyLayer (float opacity) + void beginTransparencyLayer (float /*opacity*/) { jassertfalse; //xxx todo } @@ -208,16 +176,29 @@ public: { } - void fillRect (const Rectangle& r, bool replaceExistingContents) + void fillRect (const Rectangle& r, bool /*replaceExistingContents*/) { + fillRect (r.toFloat()); + } + + void fillRect (const Rectangle& r) + { + renderingTarget->SetTransform (transformToMatrix (currentState->transform)); currentState->createBrush(); - renderingTarget->FillRectangle (rectangleToRectF (r + currentState->origin), currentState->currentBrush); + renderingTarget->FillRectangle (rectangleToRectF (r), currentState->currentBrush); + renderingTarget->SetTransform (D2D1::IdentityMatrix()); + } + + void fillRectList (const RectangleList& list) + { + for (const Rectangle* r = list.begin(), * const e = list.end(); r != e; ++r) + fillRect (*r); } void fillPath (const Path& p, const AffineTransform& transform) { currentState->createBrush(); - ComSmartPtr geometry (pathToPathGeometry (p, transform, currentState->origin)); + ComSmartPtr geometry (pathToPathGeometry (p, transform.followedBy (currentState->transform))); if (renderingTarget != nullptr) renderingTarget->FillGeometry (geometry, currentState->currentBrush); @@ -225,10 +206,7 @@ public: void drawImage (const Image& image, const AffineTransform& transform) { - const int x = currentState->origin.getX(); - const int y = currentState->origin.getY(); - - renderingTarget->SetTransform (transformToMatrix (transform) * D2D1::Matrix3x2F::Translation (x, y)); + renderingTarget->SetTransform (transformToMatrix (transform.followedBy (currentState->transform))); D2D1_SIZE_U size; size.width = image.getWidth(); @@ -254,40 +232,13 @@ public: void drawLine (const Line & line) { // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour - const Line l (line.getStart() + currentState->origin.toFloat(), - line.getEnd() + currentState->origin.toFloat()); - + renderingTarget->SetTransform (transformToMatrix (currentState->transform)); currentState->createBrush(); - renderingTarget->DrawLine (D2D1::Point2F (l.getStartX(), l.getStartY()), - D2D1::Point2F (l.getEndX(), l.getEndY()), - currentState->currentBrush); - } - - void drawVerticalLine (int x, float top, float bottom) - { - // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour - currentState->createBrush(); - - x += currentState->origin.getX(); - const int y = currentState->origin.getY(); - - renderingTarget->DrawLine (D2D1::Point2F (x, y + top), - D2D1::Point2F (x, y + bottom), - currentState->currentBrush); - } - - void drawHorizontalLine (int y, float left, float right) - { - // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour - currentState->createBrush(); - - y += currentState->origin.getY(); - const int x = currentState->origin.getX(); - - renderingTarget->DrawLine (D2D1::Point2F (x + left, y), - D2D1::Point2F (x + right, y), + renderingTarget->DrawLine (D2D1::Point2F (line.getStartX(), line.getStartY()), + D2D1::Point2F (line.getEndX(), line.getEndY()), currentState->currentBrush); + renderingTarget->SetTransform (D2D1::IdentityMatrix()); } void setFont (const Font& newFont) @@ -302,49 +253,53 @@ public: void drawGlyph (int glyphNumber, const AffineTransform& transform) { - const float x = currentState->origin.getX(); - const float y = currentState->origin.getY(); - currentState->createBrush(); currentState->createFont(); - float kerning = currentState->font.getExtraKerningFactor(); // xxx why does removing this line mess up the kerning?? float hScale = currentState->font.getHorizontalScale(); - renderingTarget->SetTransform (D2D1::Matrix3x2F::Scale (hScale, 1) * transformToMatrix (transform) * D2D1::Matrix3x2F::Translation (x, y)); - - float dpiX = 0, dpiY = 0; - SharedD2DFactory::getInstance()->d2dFactory->GetDesktopDpi (&dpiX, &dpiY); - - UINT32 glyphNum = glyphNumber; - UINT16 glyphNum1 = 0; // xxx needs a better name - what is this for? - currentState->currentFontFace->GetGlyphIndices (&glyphNum, 1, &glyphNum1); + renderingTarget->SetTransform (transformToMatrix (AffineTransform::scale (hScale, 1.0f) + .followedBy (transform) + .followedBy (currentState->transform))); + const UINT16 glyphIndices = (UINT16) glyphNumber; + const FLOAT glyphAdvances = 0; DWRITE_GLYPH_OFFSET offset; offset.advanceOffset = 0; offset.ascenderOffset = 0; - float glyphAdvances = 0; - DWRITE_GLYPH_RUN glyph; - glyph.fontFace = currentState->currentFontFace; - glyph.glyphCount = 1; - glyph.glyphIndices = &glyphNum1; - glyph.isSideways = FALSE; - glyph.glyphAdvances = &glyphAdvances; - glyph.glyphOffsets = &offset; - glyph.fontEmSize = (float) currentState->font.getHeight() * dpiX / 96.0f * (1 + currentState->fontScaling) / 2; + DWRITE_GLYPH_RUN glyphRun; + glyphRun.fontFace = currentState->currentFontFace; + glyphRun.fontEmSize = (FLOAT) (currentState->font.getHeight() * currentState->fontHeightToEmSizeFactor); + glyphRun.glyphCount = 1; + glyphRun.glyphIndices = &glyphIndices; + glyphRun.glyphAdvances = &glyphAdvances; + glyphRun.glyphOffsets = &offset; + glyphRun.isSideways = FALSE; + glyphRun.bidiLevel = 0; - renderingTarget->DrawGlyphRun (D2D1::Point2F (0, 0), &glyph, currentState->currentBrush); + renderingTarget->DrawGlyphRun (D2D1::Point2F (0, 0), &glyphRun, currentState->currentBrush); renderingTarget->SetTransform (D2D1::IdentityMatrix()); } + bool drawTextLayout (const AttributedString& text, const Rectangle& area) + { + renderingTarget->SetTransform (transformToMatrix (currentState->transform)); + + DirectWriteTypeLayout::drawToD2DContext (text, area, renderingTarget, factories->directWriteFactory, + factories->d2dFactory, factories->systemFonts); + + renderingTarget->SetTransform (D2D1::IdentityMatrix()); + return true; + } + //============================================================================== class SavedState { public: SavedState (Direct2DLowLevelGraphicsContext& owner_) : owner (owner_), currentBrush (0), - fontScaling (1.0f), currentFontFace (0), + fontHeightToEmSizeFactor (1.0f), currentFontFace (0), clipsRect (false), shouldClipRect (false), clipsRectList (false), shouldClipRectList (false), clipsComplex (false), shouldClipComplex (false), @@ -356,8 +311,8 @@ public: // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write? setFill (owner.currentState->fillType); currentBrush = owner.currentState->currentBrush; - origin = owner.currentState->origin; clipRect = owner.currentState->clipRect; + transform = owner.currentState->transform; font = owner.currentState->font; currentFontFace = owner.currentState->currentFontFace; @@ -390,7 +345,7 @@ public: void clipToRectangle (const Rectangle& r) { clearClip(); - clipRect = r + origin; + clipRect = r.toFloat().transformed (transform).getSmallestIntegerContainer(); shouldClipRect = true; pushClips(); } @@ -572,35 +527,11 @@ public: void createFont() { - // xxx The font shouldn't be managed by the graphics context. - // The correct way to handle font lifetimes is to use a subclass of Typeface - see - // OSXTypeface and WindowsTypeface classes. D2D support could probably just be added to the - // WindowsTypeface class. - - if (currentFontFace == 0) + if (currentFontFace == nullptr) { - WindowsTypeface* systemType = dynamic_cast (font.getTypeface()); - fontScaling = systemType->getAscent(); - - BOOL fontFound; - uint32 fontIndex; - - IDWriteFontCollection* fonts = SharedD2DFactory::getInstance()->systemFonts; - - fonts->FindFamilyName (systemType->getName(), &fontIndex, &fontFound); - if (! fontFound) - fontIndex = 0; - - ComSmartPtr fontFam; - fonts->GetFontFamily (fontIndex, fontFam.resetAndGetPointerAddress()); - - ComSmartPtr font; - DWRITE_FONT_WEIGHT weight = this->font.isBold() ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; - DWRITE_FONT_STYLE style = this->font.isItalic() ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; - fontFam->GetFirstMatchingFont (weight, DWRITE_FONT_STRETCH_NORMAL, style, font.resetAndGetPointerAddress()); - - font->CreateFontFace (localFontFace.resetAndGetPointerAddress()); - currentFontFace = localFontFace; + WindowsDirectWriteTypeface* typeface = dynamic_cast (font.getTypeface()); + currentFontFace = typeface->getIDWriteFontFace(); + fontHeightToEmSizeFactor = typeface->unitsToHeightScaleFactor(); } } @@ -626,9 +557,6 @@ public: { if (currentBrush == 0) { - const int x = origin.getX(); - const int y = origin.getY(); - if (fillType.isColour()) { D2D1_COLOR_F colour = colourToD2D (fillType.colour); @@ -667,7 +595,7 @@ public: D2D1_BRUSH_PROPERTIES brushProps; brushProps.opacity = fillType.getOpacity(); - brushProps.transform = transformToMatrix (fillType.transform); + brushProps.transform = transformToMatrix (fillType.transform.followedBy (transform)); const int numColors = fillType.gradient->getNumColours(); @@ -676,7 +604,7 @@ public: for (int i = fillType.gradient->getNumColours(); --i >= 0;) { stops[i].color = colourToD2D (fillType.gradient->getColour(i)); - stops[i].position = fillType.gradient->getColourPosition(i); + stops[i].position = (FLOAT) fillType.gradient->getColourPosition(i); } owner.renderingTarget->CreateGradientStopCollection (stops.getData(), numColors, gradientStops.resetAndGetPointerAddress()); @@ -685,12 +613,12 @@ public: { radialGradient = 0; - const Point& p1 = fillType.gradient->point1; - const Point& p2 = fillType.gradient->point2; + const Point p1 = fillType.gradient->point1; + const Point p2 = fillType.gradient->point2; float r = p1.getDistanceFrom (p2); D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES props = - D2D1::RadialGradientBrushProperties (D2D1::Point2F (p1.getX() + x, p1.getY() + y), + D2D1::RadialGradientBrushProperties (D2D1::Point2F (p1.x, p1.y), D2D1::Point2F (0, 0), r, r); @@ -701,12 +629,12 @@ public: { linearGradient = 0; - const Point& p1 = fillType.gradient->point1; - const Point& p2 = fillType.gradient->point2; + const Point p1 = fillType.gradient->point1; + const Point p2 = fillType.gradient->point2; D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props = - D2D1::LinearGradientBrushProperties (D2D1::Point2F (p1.getX() + x, p1.getY() + y), - D2D1::Point2F (p2.getX() + x, p2.getY() + y)); + D2D1::LinearGradientBrushProperties (D2D1::Point2F (p1.x, p1.y), + D2D1::Point2F (p2.x, p2.y)); owner.renderingTarget->CreateLinearGradientBrush (props, brushProps, gradientStops, linearGradient.resetAndGetPointerAddress()); @@ -721,10 +649,10 @@ public: Direct2DLowLevelGraphicsContext& owner; - Point origin; + AffineTransform transform; Font font; - float fontScaling; + float fontHeightToEmSizeFactor; IDWriteFontFace* currentFontFace; ComSmartPtr localFontFace; @@ -760,11 +688,12 @@ public: ComSmartPtr gradientStops; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState) }; //============================================================================== private: + SharedResourcePointer factories; HWND hwnd; ComSmartPtr renderingTarget; ComSmartPtr colourBrush; @@ -774,20 +703,21 @@ private: OwnedArray states; //============================================================================== - static D2D1_RECT_F rectangleToRectF (const Rectangle& r) + template + static D2D1_RECT_F rectangleToRectF (const Rectangle& r) { return D2D1::RectF ((float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom()); } - static const D2D1_COLOR_F colourToD2D (const Colour& c) + static D2D1_COLOR_F colourToD2D (Colour c) { return D2D1::ColorF::ColorF (c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha()); } - static const D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform = AffineTransform::identity) + static D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform) { transform.transformPoint (x, y); - return D2D1::Point2F (x, y); + return D2D1::Point2F ((FLOAT) x, (FLOAT) y); } static void rectToGeometrySink (const Rectangle& rect, ID2D1GeometrySink* sink) @@ -799,10 +729,10 @@ private: sink->EndFigure (D2D1_FIGURE_END_CLOSED); } - static ID2D1PathGeometry* rectListToPathGeometry (const RectangleList& clipRegion) + static ID2D1PathGeometry* rectListToPathGeometry (const RectangleList& clipRegion) { ID2D1PathGeometry* p = nullptr; - SharedD2DFactory::getInstance()->d2dFactory->CreatePathGeometry (&p); + factories->d2dFactory->CreatePathGeometry (&p); ComSmartPtr sink; HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); // xxx handle error @@ -815,7 +745,7 @@ private: return p; } - static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform, int x, int y) + static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform) { Path::Iterator it (path); @@ -828,13 +758,13 @@ private: D2D1_BEZIER_SEGMENT seg; transform.transformPoint (it.x1, it.y1); - seg.point1 = D2D1::Point2F (it.x1 + x, it.y1 + y); + seg.point1 = D2D1::Point2F (it.x1, it.y1); transform.transformPoint (it.x2, it.y2); - seg.point2 = D2D1::Point2F (it.x2 + x, it.y2 + y); + seg.point2 = D2D1::Point2F (it.x2, it.y2); transform.transformPoint(it.x3, it.y3); - seg.point3 = D2D1::Point2F (it.x3 + x, it.y3 + y); + seg.point3 = D2D1::Point2F (it.x3, it.y3); sink->AddBezier (seg); break; @@ -843,7 +773,7 @@ private: case Path::Iterator::lineTo: { transform.transformPoint (it.x1, it.y1); - sink->AddLine (D2D1::Point2F (it.x1 + x, it.y1 + y)); + sink->AddLine (D2D1::Point2F (it.x1, it.y1)); break; } @@ -852,10 +782,10 @@ private: D2D1_QUADRATIC_BEZIER_SEGMENT seg; transform.transformPoint (it.x1, it.y1); - seg.point1 = D2D1::Point2F (it.x1 + x, it.y1 + y); + seg.point1 = D2D1::Point2F (it.x1, it.y1); transform.transformPoint (it.x2, it.y2); - seg.point2 = D2D1::Point2F (it.x2 + x, it.y2 + y); + seg.point2 = D2D1::Point2F (it.x2, it.y2); sink->AddQuadraticBezier (seg); break; @@ -870,29 +800,29 @@ private: case Path::Iterator::startNewSubPath: { transform.transformPoint (it.x1, it.y1); - sink->BeginFigure (D2D1::Point2F (it.x1 + x, it.y1 + y), D2D1_FIGURE_BEGIN_FILLED); + sink->BeginFigure (D2D1::Point2F (it.x1, it.y1), D2D1_FIGURE_BEGIN_FILLED); break; } } } } - static ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform, const Point& point) + static ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform) { ID2D1PathGeometry* p = nullptr; - SharedD2DFactory::getInstance()->d2dFactory->CreatePathGeometry (&p); + factories->d2dFactory->CreatePathGeometry (&p); ComSmartPtr sink; HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding() - pathToGeometrySink (path, sink, transform, point.getX(), point.getY()); + pathToGeometrySink (path, sink, transform); hr = sink->Close(); return p; } - static const D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform) + static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform) { D2D1::Matrix3x2F matrix; matrix._11 = transform.mat00; @@ -904,5 +834,5 @@ private: return matrix; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DLowLevelGraphicsContext); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DLowLevelGraphicsContext) }; diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp index f63bf79..89ab8f6 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeLayout.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -27,24 +26,21 @@ #if JUCE_USE_DIRECTWRITE namespace DirectWriteTypeLayout { - class CustomDirectWriteTextRenderer : public ComBaseClassHelper + class CustomDirectWriteTextRenderer : public ComBaseClassHelper { public: - CustomDirectWriteTextRenderer (IDWriteFontCollection* const fontCollection_) - : fontCollection (fontCollection_), + CustomDirectWriteTextRenderer (IDWriteFontCollection* const fonts) + : ComBaseClassHelper (0), + fontCollection (fonts), currentLine (-1), lastOriginY (-10000.0f) { - resetReferenceCount(); } JUCE_COMRESULT QueryInterface (REFIID refId, void** result) { - #if ! JUCE_MINGW - if (refId == __uuidof (IDWritePixelSnapping)) { AddRef(); *result = dynamic_cast (this); return S_OK; } - #else - jassertfalse; // need to find a mingw equivalent of __uuidof to make this possible - #endif + if (refId == __uuidof (IDWritePixelSnapping)) + return castToType (result); return ComBaseClassHelper::QueryInterface (refId, result); } @@ -67,6 +63,9 @@ namespace DirectWriteTypeLayout { TextLayout* const layout = static_cast (clientDrawingContext); + if (! (baselineOriginY >= -1.0e10f && baselineOriginY <= 1.0e10f)) + baselineOriginY = 0; // DirectWrite sometimes sends NaNs in this parameter + if (baselineOriginY != lastOriginY) { lastOriginY = baselineOriginY; @@ -77,7 +76,8 @@ namespace DirectWriteTypeLayout jassert (currentLine == layout->getNumLines()); TextLayout::Line* const newLine = new TextLayout::Line(); layout->addLine (newLine); - newLine->lineOrigin = Point (baselineOriginX, baselineOriginY); // The x value is only correct when dealing with LTR text + + newLine->lineOrigin = Point (baselineOriginX, baselineOriginY); } } @@ -89,8 +89,8 @@ namespace DirectWriteTypeLayout glyphLine.ascent = jmax (glyphLine.ascent, scaledFontSize (dwFontMetrics.ascent, dwFontMetrics, glyphRun)); glyphLine.descent = jmax (glyphLine.descent, scaledFontSize (dwFontMetrics.descent, dwFontMetrics, glyphRun)); - int styleFlags = 0; - const String fontName (getFontName (glyphRun, styleFlags)); + String fontFamily, fontStyle; + getFontFamilyAndStyle (glyphRun, fontFamily, fontStyle); TextLayout::Run* const glyphRunLayout = new TextLayout::Run (Range (runDescription->textPosition, runDescription->textPosition + runDescription->stringLength), @@ -102,7 +102,7 @@ namespace DirectWriteTypeLayout const float totalHeight = std::abs ((float) dwFontMetrics.ascent) + std::abs ((float) dwFontMetrics.descent); const float fontHeightToEmSizeFactor = (float) dwFontMetrics.designUnitsPerEm / totalHeight; - glyphRunLayout->font = Font (fontName, glyphRun->fontEmSize / fontHeightToEmSizeFactor, styleFlags); + glyphRunLayout->font = Font (fontFamily, fontStyle, glyphRun->fontEmSize / fontHeightToEmSizeFactor); glyphRunLayout->colour = getColourOf (static_cast (clientDrawingEffect)); const Point lineOrigin (layout->getLine (currentLine).lineOrigin); @@ -145,62 +145,32 @@ namespace DirectWriteTypeLayout return Colour::fromFloatRGBA (colour.r, colour.g, colour.b, colour.a); } - String getFontName (DWRITE_GLYPH_RUN const* glyphRun, int& styleFlags) const + void getFontFamilyAndStyle (DWRITE_GLYPH_RUN const* glyphRun, String& family, String& style) const { ComSmartPtr dwFont; - HRESULT hr = fontCollection->GetFontFromFontFace (glyphRun->fontFace, dwFont.resetAndGetPointerAddress()); jassert (dwFont != nullptr); - if (dwFont->GetWeight() == DWRITE_FONT_WEIGHT_BOLD) styleFlags |= Font::bold; - if (dwFont->GetStyle() == DWRITE_FONT_STYLE_ITALIC) styleFlags |= Font::italic; + { + ComSmartPtr dwFontFamily; + hr = dwFont->GetFontFamily (dwFontFamily.resetAndGetPointerAddress()); + family = getFontFamilyName (dwFontFamily); + } - ComSmartPtr dwFontFamily; - hr = dwFont->GetFontFamily (dwFontFamily.resetAndGetPointerAddress()); - jassert (dwFontFamily != nullptr); - - // Get the Font Family Names - ComSmartPtr dwFamilyNames; - hr = dwFontFamily->GetFamilyNames (dwFamilyNames.resetAndGetPointerAddress()); - jassert (dwFamilyNames != nullptr); - - UINT32 index = 0; - BOOL exists = false; - hr = dwFamilyNames->FindLocaleName (L"en-us", &index, &exists); - if (! exists) - index = 0; - - UINT32 length = 0; - hr = dwFamilyNames->GetStringLength (index, &length); - - HeapBlock name (length + 1); - hr = dwFamilyNames->GetString (index, name, length + 1); - - return String (name); + style = getFontFaceName (dwFont); } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomDirectWriteTextRenderer); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomDirectWriteTextRenderer) }; //================================================================================================== - float getFontHeightToEmSizeFactor (const Font& font, IDWriteFontCollection& dwFontCollection) + static float getFontHeightToEmSizeFactor (IDWriteFont* const dwFont) { - BOOL fontFound = false; - uint32 fontIndex; - dwFontCollection.FindFamilyName (font.getTypefaceName().toWideCharPointer(), &fontIndex, &fontFound); - - if (! fontFound) - fontIndex = 0; - - ComSmartPtr dwFontFamily; - HRESULT hr = dwFontCollection.GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); - - ComSmartPtr dwFont; - hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, - dwFont.resetAndGetPointerAddress()); - ComSmartPtr dwFontFace; - hr = dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); + dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); + + if (dwFontFace == nullptr) + return 1.0f; DWRITE_FONT_METRICS dwFontMetrics; dwFontFace->GetMetrics (&dwFontMetrics); @@ -231,42 +201,74 @@ namespace DirectWriteTypeLayout default: jassertfalse; break; // Illegal flags! } - format->SetTextAlignment (alignment); - format->SetWordWrapping (wrapType); - // DirectWrite does not automatically set reading direction // This must be set correctly and manually when using RTL Scripts (Hebrew, Arabic) if (text.getReadingDirection() == AttributedString::rightToLeft) + { format->SetReadingDirection (DWRITE_READING_DIRECTION_RIGHT_TO_LEFT); + + switch (text.getJustification().getOnlyHorizontalFlags()) + { + case Justification::left: alignment = DWRITE_TEXT_ALIGNMENT_TRAILING; break; + case Justification::right: alignment = DWRITE_TEXT_ALIGNMENT_LEADING; break; + default: break; + } + } + + format->SetTextAlignment (alignment); + format->SetWordWrapping (wrapType); } void addAttributedRange (const AttributedString::Attribute& attr, IDWriteTextLayout* textLayout, - const int textLen, ID2D1DCRenderTarget* const renderTarget, IDWriteFontCollection* const fontCollection) + const int textLen, ID2D1RenderTarget* const renderTarget, IDWriteFontCollection* const fontCollection) { DWRITE_TEXT_RANGE range; range.startPosition = attr.range.getStart(); range.length = jmin (attr.range.getLength(), textLen - attr.range.getStart()); - const Font* const font = attr.getFont(); - - if (font != nullptr) + if (const Font* const font = attr.getFont()) { - textLayout->SetFontFamilyName (font->getTypefaceName().toWideCharPointer(), range); + const String familyName (FontStyleHelpers::getConcreteFamilyName (*font)); - const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (*font, *fontCollection); + BOOL fontFound = false; + uint32 fontIndex; + fontCollection->FindFamilyName (familyName.toWideCharPointer(), + &fontIndex, &fontFound); + + if (! fontFound) + fontIndex = 0; + + ComSmartPtr fontFamily; + HRESULT hr = fontCollection->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); + + ComSmartPtr dwFont; + uint32 fontFacesCount = 0; + fontFacesCount = fontFamily->GetFontCount(); + + for (int i = fontFacesCount; --i >= 0;) + { + hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); + + if (font->getTypefaceStyle() == getFontFaceName (dwFont)) + break; + } + + textLayout->SetFontFamilyName (familyName.toWideCharPointer(), range); + textLayout->SetFontWeight (dwFont->GetWeight(), range); + textLayout->SetFontStretch (dwFont->GetStretch(), range); + textLayout->SetFontStyle (dwFont->GetStyle(), range); + + const float fontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); textLayout->SetFontSize (font->getHeight() * fontHeightToEmSizeFactor, range); - - if (font->isBold()) textLayout->SetFontWeight (DWRITE_FONT_WEIGHT_BOLD, range); - if (font->isItalic()) textLayout->SetFontStyle (DWRITE_FONT_STYLE_ITALIC, range); } - if (attr.getColour() != nullptr) + if (const Colour* const colour = attr.getColour()) { ComSmartPtr d2dBrush; - renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (attr.getColour()->getFloatRed(), - attr.getColour()->getFloatGreen(), - attr.getColour()->getFloatBlue(), - attr.getColour()->getFloatAlpha())), + renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (colour->getFloatRed(), + colour->getFloatGreen(), + colour->getFloatBlue(), + colour->getFloatAlpha())), d2dBrush.resetAndGetPointerAddress()); // We need to call SetDrawingEffect with a legimate brush to get DirectWrite to break text based on colours @@ -274,6 +276,56 @@ namespace DirectWriteTypeLayout } } + bool setupLayout (const AttributedString& text, const float maxWidth, const float maxHeight, + ID2D1RenderTarget* const renderTarget, IDWriteFactory* const directWriteFactory, + IDWriteFontCollection* const fontCollection, ComSmartPtr& textLayout) + { + // To add color to text, we need to create a D2D render target + // Since we are not actually rendering to a D2D context we create a temporary GDI render target + + Font defaultFont; + BOOL fontFound = false; + uint32 fontIndex; + fontCollection->FindFamilyName (defaultFont.getTypeface()->getName().toWideCharPointer(), &fontIndex, &fontFound); + + if (! fontFound) + fontIndex = 0; + + ComSmartPtr dwFontFamily; + HRESULT hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); + + ComSmartPtr dwFont; + hr = dwFontFamily->GetFirstMatchingFont (DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STRETCH_NORMAL, DWRITE_FONT_STYLE_NORMAL, + dwFont.resetAndGetPointerAddress()); + + const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (dwFont); + + jassert (directWriteFactory != nullptr); + + ComSmartPtr dwTextFormat; + hr = directWriteFactory->CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), fontCollection, + DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, + defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, + L"en-us", dwTextFormat.resetAndGetPointerAddress()); + + setTextFormatProperties (text, dwTextFormat); + + const int textLen = text.getText().length(); + + hr = directWriteFactory->CreateTextLayout (text.getText().toWideCharPointer(), textLen, dwTextFormat, + maxWidth, maxHeight, textLayout.resetAndGetPointerAddress()); + + if (FAILED (hr) || textLayout == nullptr) + return false; + + const int numAttributes = text.getNumAttributes(); + + for (int i = 0; i < numAttributes; ++i) + addAttributedRange (*text.getAttribute (i), textLayout, textLen, renderTarget, fontCollection); + + return true; + } + void createLayout (TextLayout& layout, const AttributedString& text, IDWriteFactory* const directWriteFactory, ID2D1Factory* const direct2dFactory, IDWriteFontCollection* const fontCollection) { @@ -289,30 +341,10 @@ namespace DirectWriteTypeLayout ComSmartPtr renderTarget; HRESULT hr = direct2dFactory->CreateDCRenderTarget (&d2dRTProp, renderTarget.resetAndGetPointerAddress()); - Font defaultFont; - const float defaultFontHeightToEmSizeFactor = getFontHeightToEmSizeFactor (defaultFont, *fontCollection); - - jassert (directWriteFactory != nullptr); - - ComSmartPtr dwTextFormat; - hr = directWriteFactory->CreateTextFormat (defaultFont.getTypefaceName().toWideCharPointer(), fontCollection, - DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, - defaultFont.getHeight() * defaultFontHeightToEmSizeFactor, - L"en-us", dwTextFormat.resetAndGetPointerAddress()); - - setTextFormatProperties (text, dwTextFormat); - - const int textLen = text.getText().length(); - ComSmartPtr dwTextLayout; - hr = directWriteFactory->CreateTextLayout (text.getText().toWideCharPointer(), textLen, - dwTextFormat, layout.getWidth(), - 1.0e7f, dwTextLayout.resetAndGetPointerAddress()); - const int numAttributes = text.getNumAttributes(); - - for (int i = 0; i < numAttributes; ++i) - addAttributedRange (*text.getAttribute (i), dwTextLayout, textLen, renderTarget, fontCollection); + if (! setupLayout (text, layout.getWidth(), 1.0e7f, renderTarget, directWriteFactory, fontCollection, dwTextLayout)) + return; UINT32 actualLineCount = 0; hr = dwTextLayout->GetLineMetrics (nullptr, 0, &actualLineCount); @@ -331,8 +363,24 @@ namespace DirectWriteTypeLayout for (int i = 0; i < numLines; ++i) { - lastLocation = dwLineMetrics[i].length; layout.getLine(i).stringRange = Range (lastLocation, (int) lastLocation + dwLineMetrics[i].length); + lastLocation += dwLineMetrics[i].length; + } + } + + void drawToD2DContext (const AttributedString& text, const Rectangle& area, ID2D1RenderTarget* const renderTarget, + IDWriteFactory* const directWriteFactory, IDWriteFontCollection* const fontCollection) + { + ComSmartPtr dwTextLayout; + + if (setupLayout (text, area.getWidth(), area.getHeight(), renderTarget, directWriteFactory, fontCollection, dwTextLayout)) + { + ComSmartPtr d2dBrush; + renderTarget->CreateSolidColorBrush (D2D1::ColorF (D2D1::ColorF (0.0f, 0.0f, 0.0f, 1.0f)), + d2dBrush.resetAndGetPointerAddress()); + + renderTarget->DrawTextLayout (D2D1::Point2F ((float) area.getX(), (float) area.getY()), + dwTextLayout, d2dBrush, D2D1_DRAW_TEXT_OPTIONS_CLIP); } } } @@ -341,12 +389,26 @@ namespace DirectWriteTypeLayout bool TextLayout::createNativeLayout (const AttributedString& text) { #if JUCE_USE_DIRECTWRITE - const Direct2DFactories& factories = Direct2DFactories::getInstance(); + SharedResourcePointer factories; - if (factories.d2dFactory != nullptr && factories.systemFonts != nullptr) + if (factories->d2dFactory != nullptr && factories->systemFonts != nullptr) { - DirectWriteTypeLayout::createLayout (*this, text, factories.directWriteFactory, - factories.d2dFactory, factories.systemFonts); + #if JUCE_64BIT + // There's a mysterious bug in 64-bit Windows that causes garbage floating-point + // values to be returned to DrawGlyphRun the first time that it gets used. + // In lieu of a better plan, this bodge uses a dummy call to work around this. + static bool hasBeenCalled = false; + if (! hasBeenCalled) + { + hasBeenCalled = true; + TextLayout dummy; + DirectWriteTypeLayout::createLayout (dummy, text, factories->directWriteFactory, + factories->d2dFactory, factories->systemFonts); + } + #endif + + DirectWriteTypeLayout::createLayout (*this, text, factories->directWriteFactory, + factories->d2dFactory, factories->systemFonts); return true; } #else diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp index 0f1869c..7240142 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp @@ -1,29 +1,69 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ #if JUCE_USE_DIRECTWRITE +namespace +{ + static String getLocalisedName (IDWriteLocalizedStrings* names) + { + jassert (names != nullptr); + + uint32 index = 0; + BOOL exists = false; + HRESULT hr = names->FindLocaleName (L"en-us", &index, &exists); + if (! exists) + index = 0; + + uint32 length = 0; + hr = names->GetStringLength (index, &length); + + HeapBlock name (length + 1); + hr = names->GetString (index, name, length + 1); + + return static_cast (name); + } + + static String getFontFamilyName (IDWriteFontFamily* family) + { + jassert (family != nullptr); + ComSmartPtr familyNames; + HRESULT hr = family->GetFamilyNames (familyNames.resetAndGetPointerAddress()); + jassert (SUCCEEDED (hr)); (void) hr; + return getLocalisedName (familyNames); + } + + static String getFontFaceName (IDWriteFont* font) + { + jassert (font != nullptr); + ComSmartPtr faceNames; + HRESULT hr = font->GetFaceNames (faceNames.resetAndGetPointerAddress()); + jassert (SUCCEEDED (hr)); (void) hr; + + return getLocalisedName (faceNames); + } +} + class Direct2DFactories { public: @@ -31,7 +71,8 @@ public: { if (direct2dDll.open ("d2d1.dll")) { - JUCE_DLL_FUNCTION (D2D1CreateFactory, d2d1CreateFactory, HRESULT, direct2dDll, (D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**)) + JUCE_LOAD_WINAPI_FUNCTION (direct2dDll, D2D1CreateFactory, d2d1CreateFactory, + HRESULT, (D2D1_FACTORY_TYPE, REFIID, D2D1_FACTORY_OPTIONS*, void**)) if (d2d1CreateFactory != nullptr) { @@ -45,7 +86,8 @@ public: if (directWriteDll.open ("DWrite.dll")) { - JUCE_DLL_FUNCTION (DWriteCreateFactory, dWriteCreateFactory, HRESULT, directWriteDll, (DWRITE_FACTORY_TYPE, REFIID, IUnknown**)) + JUCE_LOAD_WINAPI_FUNCTION (directWriteDll, DWriteCreateFactory, dWriteCreateFactory, + HRESULT, (DWRITE_FACTORY_TYPE, REFIID, IUnknown**)) if (dWriteCreateFactory != nullptr) { @@ -65,20 +107,14 @@ public: systemFonts = nullptr; } - static const Direct2DFactories& getInstance() - { - static Direct2DFactories instance; - return instance; - } - - ComSmartPtr d2dFactory; - ComSmartPtr directWriteFactory; - ComSmartPtr systemFonts; + ComSmartPtr d2dFactory; + ComSmartPtr directWriteFactory; + ComSmartPtr systemFonts; private: DynamicLibrary direct2dDll, directWriteDll; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Direct2DFactories) }; //================================================================================================== @@ -86,8 +122,8 @@ class WindowsDirectWriteTypeface : public Typeface { public: WindowsDirectWriteTypeface (const Font& font, IDWriteFontCollection* fontCollection) - : Typeface (font.getTypefaceName()), - ascent (0.0f) + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), + unitsToHeightScaleFactor (1.0f), heightToPointsFactor (1.0f), ascent (0.0f) { jassert (fontCollection != nullptr); @@ -102,33 +138,59 @@ public: ComSmartPtr dwFontFamily; hr = fontCollection->GetFontFamily (fontIndex, dwFontFamily.resetAndGetPointerAddress()); - // Get a specific font in the font family using certain weight and style flags + // Get a specific font in the font family using typeface style ComSmartPtr dwFont; - DWRITE_FONT_WEIGHT dwWeight = font.isBold() ? DWRITE_FONT_WEIGHT_BOLD : DWRITE_FONT_WEIGHT_NORMAL; - DWRITE_FONT_STYLE dwStyle = font.isItalic() ? DWRITE_FONT_STYLE_ITALIC : DWRITE_FONT_STYLE_NORMAL; + uint32 fontFacesCount = 0; + fontFacesCount = dwFontFamily->GetFontCount(); - hr = dwFontFamily->GetFirstMatchingFont (dwWeight, DWRITE_FONT_STRETCH_NORMAL, dwStyle, dwFont.resetAndGetPointerAddress()); + for (int i = fontFacesCount; --i >= 0;) + { + hr = dwFontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); + + if (i == 0) + break; + + ComSmartPtr faceNames; + hr = dwFont->GetFaceNames (faceNames.resetAndGetPointerAddress()); + + if (font.getTypefaceStyle() == getLocalisedName (faceNames)) + break; + } + + jassert (dwFont != nullptr); hr = dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); - DWRITE_FONT_METRICS dwFontMetrics; - dwFontFace->GetMetrics (&dwFontMetrics); + if (dwFontFace != nullptr) + { + DWRITE_FONT_METRICS dwFontMetrics; + dwFontFace->GetMetrics (&dwFontMetrics); - // All Font Metrics are in design units so we need to get designUnitsPerEm value to get the metrics - // into Em/Design Independent Pixels - designUnitsPerEm = dwFontMetrics.designUnitsPerEm; + // All Font Metrics are in design units so we need to get designUnitsPerEm value + // to get the metrics into Em/Design Independent Pixels + designUnitsPerEm = dwFontMetrics.designUnitsPerEm; - ascent = std::abs ((float) dwFontMetrics.ascent); - const float totalSize = ascent + std::abs ((float) dwFontMetrics.descent); - ascent /= totalSize; - unitsToHeightScaleFactor = 1.0f / (totalSize / designUnitsPerEm); - const float pathAscent = (((float) dwFontMetrics.ascent) / ((float) designUnitsPerEm)) * 1024.0f; - const float pathDescent = (((float) dwFontMetrics.descent) / ((float) designUnitsPerEm)) * 1024.0f; - const float pathTotalSize = std::abs (pathAscent) + std::abs (pathDescent); - pathTransform = AffineTransform::identity.scale (1.0f / pathTotalSize, 1.0f / pathTotalSize); + ascent = std::abs ((float) dwFontMetrics.ascent); + const float totalSize = ascent + std::abs ((float) dwFontMetrics.descent); + ascent /= totalSize; + unitsToHeightScaleFactor = designUnitsPerEm / totalSize; + + HDC tempDC = GetDC (0); + float dpi = (GetDeviceCaps (tempDC, LOGPIXELSX) + GetDeviceCaps (tempDC, LOGPIXELSY)) / 2.0f; + heightToPointsFactor = (dpi / GetDeviceCaps (tempDC, LOGPIXELSY)) * unitsToHeightScaleFactor; + ReleaseDC (0, tempDC); + + const float pathAscent = (1024.0f * dwFontMetrics.ascent) / designUnitsPerEm; + const float pathDescent = (1024.0f * dwFontMetrics.descent) / designUnitsPerEm; + const float pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent)); + pathTransform = AffineTransform::scale (pathScale); + } } - float getAscent() const { return ascent; } - float getDescent() const { return 1.0f - ascent; } + bool loadedOk() const noexcept { return dwFontFace != nullptr; } + + float getAscent() const { return ascent; } + float getDescent() const { return 1.0f - ascent; } + float getHeightToPointsFactor() const { return heightToPointsFactor; } float getStringWidth (const String& text) { @@ -169,17 +231,6 @@ public: } } - EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform) - { - Path path; - - if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty()) - return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0), - path, transform); - - return nullptr; - } - bool getOutlineForGlyph (int glyphNumber, Path& path) { jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty @@ -195,16 +246,19 @@ public: return true; } + IDWriteFontFace* getIDWriteFontFace() const noexcept { return dwFontFace; } + private: + SharedResourcePointer factories; ComSmartPtr dwFontFace; - float unitsToHeightScaleFactor, ascent; + float unitsToHeightScaleFactor, heightToPointsFactor, ascent; int designUnitsPerEm; AffineTransform pathTransform; class PathGeometrySink : public ComBaseClassHelper { public: - PathGeometrySink() { resetReferenceCount(); } + PathGeometrySink() : ComBaseClassHelper (0) {} void __stdcall AddBeziers (const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) { @@ -244,10 +298,10 @@ private: Path path; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathGeometrySink); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PathGeometrySink) }; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDirectWriteTypeface); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsDirectWriteTypeface) }; #endif diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp index 8d692a3..ab5dbe8 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp @@ -1,31 +1,140 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +/* This is some quick-and-dirty code to extract the typeface name from a lump of TTF file data. + It's needed because although win32 will happily load a TTF file from in-memory data, it won't + tell you the name of the damned font that it just loaded.. and in order to actually use the font, + you need to know its name!! Anyway, this awful hack seems to work for most fonts. +*/ +namespace TTFNameExtractor +{ + struct OffsetTable + { + uint32 version; + uint16 numTables, searchRange, entrySelector, rangeShift; + }; + + struct TableDirectory + { + char tag[4]; + uint32 checkSum, offset, length; + }; + + struct NamingTable + { + uint16 formatSelector; + uint16 numberOfNameRecords; + uint16 offsetStartOfStringStorage; + }; + + struct NameRecord + { + uint16 platformID, encodingID, languageID; + uint16 nameID, stringLength, offsetFromStorageArea; + }; + + static String parseNameRecord (MemoryInputStream& input, const NameRecord& nameRecord, + const int64 directoryOffset, const int64 offsetOfStringStorage) + { + String result; + const int64 oldPos = input.getPosition(); + input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea)); + const int stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength); + const int platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID); + + if (platformID == 0 || platformID == 3) + { + const int numChars = stringLength / 2 + 1; + HeapBlock buffer; + buffer.calloc (numChars + 1); + input.read (buffer, stringLength); + + for (int i = 0; i < numChars; ++i) + buffer[i] = ByteOrder::swapIfLittleEndian (buffer[i]); + + static_jassert (sizeof (CharPointer_UTF16::CharType) == sizeof (uint16)); + result = CharPointer_UTF16 ((CharPointer_UTF16::CharType*) buffer.getData()); + } + else + { + HeapBlock buffer; + buffer.calloc (stringLength + 1); + input.read (buffer, stringLength); + result = CharPointer_UTF8 (buffer.getData()); + } + + input.setPosition (oldPos); + return result; + } + + static String parseNameTable (MemoryInputStream& input, int64 directoryOffset) + { + input.setPosition (directoryOffset); + + NamingTable namingTable = { 0 }; + input.read (&namingTable, sizeof (namingTable)); + + for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (namingTable.numberOfNameRecords); ++i) + { + NameRecord nameRecord = { 0 }; + input.read (&nameRecord, sizeof (nameRecord)); + + if (ByteOrder::swapIfLittleEndian (nameRecord.nameID) == 4) + { + const String result (parseNameRecord (input, nameRecord, directoryOffset, + ByteOrder::swapIfLittleEndian (namingTable.offsetStartOfStringStorage))); + + if (result.isNotEmpty()) + return result; + } + } + + return String(); + } + + static String getTypefaceNameFromFile (MemoryInputStream& input) + { + OffsetTable offsetTable = { 0 }; + input.read (&offsetTable, sizeof (offsetTable)); + + for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i) + { + TableDirectory tableDirectory; + zerostruct (tableDirectory); + input.read (&tableDirectory, sizeof (tableDirectory)); + + if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name")) + return parseNameTable (input, ByteOrder::swapIfLittleEndian (tableDirectory.offset)); + } + + return String(); + } +} + namespace FontEnumerators { - int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) + static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) { if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0) { @@ -36,7 +145,7 @@ namespace FontEnumerators return 1; } - int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) + static int CALLBACK fontEnum1 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) { if (lpelfe != nullptr && (type & RASTER_FONTTYPE) == 0) { @@ -65,35 +174,106 @@ namespace FontEnumerators StringArray Font::findAllTypefaceNames() { StringArray results; - HDC dc = CreateCompatibleDC (0); + #if JUCE_USE_DIRECTWRITE + SharedResourcePointer factories; + + if (factories->systemFonts != nullptr) { - LOGFONTW lf = { 0 }; - lf.lfWeight = FW_DONTCARE; - lf.lfOutPrecision = OUT_OUTLINE_PRECIS; - lf.lfQuality = DEFAULT_QUALITY; - lf.lfCharSet = DEFAULT_CHARSET; - lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; - lf.lfPitchAndFamily = FF_DONTCARE; + ComSmartPtr fontFamily; + uint32 fontFamilyCount = 0; + fontFamilyCount = factories->systemFonts->GetFontFamilyCount(); - EnumFontFamiliesEx (dc, &lf, - (FONTENUMPROCW) &FontEnumerators::fontEnum1, - (LPARAM) &results, 0); + for (uint32 i = 0; i < fontFamilyCount; ++i) + { + HRESULT hr = factories->systemFonts->GetFontFamily (i, fontFamily.resetAndGetPointerAddress()); + + if (SUCCEEDED (hr)) + results.addIfNotAlreadyThere (getFontFamilyName (fontFamily)); + } } + else + #endif + { + HDC dc = CreateCompatibleDC (0); - DeleteDC (dc); + { + LOGFONTW lf = { 0 }; + lf.lfWeight = FW_DONTCARE; + lf.lfOutPrecision = OUT_OUTLINE_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfPitchAndFamily = FF_DONTCARE; + + EnumFontFamiliesEx (dc, &lf, + (FONTENUMPROCW) &FontEnumerators::fontEnum1, + (LPARAM) &results, 0); + } + + DeleteDC (dc); + } results.sort (true); return results; } -extern bool juce_IsRunningInWine(); +StringArray Font::findAllTypefaceStyles (const String& family) +{ + if (FontStyleHelpers::isPlaceholderFamilyName (family)) + return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family)); + + StringArray results; + + #if JUCE_USE_DIRECTWRITE + SharedResourcePointer factories; + + if (factories->systemFonts != nullptr) + { + BOOL fontFound = false; + uint32 fontIndex = 0; + HRESULT hr = factories->systemFonts->FindFamilyName (family.toWideCharPointer(), &fontIndex, &fontFound); + if (! fontFound) + fontIndex = 0; + + // Get the font family using the search results + // Fonts like: Times New Roman, Times New Roman Bold, Times New Roman Italic are all in the same font family + ComSmartPtr fontFamily; + hr = factories->systemFonts->GetFontFamily (fontIndex, fontFamily.resetAndGetPointerAddress()); + + // Get the font faces + ComSmartPtr dwFont; + uint32 fontFacesCount = 0; + fontFacesCount = fontFamily->GetFontCount(); + + for (uint32 i = 0; i < fontFacesCount; ++i) + { + hr = fontFamily->GetFont (i, dwFont.resetAndGetPointerAddress()); + + // Ignore any algorithmically generated bold and oblique styles.. + if (dwFont->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE) + results.addIfNotAlreadyThere (getFontFaceName (dwFont)); + } + } + else + #endif + { + results.add ("Regular"); + results.add ("Italic"); + results.add ("Bold"); + results.add ("Bold Italic"); + } + + return results; +} + +extern bool juce_isRunningInWine(); struct DefaultFontNames { DefaultFontNames() { - if (juce_IsRunningInWine()) + if (juce_isRunningInWine()) { // If we're running in Wine, then use fonts that might be available on Linux.. defaultSans = "Bitstream Vera Sans"; @@ -103,7 +283,7 @@ struct DefaultFontNames else { defaultSans = "Verdana"; - defaultSerif = "Times"; + defaultSerif = "Times New Roman"; defaultFixed = "Lucida Console"; defaultFallback = "Tahoma"; // (contains plenty of unicode characters) } @@ -116,15 +296,17 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font) { static DefaultFontNames defaultNames; - String faceName (font.getTypefaceName()); + Font newFont (font); + const String& faceName = font.getTypefaceName(); - if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans; - else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif; - else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed; + if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans); + else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif); + else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed); - Font f (font); - f.setTypefaceName (faceName); - return Typeface::createSystemTypefaceFor (f); + if (font.getTypefaceStyle() == getDefaultStyle()) + newFont.setTypefaceStyle ("Regular"); + + return Typeface::createSystemTypefaceFor (newFont); } //============================================================================== @@ -132,23 +314,29 @@ class WindowsTypeface : public Typeface { public: WindowsTypeface (const Font& font) - : Typeface (font.getTypefaceName()), - fontH (0), - previousFontH (0), - dc (CreateCompatibleDC (0)), - ascent (1.0f), - defaultGlyph (-1), - bold (font.isBold()), - italic (font.isItalic()) + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), + fontH (0), previousFontH (0), + dc (CreateCompatibleDC (0)), memoryFont (0), + ascent (1.0f), heightToPointsFactor (1.0f), + defaultGlyph (-1) { loadFont(); + } - if (GetTextMetrics (dc, &tm)) - { - ascent = tm.tmAscent / (float) tm.tmHeight; - defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar); - createKerningPairs (dc, (float) tm.tmHeight); - } + WindowsTypeface (const void* data, size_t dataSize) + : Typeface (String(), String()), + fontH (0), previousFontH (0), + dc (CreateCompatibleDC (0)), memoryFont (0), + ascent (1.0f), heightToPointsFactor (1.0f), + defaultGlyph (-1) + { + DWORD numInstalled = 0; + memoryFont = AddFontMemResourceEx (const_cast (data), (DWORD) dataSize, + nullptr, &numInstalled); + + MemoryInputStream m (data, dataSize, false); + name = TTFNameExtractor::getTypefaceNameFromFile (m); + loadFont(); } ~WindowsTypeface() @@ -158,10 +346,14 @@ public: if (fontH != 0) DeleteObject (fontH); + + if (memoryFont != 0) + RemoveFontMemResourceEx (memoryFont); } - float getAscent() const { return ascent; } - float getDescent() const { return 1.0f - ascent; } + float getAscent() const { return ascent; } + float getDescent() const { return 1.0f - ascent; } + float getHeightToPointsFactor() const { return heightToPointsFactor; } float getStringWidth (const String& text) { @@ -280,9 +472,9 @@ private: HGDIOBJ previousFontH; HDC dc; TEXTMETRIC tm; - float ascent; - int defaultGlyph; - bool bold, italic; + HANDLE memoryFont; + float ascent, heightToPointsFactor; + int defaultGlyph, heightInPoints; struct KerningPair { @@ -314,8 +506,8 @@ private: lf.lfOutPrecision = OUT_OUTLINE_PRECIS; lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; lf.lfQuality = PROOF_QUALITY; - lf.lfItalic = (BYTE) (italic ? TRUE : FALSE); - lf.lfWeight = bold ? FW_BOLD : FW_NORMAL; + lf.lfItalic = (BYTE) (style == "Italic" ? TRUE : FALSE); + lf.lfWeight = style == "Bold" ? FW_BOLD : FW_NORMAL; lf.lfHeight = -256; name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName)); @@ -330,7 +522,8 @@ private: OUTLINETEXTMETRIC otm; if (GetOutlineTextMetrics (dc, sizeof (otm), &otm) != 0) { - lf.lfHeight = -(int) otm.otmEMSquare; + heightInPoints = otm.otmEMSquare; + lf.lfHeight = -(int) heightInPoints; fontH = CreateFontIndirect (&lf); SelectObject (dc, fontH); @@ -338,6 +531,15 @@ private: } } } + + if (GetTextMetrics (dc, &tm)) + { + float dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f; + heightToPointsFactor = (dpi / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight; + ascent = tm.tmAscent / (float) tm.tmHeight; + defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar); + createKerningPairs (dc, (float) tm.tmHeight); + } } void createKerningPairs (HDC dc, const float height) @@ -409,7 +611,7 @@ private: return kerningPairs.getReference (index).kerning; } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsTypeface) }; const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 } }; @@ -417,11 +619,26 @@ const MAT2 WindowsTypeface::identityMatrix = { { 0, 1 }, { 0, 0 }, { 0, 0 }, { 0 Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { #if JUCE_USE_DIRECTWRITE - const Direct2DFactories& factories = Direct2DFactories::getInstance(); + SharedResourcePointer factories; - if (factories.systemFonts != nullptr) - return new WindowsDirectWriteTypeface (font, factories.systemFonts); - else + if (factories->systemFonts != nullptr) + { + ScopedPointer wtf (new WindowsDirectWriteTypeface (font, factories->systemFonts)); + + if (wtf->loadedOk()) + return wtf.release(); + } #endif - return new WindowsTypeface (font); + + return new WindowsTypeface (font); +} + +Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) +{ + return new WindowsTypeface (data, dataSize); +} + +void Typeface::scanFolderForFonts (const File&) +{ + jassertfalse; // not implemented on this platform } diff --git a/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.cpp b/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.cpp deleted file mode 100644 index 7692f24..0000000 --- a/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -Justification::Justification (const Justification& other) noexcept - : flags (other.flags) -{ -} - -Justification& Justification::operator= (const Justification& other) noexcept -{ - flags = other.flags; - return *this; -} - -int Justification::getOnlyVerticalFlags() const noexcept -{ - return flags & (top | bottom | verticallyCentred); -} - -int Justification::getOnlyHorizontalFlags() const noexcept -{ - return flags & (left | right | horizontallyCentred | horizontallyJustified); -} diff --git a/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.h b/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.h index d598d90..f80fa21 100644 --- a/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.h +++ b/JuceLibraryCode/modules/juce_graphics/placement/juce_Justification.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_JUSTIFICATION_JUCEHEADER__ -#define __JUCE_JUSTIFICATION_JUCEHEADER__ - -#include "../geometry/juce_Rectangle.h" +#ifndef JUCE_JUSTIFICATION_H_INCLUDED +#define JUCE_JUSTIFICATION_H_INCLUDED //============================================================================== @@ -38,37 +35,40 @@ It is used in various places wherever this kind of information is needed. */ -class JUCE_API Justification +class Justification { public: //============================================================================== - /** Creates a Justification object using a combination of flags. */ - inline Justification (int flags_) noexcept : flags (flags_) {} + /** Creates a Justification object using a combination of flags from the Flags enum. */ + Justification (int justificationFlags) noexcept : flags (justificationFlags) {} /** Creates a copy of another Justification object. */ - Justification (const Justification& other) noexcept; + Justification (const Justification& other) noexcept : flags (other.flags) {} /** Copies another Justification object. */ - Justification& operator= (const Justification& other) noexcept; + Justification& operator= (const Justification& other) noexcept + { + flags = other.flags; + return *this; + } bool operator== (const Justification& other) const noexcept { return flags == other.flags; } bool operator!= (const Justification& other) const noexcept { return flags != other.flags; } //============================================================================== /** Returns the raw flags that are set for this Justification object. */ - inline int getFlags() const noexcept { return flags; } + inline int getFlags() const noexcept { 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 noexcept { return (flags & flagsToTest) != 0; } + inline bool testFlags (int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } /** Returns just the flags from this object that deal with vertical layout. */ - int getOnlyVerticalFlags() const noexcept; + int getOnlyVerticalFlags() const noexcept { return flags & (top | bottom | verticallyCentred); } /** Returns just the flags from this object that deal with horizontal layout. */ - int getOnlyHorizontalFlags() const noexcept; + int getOnlyHorizontalFlags() const noexcept { return flags & (left | right | horizontallyCentred | horizontallyJustified); } //============================================================================== /** Adjusts the position of a rectangle to fit it into a space. @@ -103,7 +103,7 @@ public: //============================================================================== /** Flag values that can be combined and used in the constructor. */ - enum + enum Flags { //============================================================================== /** Indicates that the item should be aligned against the left edge of the available space. */ @@ -186,4 +186,4 @@ private: int flags; }; -#endif // __JUCE_JUSTIFICATION_JUCEHEADER__ +#endif // JUCE_JUSTIFICATION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp b/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp index 6281dc7..031048e 100644 --- a/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp +++ b/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -87,7 +86,7 @@ void RectanglePlacement::applyTo (double& x, double& y, double& w, double& h, } } -const AffineTransform RectanglePlacement::getTransformToFit (const Rectangle& source, const Rectangle& destination) const noexcept +AffineTransform RectanglePlacement::getTransformToFit (const Rectangle& source, const Rectangle& destination) const noexcept { if (source.isEmpty()) return AffineTransform::identity; diff --git a/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.h b/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.h index acc6709..9341e67 100644 --- a/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.h +++ b/JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ -#define __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ - -#include "../geometry/juce_AffineTransform.h" -#include "../geometry/juce_Rectangle.h" +#ifndef JUCE_RECTANGLEPLACEMENT_H_INCLUDED +#define JUCE_RECTANGLEPLACEMENT_H_INCLUDED //============================================================================== @@ -42,21 +38,24 @@ class JUCE_API RectanglePlacement { public: //============================================================================== - /** Creates a RectanglePlacement object using a combination of flags. */ - inline RectanglePlacement (int flags_) noexcept : flags (flags_) {} + /** Creates a RectanglePlacement object using a combination of flags from the Flags enum. */ + inline RectanglePlacement (int placementFlags) noexcept : flags (placementFlags) {} + + /** Creates a default RectanglePlacement object, which is equivalent to using the 'centred' flag. */ + inline RectanglePlacement() noexcept : flags (centred) {} /** Creates a copy of another RectanglePlacement object. */ - RectanglePlacement (const RectanglePlacement& other) noexcept; + RectanglePlacement (const RectanglePlacement&) noexcept; /** Copies another RectanglePlacement object. */ - RectanglePlacement& operator= (const RectanglePlacement& other) noexcept; + RectanglePlacement& operator= (const RectanglePlacement&) noexcept; - bool operator== (const RectanglePlacement& other) const noexcept; - bool operator!= (const RectanglePlacement& other) const noexcept; + bool operator== (const RectanglePlacement&) const noexcept; + bool operator!= (const RectanglePlacement&) const noexcept; //============================================================================== /** Flag values that can be combined and used in the constructor. */ - enum + enum Flags { //============================================================================== /** Indicates that the source rectangle's left edge should be aligned with the left edge of the target rectangle. */ @@ -132,7 +131,7 @@ public: //============================================================================== /** 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 source rectangle coordinates will be adjusted so that they fit into the destination rectangle based on this object's flags. */ void applyTo (double& sourceX, @@ -144,12 +143,12 @@ public: double destinationW, double destinationH) const noexcept; - /** Returns the transform that should be applied to these source co-ordinates to fit them + /** Returns the rectangle that should be used to fit the given source rectangle into the destination rectangle using the current flags. */ template - const Rectangle appliedTo (const Rectangle& source, - const Rectangle& destination) const noexcept + Rectangle appliedTo (const Rectangle& source, + const Rectangle& destination) const noexcept { double x = source.getX(), y = source.getY(), w = source.getWidth(), h = source.getHeight(); applyTo (x, y, w, h, static_cast (destination.getX()), static_cast (destination.getY()), @@ -158,11 +157,11 @@ public: static_cast (w), static_cast (h)); } - /** Returns the transform that should be applied to these source co-ordinates to fit them + /** Returns the transform that should be applied to these source coordinates to fit them into the destination rectangle using the current flags. */ - const AffineTransform getTransformToFit (const Rectangle& source, - const Rectangle& destination) const noexcept; + AffineTransform getTransformToFit (const Rectangle& source, + const Rectangle& destination) const noexcept; private: @@ -170,4 +169,4 @@ private: int flags; }; -#endif // __JUCE_RECTANGLEPLACEMENT_JUCEHEADER__ +#endif // JUCE_RECTANGLEPLACEMENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp index e0f8961..4e21873 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp @@ -1,112 +1,56 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if JUCE_MAC - extern void juce_initialiseMacMainMenu(); -#endif +JUCEApplication::JUCEApplication() {} +JUCEApplication::~JUCEApplication() {} //============================================================================== -class AppBroadcastCallback : public ActionListener -{ -public: - AppBroadcastCallback() { MessageManager::getInstance()->registerBroadcastListener (this); } - ~AppBroadcastCallback() { MessageManager::getInstance()->deregisterBroadcastListener (this); } - - void actionListenerCallback (const String& message) - { - JUCEApplication* const app = JUCEApplication::getInstance(); - - if (app != 0 && message.startsWith (app->getApplicationName() + "/")) - app->anotherInstanceStarted (message.substring (app->getApplicationName().length() + 1)); - } -}; - -//============================================================================== -JUCEApplication::JUCEApplication() - : appReturnValue (0), - stillInitialising (true) +JUCEApplication* JUCE_CALLTYPE JUCEApplication::getInstance() noexcept { + return dynamic_cast (JUCEApplicationBase::getInstance()); } -JUCEApplication::~JUCEApplication() -{ - if (appLock != nullptr) - { - appLock->exit(); - appLock = nullptr; - } -} +bool JUCEApplication::moreThanOneInstanceAllowed() { return true; } +void JUCEApplication::anotherInstanceStarted (const String&) {} -//============================================================================== -bool JUCEApplication::moreThanOneInstanceAllowed() -{ - return true; -} +void JUCEApplication::suspended() {} +void JUCEApplication::resumed() {} -void JUCEApplication::anotherInstanceStarted (const String&) -{ -} +void JUCEApplication::systemRequestedQuit() { quit(); } -void JUCEApplication::systemRequestedQuit() -{ - quit(); -} - -void JUCEApplication::quit() -{ - MessageManager::getInstance()->stopDispatchLoop(); -} - -void JUCEApplication::setApplicationReturnValue (const int newReturnValue) noexcept -{ - appReturnValue = newReturnValue; -} - -//============================================================================== -void JUCEApplication::unhandledException (const std::exception*, - const String&, - const int) +void JUCEApplication::unhandledException (const std::exception*, const String&, int) { jassertfalse; } -void JUCEApplication::sendUnhandledException (const std::exception* const e, - const char* const sourceFile, - const int lineNumber) -{ - if (JUCEApplicationBase::getInstance() != nullptr) - JUCEApplicationBase::getInstance()->unhandledException (e, sourceFile, lineNumber); -} - //============================================================================== ApplicationCommandTarget* JUCEApplication::getNextCommandTarget() { return nullptr; } -void JUCEApplication::getAllCommands (Array & commands) +void JUCEApplication::getAllCommands (Array& commands) { commands.add (StandardApplicationCommandIDs::quit); } @@ -117,8 +61,7 @@ void JUCEApplication::getCommandInfo (const CommandID commandID, ApplicationComm { result.setInfo (TRANS("Quit"), TRANS("Quits the application"), - "Application", - 0); + "Application", 0); result.defaultKeypresses.add (KeyPress ('q', ModifierKeys::commandModifier, 0)); } @@ -136,127 +79,20 @@ bool JUCEApplication::perform (const InvocationInfo& info) } //============================================================================== -bool JUCEApplication::initialiseApp (const String& commandLine) -{ - commandLineParameters = commandLine.trim(); - - #if ! (JUCE_IOS || JUCE_ANDROID) - jassert (appLock == nullptr); // initialiseApp must only be called once! - - if (! moreThanOneInstanceAllowed()) - { - appLock = new InterProcessLock ("juceAppLock_" + getApplicationName()); - - if (! appLock->enter(0)) - { - appLock = nullptr; - MessageManager::broadcastMessage (getApplicationName() + "/" + commandLineParameters); - - DBG ("Another instance is running - quitting..."); - return false; - } - } - #endif - - // let the app do its setting-up.. - initialise (commandLineParameters); - - #if JUCE_MAC - juce_initialiseMacMainMenu(); // needs to be called after the app object has created, to get its name - #endif - - #if ! (JUCE_IOS || JUCE_ANDROID) - broadcastCallback = new AppBroadcastCallback(); - #endif - - stillInitialising = false; - return true; -} - -int JUCEApplication::shutdownApp() -{ - jassert (JUCEApplicationBase::getInstance() == this); - - broadcastCallback = nullptr; - - JUCE_TRY - { - // give the app a chance to clean up.. - shutdown(); - } - JUCE_CATCH_EXCEPTION - - return getApplicationReturnValue(); -} - -//============================================================================== -#if ! JUCE_ANDROID -int JUCEApplication::main (const String& commandLine) -{ - ScopedJuceInitialiser_GUI libraryInitialiser; - jassert (createInstance != nullptr); - int returnCode = 0; - - { - const ScopedPointer app (dynamic_cast (createInstance())); - - jassert (app != nullptr); - - if (! app->initialiseApp (commandLine)) - return 0; - - JUCE_TRY - { - // loop until a quit message is received.. - MessageManager::getInstance()->runDispatchLoop(); - } - JUCE_CATCH_EXCEPTION - - returnCode = app->shutdownApp(); - } - - return returnCode; -} - -#if JUCE_IOS - extern int juce_iOSMain (int argc, const char* argv[]); -#endif - -#if ! JUCE_WINDOWS - extern const char* juce_Argv0; -#endif - #if JUCE_MAC - extern void initialiseNSApplication(); + extern void juce_initialiseMacMainMenu(); #endif -int JUCEApplication::main (int argc, const char* argv[]) +bool JUCEApplication::initialiseApp() { - JUCE_AUTORELEASEPOOL - - #if JUCE_MAC - initialiseNSApplication(); - #endif - - #if ! JUCE_WINDOWS - jassert (createInstance != nullptr); - juce_Argv0 = argv[0]; - #endif - - #if JUCE_IOS - return juce_iOSMain (argc, argv); - #else - String cmd; - for (int i = 1; i < argc; ++i) + if (JUCEApplicationBase::initialiseApp()) { - String arg (argv[i]); - if (arg.containsChar (' ') && ! arg.isQuotedString()) - arg = arg.quoted ('"'); + #if JUCE_MAC + juce_initialiseMacMainMenu(); // (needs to get the app's name) + #endif - cmd << arg << ' '; + return true; } - return JUCEApplication::main (cmd); - #endif + return false; } -#endif diff --git a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.h b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.h index 2616584..1ffb27e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.h +++ b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_APPLICATION_JUCEHEADER__ -#define __JUCE_APPLICATION_JUCEHEADER__ +#ifndef JUCE_APPLICATION_H_INCLUDED +#define JUCE_APPLICATION_H_INCLUDED //============================================================================== @@ -32,24 +31,27 @@ 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. + Any application that wants to run an event loop must declare a subclass of + JUCEApplicationBase or 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. + It then needs to use the START_JUCE_APPLICATION macro somewhere in a CPP file + to declare an instance of this class and generate suitable platform-specific + boilerplate code to launch the app. + + Note that this class is derived from JUCEApplicationBase, which contains most + of the useful methods and functionality. This derived class is here simply as + a convenient way to also inherit from an ApplicationCommandTarget, and to implement + default versions of some of the pure virtual base class methods. But you can derive + your app object directly from JUCEApplicationBase if you want to, and by doing so + can avoid having a dependency on the juce_gui_basics module. e.g. @code class MyJUCEApp : public JUCEApplication { public: - MyJUCEApp() - { - } - - ~MyJUCEApp() - { - } + MyJUCEApp() {} + ~MyJUCEApp() {} void initialise (const String& commandLine) { @@ -60,7 +62,7 @@ void shutdown() { - myMainWindow = 0; + myMainWindow = nullptr; } const String getApplicationName() @@ -74,19 +76,19 @@ } private: - ScopedPointer myMainWindow; + ScopedPointer myMainWindow; }; - // this creates wrapper code to actually launch the app properly. + // this generates boilerplate code to launch our app class: START_JUCE_APPLICATION (MyJUCEApp) @endcode - @see MessageManager + @see JUCEApplicationBase, START_JUCE_APPLICATION */ class JUCE_API JUCEApplication : public JUCEApplicationBase, public ApplicationCommandTarget { -protected: +public: //============================================================================== /** Constructs a JUCE app object. @@ -96,37 +98,23 @@ protected: */ 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(); + ~JUCEApplication(); //============================================================================== /** Returns the global instance of the application object being run. */ - static JUCEApplication* getInstance() noexcept { return dynamic_cast (JUCEApplicationBase::getInstance()); } + static JUCEApplication* JUCE_CALLTYPE getInstance() noexcept; //============================================================================== - /** 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 noexcept { return stillInitialising; } - - //============================================================================== - /** Returns the application's name. - - An application must implement this to name itself. - */ + /** Returns the application's name. */ virtual const String getApplicationName() = 0; - /** Returns the application's version number. - */ + /** Returns the application's version number. */ virtual const String getApplicationVersion() = 0; /** Checks whether multiple instances of the app are allowed. @@ -139,13 +127,12 @@ public: 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(); + bool moreThanOneInstanceAllowed() override; /** 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); + void anotherInstanceStarted (const String& commandLine) override; /** Called when the operating system is trying to close the application. @@ -159,7 +146,17 @@ public: 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(); + void systemRequestedQuit() override; + + /** This method is called when the application is being put into background mode + by the operating system. + */ + void suspended() override; + + /** This method is called when the application is being woken from background mode + by the operating system. + */ + void resumed() override; /** 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 @@ -169,47 +166,9 @@ public: 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 - */ - 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) noexcept; - - /** Returns the value that has been set as the application's exit code. - @see setApplicationReturnValue - */ - int getApplicationReturnValue() const noexcept { return appReturnValue; } - - /** Returns the application's command line parameters. */ - const String& getCommandLineParameters() const noexcept { return commandLineParameters; } - - /** Returns true if this executable is running as an app (as opposed to being a plugin - or other kind of shared library. */ - static inline bool isStandaloneApp() noexcept { return createInstance != nullptr; } + void unhandledException (const std::exception* e, + const String& sourceFilename, + int lineNumber) override; //============================================================================== /** @internal */ @@ -217,30 +176,15 @@ public: /** @internal */ void getCommandInfo (CommandID, ApplicationCommandInfo&); /** @internal */ - void getAllCommands (Array &); + void getAllCommands (Array&); /** @internal */ bool perform (const InvocationInfo&); - //============================================================================== - #ifndef DOXYGEN - // The following methods are internal calls - not for public use. - static int main (const String& commandLine); - static int main (int argc, const char* argv[]); - static void sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber); - bool initialiseApp (const String& commandLine); - int shutdownApp(); - #endif - private: - //============================================================================== - String commandLineParameters; - ScopedPointer appLock; - ScopedPointer broadcastCallback; - int appReturnValue; - bool stillInitialising; + bool initialiseApp() override; - JUCE_DECLARE_NON_COPYABLE (JUCEApplication); + JUCE_DECLARE_NON_COPYABLE (JUCEApplication) }; -#endif // __JUCE_APPLICATION_JUCEHEADER__ +#endif // JUCE_APPLICATION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Initialisation.h b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Initialisation.h deleted file mode 100644 index ff03508..0000000 --- a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Initialisation.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -#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 - Process::setCurrentModuleInstanceHandle() method. - - @see shutdownJuce_GUI() -*/ -JUCE_API void JUCE_CALLTYPE 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() -*/ -JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI(); - - -//============================================================================== -/** 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(); } -}; - - -//============================================================================== -/* - 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 JUCE_ANDROID - #define START_JUCE_APPLICATION(AppClass) \ - juce::JUCEApplication* juce_CreateApplication() { return new AppClass(); } - -#elif defined (JUCE_GCC) || defined (__MWERKS__) - - #define START_JUCE_APPLICATION(AppClass) \ - static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ - int main (int argc, char* argv[]) \ - { \ - juce::JUCEApplication::createInstance = &juce_CreateApplication; \ - return juce::JUCEApplication::main (argc, (const char**) argv); \ - } - -#elif JUCE_WINDOWS - - #ifdef _CONSOLE - #define START_JUCE_APPLICATION(AppClass) \ - static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ - int main (int, char* argv[]) \ - { \ - juce::JUCEApplication::createInstance = &juce_CreateApplication; \ - return juce::JUCEApplication::main (juce::Process::getCurrentCommandLineParams()); \ - } - #elif ! defined (_AFXDLL) - #ifdef _WINDOWS_ - #define START_JUCE_APPLICATION(AppClass) \ - static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ - int WINAPI WinMain (HINSTANCE, HINSTANCE, LPSTR, int) \ - { \ - juce::JUCEApplication::createInstance = &juce_CreateApplication; \ - return juce::JUCEApplication::main (juce::Process::getCurrentCommandLineParams()); \ - } - #else - #define START_JUCE_APPLICATION(AppClass) \ - static juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \ - int __stdcall WinMain (void*, void*, const char*, int) \ - { \ - juce::JUCEApplication::createInstance = &juce_CreateApplication; \ - return juce::JUCEApplication::main (juce::Process::getCurrentCommandLineParams()); \ - } - #endif - #endif - -#endif - - -#endif // __JUCE_INITIALISATION_JUCEHEADER__ diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp index 1a655e0..75f8590 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.cpp @@ -1,71 +1,45 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -ArrowButton::ArrowButton (const String& name, - float arrowDirectionInRadians, - const Colour& arrowColour) - : Button (name), - colour (arrowColour) +ArrowButton::ArrowButton (const String& name, float arrowDirectionInRadians, Colour arrowColour) + : Button (name), colour (arrowColour) { - path.lineTo (0.0f, 1.0f); - path.lineTo (1.0f, 0.5f); - path.closeSubPath(); - - path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, - 0.5f, 0.5f)); - - setComponentEffect (&shadow); - updateShadowAndOffset(); + path.addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); + path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * arrowDirectionInRadians, 0.5f, 0.5f)); } -ArrowButton::~ArrowButton() -{ -} +ArrowButton::~ArrowButton() {} -void ArrowButton::paintButton (Graphics& g, - bool /*isMouseOverButton*/, - bool /*isButtonDown*/) +void ArrowButton::paintButton (Graphics& g, bool /*isMouseOverButton*/, bool isButtonDown) { + Path p (path); + + const float offset = isButtonDown ? 1.0f : 0.0f; + p.applyTransform (path.getTransformToScaleToFit (offset, offset, getWidth() - 3.0f, getHeight() - 3.0f, false)); + + DropShadow (Colours::black.withAlpha (0.3f), isButtonDown ? 2 : 4, Point()).drawForPath (g, p); + g.setColour (colour); - - g.fillPath (path, path.getTransformToScaleToFit ((float) offset, - (float) offset, - (float) (getWidth() - 3), - (float) (getHeight() - 3), - false)); -} - -void ArrowButton::buttonStateChanged() -{ - updateShadowAndOffset(); -} - -void ArrowButton::updateShadowAndOffset() -{ - offset = (isDown()) ? 1 : 0; - - shadow.setShadowProperties ((isDown()) ? 1.2f : 3.0f, - 0.3f, -1, 0); + g.fillPath (p); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.h index 54f771f..5d64123 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ArrowButton.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_ARROWBUTTON_JUCEHEADER__ -#define __JUCE_ARROWBUTTON_JUCEHEADER__ - -#include "juce_Button.h" +#ifndef JUCE_ARROWBUTTON_H_INCLUDED +#define JUCE_ARROWBUTTON_H_INCLUDED //============================================================================== @@ -48,33 +45,20 @@ public: */ ArrowButton (const String& buttonName, float arrowDirection, - const Colour& arrowColour); + Colour arrowColour); /** Destructor. */ ~ArrowButton(); - -protected: - //============================================================================== /** @internal */ - void paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown); - - /** @internal */ - void buttonStateChanged(); + void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; private: - //============================================================================== Colour colour; - DropShadowEffect shadow; Path path; - int offset; - void updateShadowAndOffset(); - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArrowButton); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ArrowButton) }; -#endif // __JUCE_ARROWBUTTON_JUCEHEADER__ +#endif // JUCE_ARROWBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp index f5cffc1..be7e55c 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -1,38 +1,73 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -class Button::RepeatTimer : public Timer +class Button::CallbackHelper : public Timer, + public ApplicationCommandManagerListener, + public ValueListener, + public KeyListener { public: - RepeatTimer (Button& owner_) : owner (owner_) {} - void timerCallback() { owner.repeatTimerCallback(); } + CallbackHelper (Button& b) : button (b) {} + + void timerCallback() override + { + button.repeatTimerCallback(); + } + + bool keyStateChanged (bool, Component*) override + { + return button.keyStateChangedCallback(); + } + + void valueChanged (Value& value) override + { + if (value.refersToSameSourceAs (button.isOn)) + button.setToggleState (button.isOn.getValue(), sendNotification); + } + + bool keyPressed (const KeyPress&, Component*) override + { + // returning true will avoid forwarding events for keys that we're using as shortcuts + return button.isShortcutPressed(); + } + + void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) override + { + if (info.commandID == button.commandID + && (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) == 0) + button.flashButtonState(); + } + + void applicationCommandListChanged() override + { + button.applicationCommandListChangeCallback(); + } private: - Button& owner; + Button& button; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RepeatTimer); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHelper) }; //============================================================================== @@ -46,8 +81,8 @@ Button::Button (const String& name) autoRepeatSpeed (0), autoRepeatMinimumDelay (-1), radioGroupId (0), - commandID (0), connectedEdgeFlags (0), + commandID(), buttonState (buttonNormal), lastToggleState (false), clickTogglesState (false), @@ -57,19 +92,21 @@ Button::Button (const String& name) triggerOnMouseDown (false), generateTooltip (false) { + callbackHelper = new CallbackHelper (*this); + setWantsKeyboardFocus (true); - isOn.addListener (this); + isOn.addListener (callbackHelper); } Button::~Button() { - isOn.removeListener (this); + clearShortcuts(); if (commandManagerToUse != nullptr) - commandManagerToUse->removeListener (this); + commandManagerToUse->removeListener (callbackHelper); - repeatTimer = nullptr; - clearShortcuts(); + isOn.removeListener (callbackHelper); + callbackHelper = nullptr; } //============================================================================== @@ -94,7 +131,7 @@ String Button::getTooltip() { String tt (commandManagerToUse->getDescriptionOfCommand (commandID)); - Array keyPresses (commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID)); + Array keyPresses (commandManagerToUse->getKeyMappings()->getKeyPressesAssignedToCommand (commandID)); for (int i = 0; i < keyPresses.size(); ++i) { @@ -114,49 +151,59 @@ String Button::getTooltip() return SettableTooltipClient::getTooltip(); } -void Button::setConnectedEdges (const int connectedEdgeFlags_) +void Button::setConnectedEdges (const int newFlags) { - if (connectedEdgeFlags != connectedEdgeFlags_) + if (connectedEdgeFlags != newFlags) { - connectedEdgeFlags = connectedEdgeFlags_; + connectedEdgeFlags = newFlags; repaint(); } } //============================================================================== -void Button::setToggleState (const bool shouldBeOn, - const bool sendChangeNotification) +void Button::setToggleState (const bool shouldBeOn, const NotificationType notification) { if (shouldBeOn != lastToggleState) { + WeakReference deletionWatcher (this); + + if (shouldBeOn) + { + turnOffOtherButtonsInGroup (notification); + + if (deletionWatcher == nullptr) + return; + } + if (getToggleState() != shouldBeOn) // this test means that if the value is void rather than explicitly set to isOn = shouldBeOn; // false, it won't be changed unless the required value is true. lastToggleState = shouldBeOn; repaint(); - WeakReference deletionWatcher (this); - - if (sendChangeNotification) + if (notification != dontSendNotification) { - sendClickMessage (ModifierKeys()); + // async callbacks aren't possible here + jassert (notification != sendNotificationAsync); + + sendClickMessage (ModifierKeys::getCurrentModifiers()); if (deletionWatcher == nullptr) return; } - if (lastToggleState) - { - turnOffOtherButtonsInGroup (sendChangeNotification); - - if (deletionWatcher == nullptr) - return; - } - - sendStateMessage(); + if (notification != dontSendNotification) + sendStateMessage(); + else + buttonStateChanged(); } } +void Button::setToggleState (const bool shouldBeOn, bool sendChange) +{ + setToggleState (shouldBeOn, sendChange ? sendNotification : dontSendNotification); +} + void Button::setClickingTogglesState (const bool shouldToggle) noexcept { clickTogglesState = shouldToggle; @@ -173,45 +220,41 @@ bool Button::getClickingTogglesState() const noexcept return clickTogglesState; } -void Button::valueChanged (Value& value) -{ - if (value.refersToSameSourceAs (isOn)) - setToggleState (isOn.getValue(), true); -} - -void Button::setRadioGroupId (const int newGroupId) +void Button::setRadioGroupId (const int newGroupId, NotificationType notification) { if (radioGroupId != newGroupId) { radioGroupId = newGroupId; if (lastToggleState) - turnOffOtherButtonsInGroup (true); + turnOffOtherButtonsInGroup (notification); } } -void Button::turnOffOtherButtonsInGroup (const bool sendChangeNotification) +void Button::turnOffOtherButtonsInGroup (const NotificationType notification) { - Component* const p = getParentComponent(); - - if (p != nullptr && radioGroupId != 0) + if (Component* const p = getParentComponent()) { - WeakReference deletionWatcher (this); - - for (int i = p->getNumChildComponents(); --i >= 0;) + if (radioGroupId != 0) { - Component* const c = p->getChildComponent (i); + WeakReference deletionWatcher (this); - if (c != this) + for (int i = p->getNumChildComponents(); --i >= 0;) { - Button* const b = dynamic_cast (c); + Component* const c = p->getChildComponent (i); - if (b != nullptr && b->getRadioGroupId() == radioGroupId) + if (c != this) { - b->setToggleState (false, sendChangeNotification); + if (Button* const b = dynamic_cast (c)) + { + if (b->getRadioGroupId() == radioGroupId) + { + b->setToggleState (false, notification); - if (deletionWatcher == nullptr) - return; + if (deletionWatcher == nullptr) + return; + } + } } } } @@ -263,19 +306,10 @@ void Button::setState (const ButtonState newState) } } -bool Button::isDown() const noexcept -{ - return buttonState == buttonDown; -} +bool Button::isDown() const noexcept { return buttonState == buttonDown; } +bool Button::isOver() const noexcept { return buttonState != buttonNormal; } -bool Button::isOver() const noexcept -{ - return buttonState != buttonNormal; -} - -void Button::buttonStateChanged() -{ -} +void Button::buttonStateChanged() {} uint32 Button::getMillisecondsSinceButtonDown() const noexcept { @@ -293,7 +327,7 @@ void Button::clicked() { } -void Button::clicked (const ModifierKeys& /*modifiers*/) +void Button::clicked (const ModifierKeys&) { clicked(); } @@ -308,7 +342,15 @@ void Button::triggerClick() void Button::internalClickCallback (const ModifierKeys& modifiers) { if (clickTogglesState) - setToggleState ((radioGroupId != 0) || ! lastToggleState, false); + { + const bool shouldBeOn = (radioGroupId != 0 || ! lastToggleState); + + if (shouldBeOn != getToggleState()) + { + setToggleState (shouldBeOn, sendNotification); + return; + } + } sendClickMessage (modifiers); } @@ -319,7 +361,7 @@ void Button::flashButtonState() { needsToRelease = true; setState (buttonDown); - getRepeatTimer().startTimer (100); + callbackHelper->startTimer (100); } } @@ -350,9 +392,6 @@ void Button::removeListener (ButtonListener* const listener) buttonListeners.remove (listener); } -void Button::addButtonListener (ButtonListener* l) { addListener (l); } -void Button::removeButtonListener (ButtonListener* l) { removeListener (l); } - void Button::sendClickMessage (const ModifierKeys& modifiers) { Component::BailOutChecker checker (this); @@ -395,15 +434,8 @@ void Button::paint (Graphics& g) } //============================================================================== -void Button::mouseEnter (const MouseEvent&) -{ - updateState (true, false); -} - -void Button::mouseExit (const MouseEvent&) -{ - updateState (false, false); -} +void Button::mouseEnter (const MouseEvent&) { updateState (true, false); } +void Button::mouseExit (const MouseEvent&) { updateState (false, false); } void Button::mouseDown (const MouseEvent& e) { @@ -412,7 +444,7 @@ void Button::mouseDown (const MouseEvent& e) if (isDown()) { if (autoRepeatDelay >= 0) - getRepeatTimer().startTimer (autoRepeatDelay); + callbackHelper->startTimer (autoRepeatDelay); if (triggerOnMouseDown) internalClickCallback (e.mods); @@ -422,9 +454,10 @@ void Button::mouseDown (const MouseEvent& e) void Button::mouseUp (const MouseEvent& e) { const bool wasDown = isDown(); + const bool wasOver = isOver(); updateState (isMouseOver(), false); - if (wasDown && isOver() && ! triggerOnMouseDown) + if (wasDown && wasOver && ! triggerOnMouseDown) internalClickCallback (e.mods); } @@ -434,7 +467,7 @@ void Button::mouseDrag (const MouseEvent&) updateState (isMouseOver(), true); if (autoRepeatDelay >= 0 && buttonState != oldState && isDown()) - getRepeatTimer().startTimer (autoRepeatSpeed); + callbackHelper->startTimer (autoRepeatSpeed); } void Button::focusGained (FocusChangeType) @@ -462,32 +495,31 @@ void Button::parentHierarchyChanged() if (newKeySource != keySource.get()) { if (keySource != nullptr) - keySource->removeKeyListener (this); + keySource->removeKeyListener (callbackHelper); keySource = newKeySource; if (keySource != nullptr) - keySource->addKeyListener (this); + keySource->addKeyListener (callbackHelper); } } //============================================================================== -void Button::setCommandToTrigger (ApplicationCommandManager* const commandManagerToUse_, - const int commandID_, - const bool generateTooltip_) +void Button::setCommandToTrigger (ApplicationCommandManager* const newCommandManager, + const CommandID newCommandID, const bool generateTip) { - commandID = commandID_; - generateTooltip = generateTooltip_; + commandID = newCommandID; + generateTooltip = generateTip; - if (commandManagerToUse != commandManagerToUse_) + if (commandManagerToUse != newCommandManager) { if (commandManagerToUse != nullptr) - commandManagerToUse->removeListener (this); + commandManagerToUse->removeListener (callbackHelper); - commandManagerToUse = commandManagerToUse_; + commandManagerToUse = newCommandManager; if (commandManagerToUse != nullptr) - commandManagerToUse->addListener (this); + commandManagerToUse->addListener (callbackHelper); // if you've got clickTogglesState turned on, you shouldn't also connect the button // up to be a command invoker. Instead, your command handler must flip the state of whatever @@ -497,32 +529,26 @@ void Button::setCommandToTrigger (ApplicationCommandManager* const commandManage } if (commandManagerToUse != nullptr) - applicationCommandListChanged(); + applicationCommandListChangeCallback(); else setEnabled (true); } -void Button::applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) -{ - if (info.commandID == commandID - && (info.commandFlags & ApplicationCommandInfo::dontTriggerVisualFeedback) == 0) - { - flashButtonState(); - } -} - -void Button::applicationCommandListChanged() +void Button::applicationCommandListChangeCallback() { if (commandManagerToUse != nullptr) { ApplicationCommandInfo info (0); - ApplicationCommandTarget* const target = commandManagerToUse->getTargetForCommand (commandID, info); - - setEnabled (target != nullptr && (info.flags & ApplicationCommandInfo::isDisabled) == 0); - - if (target != nullptr) - setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, false); + if (commandManagerToUse->getTargetForCommand (commandID, info) != nullptr) + { + setEnabled ((info.flags & ApplicationCommandInfo::isDisabled) == 0); + setToggleState ((info.flags & ApplicationCommandInfo::isTicked) != 0, dontSendNotification); + } + else + { + setEnabled (false); + } } } @@ -541,18 +567,15 @@ void Button::addShortcut (const KeyPress& key) void Button::clearShortcuts() { shortcuts.clear(); - parentHierarchyChanged(); } bool Button::isShortcutPressed() const { - if (! isCurrentlyBlockedByAnotherModalComponent()) - { + if (isShowing() && ! isCurrentlyBlockedByAnotherModalComponent()) for (int i = shortcuts.size(); --i >= 0;) if (shortcuts.getReference(i).isCurrentlyDown()) return true; - } return false; } @@ -566,7 +589,7 @@ bool Button::isRegisteredForShortcut (const KeyPress& key) const return false; } -bool Button::keyStateChanged (const bool, Component*) +bool Button::keyStateChangedCallback() { if (! isEnabled()) return false; @@ -575,7 +598,7 @@ bool Button::keyStateChanged (const bool, Component*) isKeyDown = isShortcutPressed(); if (autoRepeatDelay >= 0 && (isKeyDown && ! wasDown)) - getRepeatTimer().startTimer (autoRepeatDelay); + callbackHelper->startTimer (autoRepeatDelay); updateState(); @@ -590,12 +613,6 @@ bool Button::keyStateChanged (const bool, Component*) return wasDown || isKeyDown; } -bool Button::keyPressed (const KeyPress&, Component*) -{ - // returning true will avoid forwarding events for keys that we're using as shortcuts - return isShortcutPressed(); -} - bool Button::keyPressed (const KeyPress& key) { if (isEnabled() && key.isKeyCode (KeyPress::returnKey)) @@ -621,7 +638,7 @@ void Button::repeatTimerCallback() { if (needsRepainting) { - getRepeatTimer().stopTimer(); + callbackHelper->stopTimer(); updateState(); needsRepainting = false; } @@ -646,46 +663,12 @@ void Button::repeatTimerCallback() repeatSpeed = jmax (1, repeatSpeed / 2); lastRepeatTime = now; - getRepeatTimer().startTimer (repeatSpeed); + callbackHelper->startTimer (repeatSpeed); internalClickCallback (ModifierKeys::getCurrentModifiers()); } else if (! needsToRelease) { - getRepeatTimer().stopTimer(); + callbackHelper->stopTimer(); } } - -Button::RepeatTimer& Button::getRepeatTimer() -{ - if (repeatTimer == nullptr) - repeatTimer = new RepeatTimer (*this); - - return *repeatTimer; -} - -const Identifier Button::Ids::text ("text"); -const Identifier Button::Ids::radioGroup ("radioGroup"); -const Identifier Button::Ids::connectedLeft ("connectedLeft"); -const Identifier Button::Ids::connectedRight ("connectedRight"); -const Identifier Button::Ids::connectedTop ("connectedTop"); -const Identifier Button::Ids::connectedBottom ("connectedBottom"); - -void Button::refreshFromValueTree (const ValueTree& state, ComponentBuilder&) -{ - ComponentBuilder::refreshBasicComponentProperties (*this, state); - - setButtonText (state [Ids::text].toString()); - setRadioGroupId (state [Ids::radioGroup]); - setConnectedEdges (getConnectedFlags (state)); -} - -int Button::getConnectedFlags (const ValueTree& state) -{ - int connected = 0; - if (state [Button::Ids::connectedLeft]) connected |= Button::ConnectedOnLeft; - if (state [Button::Ids::connectedRight]) connected |= Button::ConnectedOnRight; - if (state [Button::Ids::connectedTop]) connected |= Button::ConnectedOnTop; - if (state [Button::Ids::connectedBottom]) connected |= Button::ConnectedOnBottom; - return connected; -} diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h index 973f9d6..2ed85c2 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.h @@ -1,36 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_BUTTON_JUCEHEADER__ -#define __JUCE_BUTTON_JUCEHEADER__ - -#include "../components/juce_Component.h" -#include "../keyboard/juce_KeyListener.h" -#include "../commands/juce_ApplicationCommandManager.h" -#include "../windows/juce_TooltipWindow.h" -#include "../layout/juce_ComponentBuilder.h" +#ifndef JUCE_BUTTON_H_INCLUDED +#define JUCE_BUTTON_H_INCLUDED //============================================================================== @@ -44,18 +37,15 @@ @see TextButton, DrawableButton, ToggleButton */ class JUCE_API Button : public Component, - public SettableTooltipClient, - public ApplicationCommandManagerListener, - public ValueListener, - private KeyListener + public SettableTooltipClient { 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) + @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); @@ -65,28 +55,23 @@ public: //============================================================================== /** 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. - + /** Returns true if the button is currently being held down. @see isOver */ bool isDown() const noexcept; /** Returns true if the mouse is currently over the button. - - This will be also be true if the mouse is being held down. - + This will be also be true if the button is being held down. @see isDown */ bool isOver() const noexcept; @@ -98,17 +83,16 @@ public: 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 + @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 notification determines the behaviour if the value changes - this + can invoke a synchronous call to clicked(), but + sendNotificationAsync is not supported @see getToggleState, setRadioGroupId */ - void setToggleState (bool shouldBeOn, - bool sendChangeNotification); + void setToggleState (bool shouldBeOn, NotificationType notification); /** Returns true if the button is 'on'. @@ -126,7 +110,7 @@ public: your own Value object. @see getToggleState, Value */ - Value& getToggleStateValue() { return isOn; } + Value& getToggleStateValue() noexcept { return isOn; } /** This tells the button to automatically flip the toggle state when the button is clicked. @@ -134,10 +118,9 @@ public: If set to true, then before the clicked() callback occurs, the toggle-state of the button is flipped. */ - void setClickingTogglesState (bool shouldToggle) noexcept; + void setClickingTogglesState (bool shouldAutoToggleOnClick) noexcept; /** 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 noexcept; @@ -158,12 +141,14 @@ public: Set the group ID back to zero if you want it to act as a normal toggle button again. + The notification argument lets you specify how other buttons should react + to being turned on or off in response to this call. + @see getRadioGroupId */ - void setRadioGroupId (int newGroupId); + void setRadioGroupId (int newGroupId, NotificationType notification = sendNotification); /** Returns the ID of the group to which this button belongs. - (See setRadioGroupId() for an explanation of this). */ int getRadioGroupId() const noexcept { return radioGroupId; } @@ -178,13 +163,13 @@ public: { public: /** Destructor. */ - virtual ~Listener() {} + virtual ~Listener() {} /** Called when the button is clicked. */ - virtual void buttonClicked (Button* button) = 0; + virtual void buttonClicked (Button*) = 0; /** Called when the button's state changes. */ - virtual void buttonStateChanged (Button*) {} + virtual void buttonStateChanged (Button*) {} }; /** Registers a listener to receive events when this button's state changes. @@ -216,7 +201,7 @@ public: 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. + pass nullptr as the command manager. 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. @@ -224,12 +209,11 @@ public: @see addShortcut, getCommandID */ void setCommandToTrigger (ApplicationCommandManager* commandManagerToUse, - int commandID, + CommandID commandID, bool generateTooltip); - /** Returns the command ID that was set by setCommandToTrigger(). - */ - int getCommandID() const noexcept { return commandID; } + /** Returns the command ID that was set by setCommandToTrigger(). */ + CommandID getCommandID() const noexcept { return commandID; } //============================================================================== /** Assigns a shortcut key to trigger the button. @@ -241,19 +225,17 @@ public: @see clearShortcuts */ - void addShortcut (const KeyPress& key); + void addShortcut (const KeyPress&); /** 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; + bool isRegisteredForShortcut (const KeyPress&) const; //============================================================================== /** Sets an auto-repeat speed for the button when it is held down. @@ -291,18 +273,18 @@ public: //============================================================================== /** Sets the tooltip for this button. - @see TooltipClient, TooltipWindow */ - void setTooltip (const String& newTooltip); + void setTooltip (const String& newTooltip) override; - // (implementation of the TooltipClient method) - String getTooltip(); + /** Returns the tooltip set by setTooltip(), or the description corresponding to + the currently mapped command if one is enabled (see setCommandToTrigger). + */ + String getTooltip() override; //============================================================================== - /** A combination of these flags are used by setConnectedEdges(). - */ + /** A combination of these flags are used by setConnectedEdges(). */ enum ConnectedEdgeFlags { ConnectedOnLeft = 1, @@ -325,27 +307,27 @@ public: void setConnectedEdges (int connectedEdgeFlags); /** Returns the set of flags passed into setConnectedEdges(). */ - int getConnectedEdgeFlags() const noexcept { return connectedEdgeFlags; } + int getConnectedEdgeFlags() const noexcept { return connectedEdgeFlags; } /** Indicates whether the button adjoins another one on its left edge. @see setConnectedEdges */ - bool isConnectedOnLeft() const noexcept { return (connectedEdgeFlags & ConnectedOnLeft) != 0; } + bool isConnectedOnLeft() const noexcept { return (connectedEdgeFlags & ConnectedOnLeft) != 0; } /** Indicates whether the button adjoins another one on its right edge. @see setConnectedEdges */ - bool isConnectedOnRight() const noexcept { return (connectedEdgeFlags & ConnectedOnRight) != 0; } + bool isConnectedOnRight() const noexcept { return (connectedEdgeFlags & ConnectedOnRight) != 0; } /** Indicates whether the button adjoins another one on its top edge. @see setConnectedEdges */ - bool isConnectedOnTop() const noexcept { return (connectedEdgeFlags & ConnectedOnTop) != 0; } + bool isConnectedOnTop() const noexcept { return (connectedEdgeFlags & ConnectedOnTop) != 0; } /** Indicates whether the button adjoins another one on its bottom edge. @see setConnectedEdges */ - bool isConnectedOnBottom() const noexcept { return (connectedEdgeFlags & ConnectedOnBottom) != 0; } + bool isConnectedOnBottom() const noexcept { return (connectedEdgeFlags & ConnectedOnBottom) != 0; } //============================================================================== @@ -365,24 +347,46 @@ public: 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); + void setState (ButtonState newState); + // This method's parameters have changed - see the new version. + JUCE_DEPRECATED (void setToggleState (bool, bool)); //============================================================================== - struct Ids + /** This abstract base class is implemented by LookAndFeel classes to provide + button-drawing functionality. + */ + struct JUCE_API LookAndFeelMethods { - static const Identifier text, radioGroup, - connectedLeft, connectedRight, connectedTop, connectedBottom; + virtual ~LookAndFeelMethods() {} + + virtual void drawButtonBackground (Graphics&, Button&, const Colour& backgroundColour, + bool isMouseOverButton, bool isButtonDown) = 0; + + virtual Font getTextButtonFont (TextButton&, int buttonHeight) = 0; + virtual int getTextButtonWidthToFitText (TextButton&, int buttonHeight) = 0; + + /** Draws the text for a TextButton. */ + virtual void drawButtonText (Graphics&, TextButton&, bool isMouseOverButton, bool isButtonDown) = 0; + + /** Draws the contents of a standard ToggleButton. */ + virtual void drawToggleButton (Graphics&, ToggleButton&, bool isMouseOverButton, bool isButtonDown) = 0; + + virtual void changeToggleButtonWidthToFitText (ToggleButton&) = 0; + + virtual void drawTickBox (Graphics&, Component&, float x, float y, float w, float h, + bool ticked, bool isEnabled, bool isMouseOverButton, bool isButtonDown) = 0; + + virtual void drawDrawableButton (Graphics&, DrawableButton&, bool isMouseOverButton, bool isButtonDown) = 0; + + private: + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // These method have been deprecated: see their replacements above. + virtual int getTextButtonFont (TextButton&) { return 0; } + virtual int changeTextButtonWidthToFitText (TextButton&, int) { return 0; } + #endif }; - void refreshFromValueTree (const ValueTree&, ComponentBuilder&); - static int getConnectedFlags (const ValueTree& state); - - //============================================================================== - // These are deprecated - please use addListener() and removeListener() instead! - JUCE_DEPRECATED (void addButtonListener (Listener*)); - JUCE_DEPRECATED (void removeButtonListener (Listener*)); - protected: //============================================================================== /** This method is called when the button has been clicked. @@ -431,83 +435,77 @@ protected: //============================================================================== /** @internal */ - virtual void internalClickCallback (const ModifierKeys& modifiers); + virtual void internalClickCallback (const ModifierKeys&); /** @internal */ - void handleCommandMessage (int commandId); + void handleCommandMessage (int commandId) override; /** @internal */ - void mouseEnter (const MouseEvent& e); + void mouseEnter (const MouseEvent&) override; /** @internal */ - void mouseExit (const MouseEvent& e); + void mouseExit (const MouseEvent&) override; /** @internal */ - void mouseDown (const MouseEvent& e); + void mouseDown (const MouseEvent&) override; /** @internal */ - void mouseDrag (const MouseEvent& e); + void mouseDrag (const MouseEvent&) override; /** @internal */ - void mouseUp (const MouseEvent& e); + void mouseUp (const MouseEvent&) override; /** @internal */ - bool keyPressed (const KeyPress& key); + bool keyPressed (const KeyPress&) override; /** @internal */ - bool keyPressed (const KeyPress& key, Component* originatingComponent); + using Component::keyStateChanged; /** @internal */ - bool keyStateChanged (bool isKeyDown, Component* originatingComponent); + void paint (Graphics&) override; /** @internal */ - void paint (Graphics& g); + void parentHierarchyChanged() override; /** @internal */ - void parentHierarchyChanged(); + void visibilityChanged() override; /** @internal */ - void visibilityChanged(); + void focusGained (FocusChangeType) override; /** @internal */ - void focusGained (FocusChangeType cause); + void focusLost (FocusChangeType) override; /** @internal */ - void focusLost (FocusChangeType cause); - /** @internal */ - void enablementChanged(); - /** @internal */ - void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&); - /** @internal */ - void applicationCommandListChanged(); - /** @internal */ - void valueChanged (Value& value); + void enablementChanged() override; private: //============================================================================== - Array shortcuts; + Array shortcuts; WeakReference keySource; String text; - ListenerList buttonListeners; + ListenerList buttonListeners; - class RepeatTimer; - friend class RepeatTimer; - friend class ScopedPointer ; - ScopedPointer repeatTimer; + class CallbackHelper; + friend class CallbackHelper; + friend struct ContainerDeletePolicy; + ScopedPointer callbackHelper; uint32 buttonPressTime, lastRepeatTime; ApplicationCommandManager* commandManagerToUse; int autoRepeatDelay, autoRepeatSpeed, autoRepeatMinimumDelay; - int radioGroupId, commandID, connectedEdgeFlags; + int radioGroupId, connectedEdgeFlags; + CommandID commandID; ButtonState buttonState; Value isOn; - bool lastToggleState : 1; - bool clickTogglesState : 1; - bool needsToRelease : 1; - bool needsRepainting : 1; - bool isKeyDown : 1; - bool triggerOnMouseDown : 1; - bool generateTooltip : 1; + bool lastToggleState; + bool clickTogglesState; + bool needsToRelease; + bool needsRepainting; + bool isKeyDown; + bool triggerOnMouseDown; + bool generateTooltip; void repeatTimerCallback(); - RepeatTimer& getRepeatTimer(); + bool keyStateChangedCallback(); + void applicationCommandListChangeCallback(); ButtonState updateState(); ButtonState updateState (bool isOver, bool isDown); bool isShortcutPressed() const; - void turnOffOtherButtonsInGroup (bool sendChangeNotification); + void turnOffOtherButtonsInGroup (NotificationType); void flashButtonState(); - void sendClickMessage (const ModifierKeys& modifiers); + void sendClickMessage (const ModifierKeys&); void sendStateMessage(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Button); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Button) }; #ifndef DOXYGEN @@ -515,4 +513,4 @@ private: typedef Button::Listener ButtonListener; #endif -#endif // __JUCE_BUTTON_JUCEHEADER__ +#endif // JUCE_BUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp index 246d1cf..23225cf 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.cpp @@ -1,45 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -DrawableButton::DrawableButton (const String& name, - const DrawableButton::ButtonStyle buttonStyle) +DrawableButton::DrawableButton (const String& name, const DrawableButton::ButtonStyle buttonStyle) : Button (name), style (buttonStyle), currentImage (nullptr), edgeIndent (3) { - if (buttonStyle == ImageOnButtonBackground) - { - backgroundOff = Colour (0xffbbbbff); - backgroundOn = Colour (0xff3333ff); - } - else - { - backgroundOff = Colours::transparentBlack; - backgroundOn = Colour (0xaabbbbff); - } } DrawableButton::~DrawableButton() @@ -47,6 +35,11 @@ DrawableButton::~DrawableButton() } //============================================================================== +static Drawable* copyDrawableIfNotNull (const Drawable* const d) +{ + return d != nullptr ? d->createCopy() : nullptr; +} + void DrawableButton::setImages (const Drawable* normal, const Drawable* over, const Drawable* down, @@ -58,14 +51,14 @@ void DrawableButton::setImages (const Drawable* normal, { jassert (normal != nullptr); // you really need to give it at least a normal image.. - if (normal != nullptr) normalImage = normal->createCopy(); - if (over != nullptr) overImage = over->createCopy(); - if (down != nullptr) downImage = down->createCopy(); - if (disabled != nullptr) disabledImage = disabled->createCopy(); - if (normalOn != nullptr) normalImageOn = normalOn->createCopy(); - if (overOn != nullptr) overImageOn = overOn->createCopy(); - if (downOn != nullptr) downImageOn = downOn->createCopy(); - if (disabledOn != nullptr) disabledImageOn = disabledOn->createCopy(); + normalImage = copyDrawableIfNotNull (normal); + overImage = copyDrawableIfNotNull (over); + downImage = copyDrawableIfNotNull (down); + disabledImage = copyDrawableIfNotNull (disabled); + normalImageOn = copyDrawableIfNotNull (normalOn); + overImageOn = copyDrawableIfNotNull (overOn); + downImageOn = copyDrawableIfNotNull (downOn); + disabledImageOn = copyDrawableIfNotNull (disabledOn); buttonStateChanged(); } @@ -80,25 +73,6 @@ void DrawableButton::setButtonStyle (const DrawableButton::ButtonStyle newStyle) } } -void DrawableButton::setBackgroundColours (const Colour& toggledOffColour, - const Colour& toggledOnColour) -{ - if (backgroundOff != toggledOffColour - || backgroundOn != toggledOnColour) - { - backgroundOff = toggledOffColour; - backgroundOn = toggledOnColour; - - repaint(); - } -} - -const Colour& DrawableButton::getBackgroundColour() const noexcept -{ - return getToggleState() ? backgroundOn - : backgroundOff; -} - void DrawableButton::setEdgeIndent (const int numPixelsIndent) { edgeIndent = numPixelsIndent; @@ -116,23 +90,28 @@ void DrawableButton::resized() { currentImage->setOriginWithOriginalSize (Point()); } + else if (style == ImageStretched) + { + currentImage->setTransformToFit (getLocalBounds().toFloat(), RectanglePlacement::stretchToFit); + } else { Rectangle imageSpace; + const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); + const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); + if (style == ImageOnButtonBackground) { - imageSpace = getLocalBounds().reduced (getWidth() / 4, getHeight() / 4); + imageSpace = getLocalBounds().reduced (jmax (getWidth() / 4, indentX), + jmax (getHeight() / 4, indentY)); } else { const int textH = (style == ImageAboveTextLabel) ? jmin (16, proportionOfHeight (0.25f)) : 0; - const int indentX = jmin (edgeIndent, proportionOfWidth (0.3f)); - const int indentY = jmin (edgeIndent, proportionOfHeight (0.3f)); - imageSpace.setBounds (indentX, indentY, - getWidth() - indentX * 2, + getWidth() - indentX * 2, getHeight() - indentY * 2 - textH); } @@ -187,48 +166,31 @@ void DrawableButton::enablementChanged() buttonStateChanged(); } -void DrawableButton::paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown) +void DrawableButton::colourChanged() { + repaint(); +} + +void DrawableButton::paintButton (Graphics& g, + const bool isMouseOverButton, + const bool isButtonDown) +{ + LookAndFeel& lf = getLookAndFeel(); + if (style == ImageOnButtonBackground) - { - getLookAndFeel().drawButtonBackground (g, *this, - getBackgroundColour(), - isMouseOverButton, - isButtonDown); - } + lf.drawButtonBackground (g, *this, + findColour (getToggleState() ? TextButton::buttonOnColourId + : TextButton::buttonColourId), + isMouseOverButton, isButtonDown); else - { - g.fillAll (getBackgroundColour()); - - const int textH = (style == ImageAboveTextLabel) - ? jmin (16, proportionOfHeight (0.25f)) - : 0; - - if (textH > 0) - { - g.setFont ((float) textH); - - g.setColour (findColour (DrawableButton::textColourId) - .withMultipliedAlpha (isEnabled() ? 1.0f : 0.4f)); - - g.drawFittedText (getButtonText(), - 2, getHeight() - textH - 1, - getWidth() - 4, textH, - Justification::centred, 1); - } - } + lf.drawDrawableButton (g, *this, isMouseOverButton, isButtonDown); } //============================================================================== Drawable* DrawableButton::getCurrentImage() const noexcept { - if (isDown()) - return getDownImage(); - - if (isOver()) - return getOverImage(); + if (isDown()) return getDownImage(); + if (isOver()) return getOverImage(); return getNormalImage(); } @@ -241,50 +203,19 @@ Drawable* DrawableButton::getNormalImage() const noexcept Drawable* DrawableButton::getOverImage() const noexcept { - Drawable* d = normalImage; - if (getToggleState()) { - if (overImageOn != nullptr) - d = overImageOn; - else if (normalImageOn != nullptr) - d = normalImageOn; - else if (overImage != nullptr) - d = overImage; - } - else - { - if (overImage != nullptr) - d = overImage; + if (overImageOn != nullptr) return overImageOn; + if (normalImageOn != nullptr) return normalImageOn; } - return d; + return overImage != nullptr ? overImage : normalImage; } Drawable* DrawableButton::getDownImage() const noexcept { - Drawable* d = normalImage; + if (Drawable* const d = getToggleState() ? downImageOn : downImage) + return d; - if (getToggleState()) - { - if (downImageOn != nullptr) - d = downImageOn; - else if (overImageOn != nullptr) - d = overImageOn; - else if (normalImageOn != nullptr) - d = normalImageOn; - else if (downImage != nullptr) - d = downImage; - else - d = getOverImage(); - } - else - { - if (downImage != nullptr) - d = downImage; - else - d = getOverImage(); - } - - return d; + return getOverImage(); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.h index 5315169..7f9116a 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_DrawableButton.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DRAWABLEBUTTON_JUCEHEADER__ -#define __JUCE_DRAWABLEBUTTON_JUCEHEADER__ - -#include "juce_Button.h" -#include "../drawables/juce_Drawable.h" +#ifndef JUCE_DRAWABLEBUTTON_H_INCLUDED +#define JUCE_DRAWABLEBUTTON_H_INCLUDED //============================================================================== @@ -49,7 +45,10 @@ public: ImageRaw, /**< The button will just display the images in their normal size and position. This leaves it up to the caller to make sure the images are the correct size and position for the button. */ ImageAboveTextLabel, /**< Draws the button as a text label across the bottom with the image resized and scaled to fit above it. */ - ImageOnButtonBackground /**< Draws the button as a standard rounded-rectangle button with the image on top. */ + ImageOnButtonBackground, /**< Draws the button as a standard rounded-rectangle button with the image on top. + Note that if you use this style, the colour IDs that control the button colour are + TextButton::buttonColourId and TextButton::buttonOnColourId. */ + ImageStretched /**< Fills the button with a stretched version of the image. */ }; //============================================================================== @@ -88,60 +87,36 @@ public: 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 + state is 'on'. If this is nullptr, 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 + state is 'on'. If this is nullptr, 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 + state is 'on'. If this is nullptr, 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 + state is 'on'. If this is nullptr, the normal image will be drawn instead with a reduced opacity */ void setImages (const Drawable* normalImage, - const Drawable* overImage = nullptr, - const Drawable* downImage = nullptr, - const Drawable* disabledImage = nullptr, - const Drawable* normalImageOn = nullptr, - const Drawable* overImageOn = nullptr, - const Drawable* downImageOn = nullptr, + const Drawable* overImage = nullptr, + const Drawable* downImage = nullptr, + const Drawable* disabledImage = nullptr, + const Drawable* normalImageOn = nullptr, + const Drawable* overImageOn = nullptr, + const Drawable* downImageOn = nullptr, const Drawable* disabledImageOn = nullptr); //============================================================================== /** Changes the button's style. - @see ButtonStyle */ void setButtonStyle (ButtonStyle newStyle); + /** Returns the current style. */ + ButtonStyle getStyle() const noexcept { return style; } + //============================================================================== - /** 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 noexcept; - /** 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); @@ -149,8 +124,12 @@ public: //============================================================================== /** Returns the image that the button is currently displaying. */ Drawable* getCurrentImage() const noexcept; + + /** Returns the image that the button will use for its normal state. */ Drawable* getNormalImage() const noexcept; + /** Returns the image that the button will use when the mouse is over it. */ Drawable* getOverImage() const noexcept; + /** Returns the image that the button will use when the mouse is held down on it. */ Drawable* getDownImage() const noexcept; //============================================================================== @@ -159,37 +138,48 @@ public: These constants can be used either via the Component::setColour(), or LookAndFeel::setColour() methods. + Note that when the ImageOnButtonBackground style is used, the colour IDs that control + the button colour are TextButton::buttonColourId and TextButton::buttonOnColourId. + @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour */ enum ColourIds { - textColourId = 0x1004010, /**< The colour to use for the URL text. */ + textColourId = 0x1004010, /**< The colour to use for the button's text label. */ + textColourOnId = 0x1004013, /**< The colour to use for the button's text.when the button's toggle state is "on". */ + + backgroundColourId = 0x1004011, /**< The colour used to fill the button's background (when + the button is toggled 'off'). Note that if you use the + ImageOnButtonBackground style, you should use TextButton::buttonColourId + to change the button's colour. */ + backgroundOnColourId = 0x1004012, /**< The colour used to fill the button's background (when + the button is toggled 'on'). Note that if you use the + ImageOnButtonBackground style, you should use TextButton::buttonOnColourId + to change the button's colour. */ }; -protected: //============================================================================== /** @internal */ - void paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown); + void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; /** @internal */ - void buttonStateChanged(); + void buttonStateChanged() override; /** @internal */ - void resized(); + void resized() override; /** @internal */ - void enablementChanged(); + void enablementChanged() override; + /** @internal */ + void colourChanged() override; private: //============================================================================== ButtonStyle style; - ScopedPointer normalImage, overImage, downImage, disabledImage; - ScopedPointer normalImageOn, overImageOn, downImageOn, disabledImageOn; + ScopedPointer normalImage, overImage, downImage, disabledImage, + normalImageOn, overImageOn, downImageOn, disabledImageOn; Drawable* currentImage; - Colour backgroundOff, backgroundOn; int edgeIndent; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableButton); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DrawableButton) }; -#endif // __JUCE_DRAWABLEBUTTON_JUCEHEADER__ +#endif // JUCE_DRAWABLEBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp index 0f5c3e0..8274f4c 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -51,7 +50,7 @@ HyperlinkButton::~HyperlinkButton() //============================================================================== void HyperlinkButton::setFont (const Font& newFont, const bool resizeToMatchComponentHeight, - const Justification& justificationType) + Justification justificationType) { font = newFont; resizeFont = resizeToMatchComponentHeight; @@ -75,7 +74,7 @@ Font HyperlinkButton::getFontToUse() const void HyperlinkButton::changeWidthToFitText() { - setSize (getFontToUse().getStringWidth (getName()) + 6, getHeight()); + setSize (getFontToUse().getStringWidth (getButtonText()) + 6, getHeight()); } void HyperlinkButton::colourChanged() @@ -104,20 +103,7 @@ void HyperlinkButton::paintButton (Graphics& g, g.setFont (getFontToUse()); - g.drawText (getButtonText(), - 2, 0, getWidth() - 2, getHeight(), + g.drawText (getButtonText(), getLocalBounds().reduced (1, 0), justification.getOnlyHorizontalFlags() | Justification::verticallyCentred, true); } - -const Identifier HyperlinkButton::Ids::tagType ("HYPERLINKBUTTON"); -const Identifier HyperlinkButton::Ids::text ("text"); -const Identifier HyperlinkButton::Ids::url ("url"); - -void HyperlinkButton::refreshFromValueTree (const ValueTree& state, ComponentBuilder&) -{ - ComponentBuilder::refreshBasicComponentProperties (*this, state); - - setButtonText (state [Ids::text].toString()); - setURL (URL (state [Ids::url].toString())); -} diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h index c2e285f..4cd3f52 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_HyperlinkButton.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_HYPERLINKBUTTON_JUCEHEADER__ -#define __JUCE_HYPERLINKBUTTON_JUCEHEADER__ - -#include "juce_Button.h" +#ifndef JUCE_HYPERLINKBUTTON_H_INCLUDED +#define JUCE_HYPERLINKBUTTON_H_INCLUDED //============================================================================== @@ -64,7 +61,7 @@ public: */ void setFont (const Font& newFont, bool resizeToMatchComponentHeight, - const Justification& justificationType = Justification::horizontallyCentred); + Justification justificationType = Justification::horizontallyCentred); //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the link. @@ -93,24 +90,14 @@ public: */ void changeWidthToFitText(); - //============================================================================== - struct Ids - { - static const Identifier tagType, text, url; - }; - - void refreshFromValueTree (const ValueTree&, ComponentBuilder&); - protected: //============================================================================== /** @internal */ - void clicked(); + void clicked() override; /** @internal */ - void colourChanged(); + void colourChanged() override; /** @internal */ - void paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown); + void paintButton (Graphics&, bool isMouseOver, bool isButtonDown) override; private: //============================================================================== @@ -121,7 +108,7 @@ private: Font getFontToUse() const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HyperlinkButton); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (HyperlinkButton) }; -#endif // __JUCE_HYPERLINKBUTTON_JUCEHEADER__ +#endif // JUCE_HYPERLINKBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.cpp index 957053d..3170d05 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -40,13 +39,13 @@ void ImageButton::setImages (const bool resizeButtonNowToFitThisImage, const bool preserveImageProportions, const Image& normalImage_, const float imageOpacityWhenNormal, - const Colour& overlayColourWhenNormal, + Colour overlayColourWhenNormal, const Image& overImage_, const float imageOpacityWhenOver, - const Colour& overlayColourWhenOver, + Colour overlayColourWhenOver, const Image& downImage_, const float imageOpacityWhenDown, - const Colour& overlayColourWhenDown, + Colour overlayColourWhenDown, const float hitTestAlphaThreshold) { normalImage = normalImage_; @@ -188,56 +187,3 @@ bool ImageButton::hitTest (int x, int y) && alphaThreshold < im.getPixelAt (((x - imageBounds.getX()) * im.getWidth()) / imageBounds.getWidth(), ((y - imageBounds.getY()) * im.getHeight()) / imageBounds.getHeight()).getAlpha()); } - -const Identifier ImageButton::Ids::tagType ("IMAGEBUTTON"); -const Identifier ImageButton::Ids::upImage ("upImage"); -const Identifier ImageButton::Ids::overImage ("overImage"); -const Identifier ImageButton::Ids::downImage ("downImage"); -const Identifier ImageButton::Ids::upOverlay ("upOverlay"); -const Identifier ImageButton::Ids::overOverlay ("overOverlay"); -const Identifier ImageButton::Ids::downOverlay ("downOverlay"); -const Identifier ImageButton::Ids::upOpacity ("upOpacity"); -const Identifier ImageButton::Ids::overOpacity ("overOpacity"); -const Identifier ImageButton::Ids::downOpacity ("downOpacity"); - -namespace ImageButtonHelpers -{ - static Colour getColourFromVar (const var& col) - { - return col.isString() ? Colour::fromString (col.toString()) - : Colours::transparentBlack; - } - - static float getOpacityFromVar (const var& v) - { - return v.isVoid() ? 1.0f : static_cast (v); - } -} - -void ImageButton::refreshFromValueTree (const ValueTree& state, ComponentBuilder& builder) -{ - Button::refreshFromValueTree (state, builder); - - const var upImageIdentifier (state [Ids::upImage]), - overImageIdentifier (state [Ids::overImage]), - downImageIdentifier (state [Ids::downImage]); - - ComponentBuilder::ImageProvider* const imageProvider = builder.getImageProvider(); - jassert (imageProvider != nullptr || upImageIdentifier.isVoid()); - - Image newUpImage, newOverImage, newDownImage; - - if (imageProvider != nullptr) - { - newUpImage = imageProvider->getImageForIdentifier (upImageIdentifier); - newOverImage = imageProvider->getImageForIdentifier (overImageIdentifier); - newDownImage = imageProvider->getImageForIdentifier (downImageIdentifier); - } - - using namespace ImageButtonHelpers; - - setImages (false, true, true, - newUpImage, getOpacityFromVar (state[Ids::upOpacity]), getColourFromVar (state[Ids::upOverlay]), - newOverImage, getOpacityFromVar (state[Ids::overOpacity]), getColourFromVar (state[Ids::overOverlay]), - newDownImage, getOpacityFromVar (state[Ids::downOpacity]), getColourFromVar (state[Ids::downOverlay])); -} diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.h index 6828603..64db59d 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ImageButton.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IMAGEBUTTON_JUCEHEADER__ -#define __JUCE_IMAGEBUTTON_JUCEHEADER__ - -#include "juce_Button.h" +#ifndef JUCE_IMAGEBUTTON_H_INCLUDED +#define JUCE_IMAGEBUTTON_H_INCLUDED //============================================================================== @@ -45,7 +42,7 @@ 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(). + are specified here can be changed later using setImages(). @param name the name to give the component */ @@ -101,13 +98,13 @@ public: bool preserveImageProportions, const Image& normalImage, float imageOpacityWhenNormal, - const Colour& overlayColourWhenNormal, + Colour overlayColourWhenNormal, const Image& overImage, float imageOpacityWhenOver, - const Colour& overlayColourWhenOver, + Colour overlayColourWhenOver, const Image& downImage, float imageOpacityWhenDown, - const Colour& overlayColourWhenDown, + Colour overlayColourWhenDown, float hitTestAlphaThreshold = 0.0f); /** Returns the currently set 'normal' image. */ @@ -128,23 +125,22 @@ public: Image getDownImage() const; //============================================================================== - struct Ids + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods { - static const Identifier tagType, upImage, overImage, downImage, - upOverlay, overOverlay, downOverlay, - upOpacity, overOpacity, downOpacity; - }; + virtual ~LookAndFeelMethods() {} - void refreshFromValueTree (const ValueTree&, ComponentBuilder&); + virtual void drawImageButton (Graphics&, Image*, + int imageX, int imageY, int imageW, int imageH, + const Colour& overlayColour, float imageOpacity, ImageButton&) = 0; + }; protected: //============================================================================== /** @internal */ - bool hitTest (int x, int y); + bool hitTest (int x, int y) override; /** @internal */ - void paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown); + void paintButton (Graphics&, bool isMouseOver, bool isButtonDown) override; private: //============================================================================== @@ -157,8 +153,8 @@ private: Image getCurrentImage() const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageButton); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImageButton) }; -#endif // __JUCE_IMAGEBUTTON_JUCEHEADER__ +#endif // JUCE_IMAGEBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp index 7746719..28cd8de 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.cpp @@ -1,61 +1,55 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -ShapeButton::ShapeButton (const String& text_, - const Colour& normalColour_, - const Colour& overColour_, - const Colour& downColour_) - : Button (text_), - normalColour (normalColour_), - overColour (overColour_), - downColour (downColour_), +ShapeButton::ShapeButton (const String& t, Colour n, Colour o, Colour d) + : Button (t), + normalColour (n), overColour (o), downColour (d), maintainShapeProportions (false), outlineWidth (0.0f) { } -ShapeButton::~ShapeButton() -{ -} +ShapeButton::~ShapeButton() {} -void ShapeButton::setColours (const Colour& newNormalColour, - const Colour& newOverColour, - const Colour& newDownColour) +void ShapeButton::setColours (Colour newNormalColour, Colour newOverColour, Colour newDownColour) { normalColour = newNormalColour; overColour = newOverColour; downColour = newDownColour; } -void ShapeButton::setOutline (const Colour& newOutlineColour, - const float newOutlineWidth) +void ShapeButton::setOutline (Colour newOutlineColour, const float newOutlineWidth) { outlineColour = newOutlineColour; outlineWidth = newOutlineWidth; } +void ShapeButton::setBorderSize (BorderSize newBorder) +{ + border = newBorder; +} + void ShapeButton::setShape (const Path& newShape, const bool resizeNowToFitThisShape, const bool maintainShapeProportions_, @@ -64,21 +58,24 @@ void ShapeButton::setShape (const Path& newShape, shape = newShape; maintainShapeProportions = maintainShapeProportions_; - shadow.setShadowProperties (3.0f, 0.5f, 0, 0); - setComponentEffect ((hasShadow) ? &shadow : 0); + shadow.setShadowProperties (DropShadow (Colours::black.withAlpha (0.5f), 3, Point())); + setComponentEffect (hasShadow ? &shadow : nullptr); if (resizeNowToFitThisShape) { Rectangle newBounds (shape.getBounds()); if (hasShadow) - newBounds.expand (4.0f, 4.0f); + newBounds = newBounds.expanded (4.0f); - shape.applyTransform (AffineTransform::translation (-newBounds.getX(), -newBounds.getY())); + shape.applyTransform (AffineTransform::translation (-newBounds.getX(), + -newBounds.getY())); - setSize (1 + (int) (newBounds.getWidth() + outlineWidth), - 1 + (int) (newBounds.getHeight() + outlineWidth)); + setSize (1 + (int) (newBounds.getWidth() + outlineWidth) + border.getLeftAndRight(), + 1 + (int) (newBounds.getHeight() + outlineWidth) + border.getTopAndBottom()); } + + repaint(); } void ShapeButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) @@ -89,25 +86,24 @@ void ShapeButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButto isButtonDown = false; } - g.setColour ((isButtonDown) ? downColour - : (isMouseOverButton) ? overColour - : normalColour); - - int w = getWidth(); - int h = getHeight(); + Rectangle r (border.subtractedFrom (getLocalBounds()).toFloat().reduced (outlineWidth * 0.5f)); if (getComponentEffect() != nullptr) + r = r.reduced (2.0f); + + if (isButtonDown) { - w -= 4; - h -= 4; + const float sizeReductionWhenPressed = 0.04f; + + r = r.reduced (sizeReductionWhenPressed * r.getWidth(), + sizeReductionWhenPressed * r.getHeight()); } - const float offset = (outlineWidth * 0.5f) + (isButtonDown ? 1.5f : 0.0f); + const AffineTransform trans (shape.getTransformToScaleToFit (r, maintainShapeProportions)); - const AffineTransform trans (shape.getTransformToScaleToFit (offset, offset, - w - offset - outlineWidth, - h - offset - outlineWidth, - maintainShapeProportions)); + g.setColour (isButtonDown ? downColour + : isMouseOverButton ? overColour + : normalColour); g.fillPath (shape, trans); if (outlineWidth > 0.0f) diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.h index 24674b1..ddc4625 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ShapeButton.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_SHAPEBUTTON_JUCEHEADER__ -#define __JUCE_SHAPEBUTTON_JUCEHEADER__ - -#include "juce_Button.h" +#ifndef JUCE_SHAPEBUTTON_H_INCLUDED +#define JUCE_SHAPEBUTTON_H_INCLUDED //============================================================================== @@ -47,9 +44,9 @@ public: @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); + Colour normalColour, + Colour overColour, + Colour downColour); /** Destructor. */ ~ShapeButton(); @@ -74,35 +71,36 @@ public: @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); + void setColours (Colour normalColour, + Colour overColour, + 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); + void setOutline (Colour outlineColour, float outlineStrokeWidth); + /** This lets you specify a border to be left around the edge of the button when + drawing the shape. + */ + void setBorderSize (BorderSize border); -protected: /** @internal */ - void paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown); + void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; private: //============================================================================== Colour normalColour, overColour, downColour, outlineColour; DropShadowEffect shadow; Path shape; + BorderSize border; bool maintainShapeProportions; float outlineWidth; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ShapeButton); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ShapeButton) }; -#endif // __JUCE_SHAPEBUTTON_JUCEHEADER__ +#endif // JUCE_SHAPEBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.cpp index 07b64e4..b51ea65 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.cpp @@ -1,31 +1,35 @@ /* ============================================================================== + file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -TextButton::TextButton (const String& name, - const String& toolTip) - : Button (name) +TextButton::TextButton() : Button (String()) +{ +} + +TextButton::TextButton (const String& name) : Button (name) +{ +} + +TextButton::TextButton (const String& name, const String& toolTip) : Button (name) { setTooltip (toolTip); } @@ -34,19 +38,15 @@ TextButton::~TextButton() { } -void TextButton::paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown) +void TextButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { - getLookAndFeel().drawButtonBackground (g, *this, - findColour (getToggleState() ? buttonOnColourId - : buttonColourId), - isMouseOverButton, - isButtonDown); + LookAndFeel& lf = getLookAndFeel(); - getLookAndFeel().drawButtonText (g, *this, - isMouseOverButton, - isButtonDown); + lf.drawButtonBackground (g, *this, + findColour (getToggleState() ? buttonOnColourId : buttonColourId), + isMouseOverButton, isButtonDown); + + lf.drawButtonText (g, *this, isMouseOverButton, isButtonDown); } void TextButton::colourChanged() @@ -54,18 +54,17 @@ void TextButton::colourChanged() repaint(); } -Font TextButton::getFont() +void TextButton::changeWidthToFitText() { - return Font (jmin (15.0f, getHeight() * 0.6f)); + changeWidthToFitText (getHeight()); } void TextButton::changeWidthToFitText (const int newHeight) { - if (newHeight >= 0) - setSize (jmax (1, getWidth()), newHeight); - - setSize (getFont().getStringWidth (getButtonText()) + getHeight(), - getHeight()); + setSize (getBestWidthForHeight (newHeight), newHeight); } -const Identifier TextButton::Ids::tagType ("TEXTBUTTON"); +int TextButton::getBestWidthForHeight (int buttonHeight) +{ + return getLookAndFeel().getTextButtonWidthToFitText (*this, buttonHeight); +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h index 03baf61..b9093b2 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_TextButton.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TEXTBUTTON_JUCEHEADER__ -#define __JUCE_TEXTBUTTON_JUCEHEADER__ - -#include "juce_Button.h" +#ifndef JUCE_TEXTBUTTON_H_INCLUDED +#define JUCE_TEXTBUTTON_H_INCLUDED //============================================================================== @@ -40,17 +37,23 @@ class JUCE_API TextButton : public Button { public: //============================================================================== - /** Creates a TextButton. + /** Creates a TextButton. */ + TextButton(); + /** 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) + */ + explicit TextButton (const String& buttonName); + + /** 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 = String::empty, - const String& toolTip = String::empty); + TextButton (const String& buttonName, const String& toolTip); /** Destructor. */ ~TextButton(); @@ -76,35 +79,35 @@ public: }; //============================================================================== - /** 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. + /** Changes this button's width to fit neatly around its current text, without + changing its height. */ - void changeWidthToFitText (int newHeight = -1); + void changeWidthToFitText(); - /** 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. + /** Resizes the button's width to fit neatly around its current text, and gives it + the specified height. */ - virtual Font getFont(); + void changeWidthToFitText (int newHeight); + + /** Returns the width that the LookAndFeel suggests would be best for this button if it + had the given height. + */ + int getBestWidthForHeight (int buttonHeight); //============================================================================== - struct Ids - { - static const Identifier tagType; - }; - -protected: /** @internal */ - void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown); + void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; /** @internal */ - void colourChanged(); + void colourChanged() override; private: - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextButton); + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // Note that this method has been removed - instead, see LookAndFeel::getTextButtonWidthToFitText() + virtual int getFont() { return 0; } + #endif + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TextButton) }; -#endif // __JUCE_TEXTBUTTON_JUCEHEADER__ +#endif // JUCE_TEXTBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp index 47efb37..edaf7ab 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.cpp @@ -1,28 +1,33 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ +ToggleButton::ToggleButton() + : Button (String::empty) +{ + setClickingTogglesState (true); +} + ToggleButton::ToggleButton (const String& buttonText) : Button (buttonText) { @@ -33,13 +38,9 @@ ToggleButton::~ToggleButton() { } -void ToggleButton::paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown) +void ToggleButton::paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) { - getLookAndFeel().drawToggleButton (g, *this, - isMouseOverButton, - isButtonDown); + getLookAndFeel().drawToggleButton (g, *this, isMouseOverButton, isButtonDown); } void ToggleButton::changeWidthToFitText() @@ -51,5 +52,3 @@ void ToggleButton::colourChanged() { repaint(); } - -const Identifier ToggleButton::Ids::tagType ("TOGGLEBUTTON"); diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h index 41f511b..911bc59 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToggleButton.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TOGGLEBUTTON_JUCEHEADER__ -#define __JUCE_TOGGLEBUTTON_JUCEHEADER__ - -#include "juce_Button.h" +#ifndef JUCE_TOGGLEBUTTON_H_INCLUDED +#define JUCE_TOGGLEBUTTON_H_INCLUDED //============================================================================== @@ -42,20 +39,22 @@ class JUCE_API ToggleButton : public Button { public: //============================================================================== + /** Creates a ToggleButton. */ + ToggleButton(); + /** 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) */ - explicit ToggleButton (const String& buttonText = String::empty); + explicit 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(); @@ -73,25 +72,16 @@ public: textColourId = 0x1006501 /**< The colour to use for the button's text. */ }; - //============================================================================== - struct Ids - { - static const Identifier tagType; - }; - protected: + //============================================================================== /** @internal */ - void paintButton (Graphics& g, - bool isMouseOverButton, - bool isButtonDown); - + void paintButton (Graphics&, bool isMouseOverButton, bool isButtonDown) override; /** @internal */ - void colourChanged(); + void colourChanged() override; private: - //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleButton); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToggleButton) }; -#endif // __JUCE_TOGGLEBUTTON_JUCEHEADER__ +#endif // JUCE_TOGGLEBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp index 599a628..f7ef1b0 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.cpp @@ -1,36 +1,35 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -ToolbarButton::ToolbarButton (const int itemId_, const String& buttonText, - Drawable* const normalImage_, Drawable* const toggledOnImage_) - : ToolbarItemComponent (itemId_, buttonText, true), - normalImage (normalImage_), - toggledOnImage (toggledOnImage_), +ToolbarButton::ToolbarButton (const int iid, const String& buttonText, + Drawable* const normalIm, Drawable* const toggledOnIm) + : ToolbarItemComponent (iid, buttonText, true), + normalImage (normalIm), + toggledOnImage (toggledOnIm), currentImage (nullptr) { - jassert (normalImage_ != nullptr); + jassert (normalImage != nullptr); } ToolbarButton::~ToolbarButton() @@ -73,6 +72,7 @@ void ToolbarButton::updateDrawable() { if (currentImage != nullptr) { + currentImage->setInterceptsMouseClicks (false, false); currentImage->setTransformToFit (getContentArea().toFloat(), RectanglePlacement::centred); currentImage->setAlpha (isEnabled() ? 1.0f : 0.5f); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.h b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.h index 23234d9..1f2548f 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.h +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_ToolbarButton.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TOOLBARBUTTON_JUCEHEADER__ -#define __JUCE_TOOLBARBUTTON_JUCEHEADER__ - -#include "../widgets/juce_ToolbarItemComponent.h" +#ifndef JUCE_TOOLBARBUTTON_H_INCLUDED +#define JUCE_TOOLBARBUTTON_H_INCLUDED //============================================================================== @@ -55,10 +52,10 @@ public: 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. + nullptr 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 + owned by this object and will be deleted when no longer needed or when + this button is deleted. */ ToolbarButton (int itemId, const String& labelText, @@ -72,17 +69,17 @@ public: //============================================================================== /** @internal */ bool getToolbarItemSizes (int toolbarDepth, bool isToolbarVertical, int& preferredSize, - int& minSize, int& maxSize); + int& minSize, int& maxSize) override; /** @internal */ - void paintButtonArea (Graphics&, int width, int height, bool isMouseOver, bool isMouseDown); + void paintButtonArea (Graphics&, int width, int height, bool isMouseOver, bool isMouseDown) override; /** @internal */ - void contentAreaChanged (const Rectangle&); + void contentAreaChanged (const Rectangle&) override; /** @internal */ - void buttonStateChanged(); + void buttonStateChanged() override; /** @internal */ - void resized(); + void resized() override; /** @internal */ - void enablementChanged(); + void enablementChanged() override; private: //============================================================================== @@ -93,8 +90,8 @@ private: Drawable* getImageToUse() const; void setCurrentImage (Drawable*); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToolbarButton); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ToolbarButton) }; -#endif // __JUCE_TOOLBARBUTTON_JUCEHEADER__ +#endif // JUCE_TOOLBARBUTTON_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h index 8572305..9a00cbc 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandID.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ -#define __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ +#ifndef JUCE_APPLICATIONCOMMANDID_H_INCLUDED +#define JUCE_APPLICATIONCOMMANDID_H_INCLUDED //============================================================================== @@ -52,32 +51,41 @@ typedef int CommandID; */ namespace StandardApplicationCommandIDs { - /** This command ID should be used to send a "Quit the App" command. + enum + { + /** 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; + 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 JUCEApplicationBase::systemRequestedQuit(). + */ + 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 "Delete" command. */ + 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 "Cut" command. */ + 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 "Copy to clipboard" command. */ + 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 "Paste from clipboard" command. */ + 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 "Select all" command. */ + selectAll = 0x1006, - /** The command ID that should be used to send a "Deselect all" command. */ - static const CommandID deselectAll = 0x1007; + /** The command ID that should be used to send a "Deselect all" command. */ + deselectAll = 0x1007, + + /** The command ID that should be used to send a "undo" command. */ + undo = 0x1008, + + /** The command ID that should be used to send a "redo" command. */ + redo = 0x1009 + }; } -#endif // __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ +#endif // JUCE_APPLICATIONCOMMANDID_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp index 93751b4..174ec80 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp @@ -1,31 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -ApplicationCommandInfo::ApplicationCommandInfo (const CommandID commandID_) noexcept - : commandID (commandID_), - flags (0) +ApplicationCommandInfo::ApplicationCommandInfo (const CommandID cid) noexcept + : commandID (cid), flags (0) { } @@ -56,7 +54,7 @@ void ApplicationCommandInfo::setTicked (const bool b) noexcept flags &= ~isTicked; } -void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, const ModifierKeys& modifiers) noexcept +void ApplicationCommandInfo::addDefaultKeypress (const int keyCode, ModifierKeys modifiers) noexcept { defaultKeypresses.add (KeyPress (keyCode, modifiers, 0)); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h index c1bac5c..f906f43 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ -#define __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ - -#include "../keyboard/juce_KeyPress.h" -#include "juce_ApplicationCommandID.h" +#ifndef JUCE_APPLICATIONCOMMANDINFO_H_INCLUDED +#define JUCE_APPLICATIONCOMMANDINFO_H_INCLUDED //============================================================================== @@ -81,8 +77,7 @@ struct JUCE_API ApplicationCommandInfo myinfo.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier)); @endcode */ - void addDefaultKeypress (int keyCode, - const ModifierKeys& modifiers) noexcept; + void addDefaultKeypress (int keyCode, ModifierKeys modifiers) noexcept; //============================================================================== /** The command's unique ID number. @@ -127,7 +122,7 @@ struct JUCE_API ApplicationCommandInfo @see addDefaultKeypress */ - Array defaultKeypresses; + Array defaultKeypresses; //============================================================================== /** Flags describing the ways in which this command should be used. @@ -191,4 +186,4 @@ struct JUCE_API ApplicationCommandInfo }; -#endif // __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ +#endif // JUCE_APPLICATIONCOMMANDINFO_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp index 96d275c..4f4ff46 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,8 +25,7 @@ ApplicationCommandManager::ApplicationCommandManager() : firstTarget (nullptr) { - keyMappings = new KeyPressMappingSet (this); - + keyMappings = new KeyPressMappingSet (*this); Desktop::getInstance().addFocusChangeListener (this); } @@ -65,7 +63,7 @@ void ApplicationCommandManager::registerCommand (const ApplicationCommandInfo& n } else { - // trying to re-register the same command with different parameters? + // trying to re-register the same command ID with different parameters? jassert (newCommand.shortName == getCommandForID (newCommand.commandID)->shortName && (newCommand.description == getCommandForID (newCommand.commandID)->description || newCommand.description.isEmpty()) && newCommand.categoryName == getCommandForID (newCommand.commandID)->categoryName @@ -79,7 +77,7 @@ void ApplicationCommandManager::registerAllCommandsForTarget (ApplicationCommand { if (target != nullptr) { - Array commandIDs; + Array commandIDs; target->getAllCommands (commandIDs); for (int i = 0; i < commandIDs.size(); ++i) @@ -101,7 +99,7 @@ void ApplicationCommandManager::removeCommand (const CommandID commandID) commands.remove (i); triggerAsyncUpdate(); - const Array keys (keyMappings->getKeyPressesAssignedToCommand (commandID)); + const Array keys (keyMappings->getKeyPressesAssignedToCommand (commandID)); for (int j = keys.size(); --j >= 0;) keyMappings->removeKeyPress (keys.getReference (j)); @@ -126,17 +124,19 @@ const ApplicationCommandInfo* ApplicationCommandManager::getCommandForID (const String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) const noexcept { - const ApplicationCommandInfo* const ci = getCommandForID (commandID); + if (const ApplicationCommandInfo* const ci = getCommandForID (commandID)) + return ci->shortName; - return ci != nullptr ? ci->shortName : String::empty; + return String(); } String ApplicationCommandManager::getDescriptionOfCommand (const CommandID commandID) const noexcept { - const ApplicationCommandInfo* const ci = getCommandForID (commandID); + if (const ApplicationCommandInfo* const ci = getCommandForID (commandID)) + return ci->description.isNotEmpty() ? ci->description + : ci->shortName; - return ci != nullptr ? (ci->description.isNotEmpty() ? ci->description : ci->shortName) - : String::empty; + return String(); } StringArray ApplicationCommandManager::getCommandCategories() const @@ -151,7 +151,7 @@ StringArray ApplicationCommandManager::getCommandCategories() const Array ApplicationCommandManager::getCommandsInCategory (const String& categoryName) const { - Array results; + Array results; for (int i = 0; i < commands.size(); ++i) if (commands.getUnchecked(i)->categoryName == categoryName) @@ -169,26 +169,24 @@ bool ApplicationCommandManager::invokeDirectly (const CommandID commandID, const return invoke (info, asynchronously); } -bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& info_, const bool asynchronously) +bool ApplicationCommandManager::invoke (const ApplicationCommandTarget::InvocationInfo& inf, const bool asynchronously) { // This call isn't thread-safe for use from a non-UI thread without locking the message // manager first.. jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager()); + bool ok = false; ApplicationCommandInfo commandInfo (0); - ApplicationCommandTarget* const target = getTargetForCommand (info_.commandID, commandInfo); - if (target == nullptr) - return false; + if (ApplicationCommandTarget* const target = getTargetForCommand (inf.commandID, commandInfo)) + { + ApplicationCommandTarget::InvocationInfo info (inf); + info.commandFlags = commandInfo.flags; - ApplicationCommandTarget::InvocationInfo info (info_); - info.commandFlags = commandInfo.flags; - - sendListenerInvokeCallback (info); - - const bool ok = target->invoke (info, asynchronously); - - commandStatusChanged(); + sendListenerInvokeCallback (info); + ok = target->invoke (info, asynchronously); + commandStatusChanged(); + } return ok; } @@ -239,9 +237,7 @@ ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget( if (c == nullptr) { - TopLevelWindow* const activeWindow = TopLevelWindow::getActiveTopLevelWindow(); - - if (activeWindow != nullptr) + if (TopLevelWindow* const activeWindow = TopLevelWindow::getActiveTopLevelWindow()) { c = activeWindow->getPeer()->getLastFocusedSubcomponent(); @@ -252,32 +248,26 @@ ApplicationCommandTarget* ApplicationCommandManager::findDefaultComponentTarget( if (c == nullptr && Process::isForegroundProcess()) { - // getting a bit desperate now - try all desktop comps.. - for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) - { - ApplicationCommandTarget* const target - = findTargetForComponent (Desktop::getInstance().getComponent (i) - ->getPeer()->getLastFocusedSubcomponent()); + Desktop& desktop = Desktop::getInstance(); - if (target != nullptr) - return target; - } + // getting a bit desperate now: try all desktop comps.. + for (int i = desktop.getNumComponents(); --i >= 0;) + if (ComponentPeer* const peer = desktop.getComponent(i)->getPeer()) + if (ApplicationCommandTarget* const target = findTargetForComponent (peer->getLastFocusedSubcomponent())) + return target; } if (c != nullptr) { - ResizableWindow* const resizableWindow = dynamic_cast (c); - // if we're focused on a ResizableWindow, chances are that it's the content // component that really should get the event. And if not, the event will // still be passed up to the top level window anyway, so let's send it to the // content comp. - if (resizableWindow != nullptr && resizableWindow->getContentComponent() != nullptr) - c = resizableWindow->getContentComponent(); + if (ResizableWindow* const resizableWindow = dynamic_cast (c)) + if (Component* const content = resizableWindow->getContentComponent()) + c = content; - ApplicationCommandTarget* const target = findTargetForComponent (c); - - if (target != nullptr) + if (ApplicationCommandTarget* const target = findTargetForComponent (c)) return target; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h index 324cd8c..1d6158e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.h @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ -#define __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ - -#include "juce_ApplicationCommandTarget.h" -class KeyPressMappingSet; -class ApplicationCommandManagerListener; -class Desktop; +#ifndef JUCE_APPLICATIONCOMMANDMANAGER_H_INCLUDED +#define JUCE_APPLICATIONCOMMANDMANAGER_H_INCLUDED //============================================================================== @@ -77,8 +71,8 @@ class Desktop; 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 + method), and so on until ApplicationCommandTarget::getNextCommandTarget() returns nullptr. + 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, @@ -109,13 +103,11 @@ public: //============================================================================== /** 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); @@ -131,7 +123,6 @@ public: 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); @@ -150,13 +141,11 @@ public: //============================================================================== /** Returns the number of commands that have been registered. - @see registerCommand */ int getNumCommands() const noexcept { 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 noexcept { return commands [index]; } @@ -187,7 +176,7 @@ public: /** Returns the list of categories. - This will go through all registered commands, and return a list of all the distict + This will go through all registered commands, and return a list of all the distinct categoryName values from their ApplicationCommandInfo structure. @see getCommandsInCategory() @@ -195,7 +184,6 @@ public: StringArray getCommandCategories() const; /** Returns a list of all the command UIDs in a particular category. - @see getCommandCategories() */ Array getCommandsInCategory (const String& categoryName) const; @@ -214,7 +202,6 @@ public: //============================================================================== /** 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. */ @@ -252,7 +239,7 @@ public: 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. + It may return nullptr if no targets are available. @see getTargetForCommand, invoke, invokeDirectly */ @@ -260,11 +247,11 @@ public: /** Sets a target to be returned by getFirstCommandTarget(). - If this is set to 0, then getFirstCommandTarget() will by default return the + If this is set to nullptr, 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. + If you use this to set a target, make sure you call setFirstCommandTarget(nullptr) + before deleting the target object. */ void setFirstCommandTarget (ApplicationCommandTarget* newTarget) noexcept; @@ -275,7 +262,7 @@ public: 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 no targets are found that can perform the command, this method will return nullptr. 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 @@ -303,24 +290,24 @@ public: static ApplicationCommandTarget* findDefaultComponentTarget(); /** Examines this component and all its parents in turn, looking for the first one - which is a ApplicationCommandTarget. + which is an ApplicationCommandTarget. - Returns the first ApplicationCommandTarget that it finds, or 0 if none of them implement - that class. + Returns the first ApplicationCommandTarget that it finds, or nullptr if none of them + implement that class. */ - static ApplicationCommandTarget* findTargetForComponent (Component* component); + static ApplicationCommandTarget* findTargetForComponent (Component*); private: //============================================================================== - OwnedArray commands; - ListenerList listeners; - ScopedPointer keyMappings; + OwnedArray commands; + ListenerList listeners; + ScopedPointer keyMappings; ApplicationCommandTarget* firstTarget; - void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo& info); - void handleAsyncUpdate(); - void globalFocusChanged (Component*); + void sendListenerInvokeCallback (const ApplicationCommandTarget::InvocationInfo&); + void handleAsyncUpdate() override; + void globalFocusChanged (Component*) override; #if JUCE_CATCH_DEPRECATED_CODE_MISUSE // This is just here to cause a compile error in old code that hasn't been changed to use the new @@ -328,7 +315,7 @@ private: virtual short getFirstCommandTarget() { return 0; } #endif - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandManager) }; @@ -348,7 +335,7 @@ public: virtual ~ApplicationCommandManagerListener() {} /** Called when an app command is about to be invoked. */ - virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo& info) = 0; + virtual void applicationCommandInvoked (const ApplicationCommandTarget::InvocationInfo&) = 0; /** Called when commands are registered or deregistered from the command manager, or when commands are made active or inactive. @@ -362,4 +349,4 @@ public: -#endif // __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ +#endif // JUCE_APPLICATIONCOMMANDMANAGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp index af288d5..c31d187 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,15 +25,14 @@ class ApplicationCommandTarget::CommandMessage : public MessageManager::MessageBase { public: - CommandMessage (ApplicationCommandTarget* const owner_, const InvocationInfo& info_) - : owner (owner_), info (info_) + CommandMessage (ApplicationCommandTarget* const target, const InvocationInfo& inf) + : owner (target), info (inf) { } - void messageCallback() + void messageCallback() override { - ApplicationCommandTarget* const target = owner; - if (target != nullptr) + if (ApplicationCommandTarget* const target = owner) target->tryToInvoke (info, false); } @@ -42,7 +40,7 @@ private: WeakReference owner; const InvocationInfo info; - JUCE_DECLARE_NON_COPYABLE (CommandMessage); + JUCE_DECLARE_NON_COPYABLE (CommandMessage) }; //============================================================================== @@ -65,15 +63,14 @@ bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bo (new CommandMessage (this, info))->post(); return true; } - else - { - const bool success = perform (info); - jassert (success); // hmm - your target should have been able to perform this command. If it can't - // do it at the moment for some reason, it should clear the 'isActive' flag when it - // returns the command's info. - return success; - } + if (perform (info)) + return true; + + // Hmm.. your target claimed that it could perform this command, but failed to do so. + // If it can't do it at the moment for some reason, it should clear the 'isActive' flag + // when it returns the command's info. + jassertfalse; } return false; @@ -81,9 +78,7 @@ bool ApplicationCommandTarget::tryToInvoke (const InvocationInfo& info, const bo ApplicationCommandTarget* ApplicationCommandTarget::findFirstTargetParentComponent() { - Component* c = dynamic_cast (this); - - if (c != nullptr) + if (Component* const c = dynamic_cast (this)) return c->findParentComponentOfClass(); return nullptr; @@ -96,7 +91,7 @@ ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const C while (target != nullptr) { - Array commandIDs; + Array commandIDs; target->getAllCommands (commandIDs); if (commandIDs.contains (commandID)) @@ -118,7 +113,7 @@ ApplicationCommandTarget* ApplicationCommandTarget::getTargetForCommand (const C if (target != nullptr) { - Array commandIDs; + Array commandIDs; target->getAllCommands (commandIDs); if (commandIDs.contains (commandID)) @@ -180,8 +175,8 @@ bool ApplicationCommandTarget::invokeDirectly (const CommandID commandID, const } //============================================================================== -ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID commandID_) - : commandID (commandID_), +ApplicationCommandTarget::InvocationInfo::InvocationInfo (const CommandID command) + : commandID (command), commandFlags (0), invocationMethod (direct), originatingComponent (nullptr), diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h index 1833898..c14faa8 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandTarget.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ -#define __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ - -#include "juce_ApplicationCommandInfo.h" -class Component; +#ifndef JUCE_APPLICATIONCOMMANDTARGET_H_INCLUDED +#define JUCE_APPLICATIONCOMMANDTARGET_H_INCLUDED //============================================================================== @@ -57,6 +53,7 @@ public: //============================================================================== /** + Contains contextual details about the invocation of a command. */ struct JUCE_API InvocationInfo { @@ -68,7 +65,6 @@ public: CommandID commandID; /** The command's flags. - See ApplicationCommandInfo for a description of these flag values. */ int commandFlags; @@ -124,7 +120,7 @@ public: 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. + It may return nullptr 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. @@ -138,7 +134,7 @@ public: Your target should add all the command IDs that it handles to the array that is passed-in. */ - virtual void getAllCommands (Array & commands) = 0; + virtual void getAllCommands (Array& commands) = 0; /** This must provide details about one of the commands that this target can perform. @@ -242,8 +238,8 @@ private: bool tryToInvoke (const InvocationInfo&, bool async); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationCommandTarget) }; -#endif // __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ +#endif // JUCE_APPLICATIONCOMMANDTARGET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp index 6671b1c..b37a981 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.cpp @@ -1,40 +1,35 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager* const commandManager_) - : commandManager (commandManager_) +KeyPressMappingSet::KeyPressMappingSet (ApplicationCommandManager& cm) + : commandManager (cm) { - // A manager is needed to get the descriptions of commands, and will be called when - // a command is invoked. So you can't leave this null.. - jassert (commandManager_ != nullptr); - Desktop::getInstance().addFocusChangeListener (this); } KeyPressMappingSet::KeyPressMappingSet (const KeyPressMappingSet& other) - : commandManager (other.commandManager) + : KeyListener(), ChangeBroadcaster(), FocusChangeListener(), commandManager (other.commandManager) { Desktop::getInstance().addFocusChangeListener (this); } @@ -54,9 +49,7 @@ Array KeyPressMappingSet::getKeyPressesAssignedToCommand (const Comman return Array(); } -void KeyPressMappingSet::addKeyPress (const CommandID commandID, - const KeyPress& newKeyPress, - int insertIndex) +void KeyPressMappingSet::addKeyPress (const CommandID commandID, const KeyPress& newKeyPress, int insertIndex) { // If you specify an upper-case letter but no shift key, how is the user supposed to press it!? // Stick to lower-case letters when defining a keypress, to avoid ambiguity. @@ -78,9 +71,7 @@ void KeyPressMappingSet::addKeyPress (const CommandID commandID, } } - const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID); - - if (ci != nullptr) + if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID)) { CommandMapping* const cm = new CommandMapping(); cm->commandID = commandID; @@ -90,24 +81,28 @@ void KeyPressMappingSet::addKeyPress (const CommandID commandID, mappings.add (cm); sendChangeMessage(); } + else + { + // If you hit this, you're trying to attach a keypress to a command ID that + // doesn't exist, so the key is not being attached. + jassertfalse; + } } } } +static void addKeyPresses (KeyPressMappingSet& set, const ApplicationCommandInfo* const ci) +{ + for (int j = 0; j < ci->defaultKeypresses.size(); ++j) + set.addKeyPress (ci->commandID, ci->defaultKeypresses.getReference (j)); +} + void KeyPressMappingSet::resetToDefaultMappings() { mappings.clear(); - for (int i = 0; i < commandManager->getNumCommands(); ++i) - { - const ApplicationCommandInfo* const ci = commandManager->getCommandForIndex (i); - - for (int j = 0; j < ci->defaultKeypresses.size(); ++j) - { - addKeyPress (ci->commandID, - ci->defaultKeypresses.getReference (j)); - } - } + for (int i = 0; i < commandManager.getNumCommands(); ++i) + addKeyPresses (*this, commandManager.getCommandForIndex (i)); sendChangeMessage(); } @@ -116,13 +111,8 @@ void KeyPressMappingSet::resetToDefaultMapping (const CommandID commandID) { clearAllKeyPresses (commandID); - const ApplicationCommandInfo* const ci = commandManager->getCommandForID (commandID); - - for (int j = 0; j < ci->defaultKeypresses.size(); ++j) - { - addKeyPress (ci->commandID, - ci->defaultKeypresses.getReference (j)); - } + if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (commandID)) + addKeyPresses (*this, ci); } void KeyPressMappingSet::clearAllKeyPresses() @@ -152,13 +142,13 @@ void KeyPressMappingSet::removeKeyPress (const KeyPress& keypress) { for (int i = mappings.size(); --i >= 0;) { - CommandMapping* const cm = mappings.getUnchecked(i); + CommandMapping& cm = *mappings.getUnchecked(i); - for (int j = cm->keypresses.size(); --j >= 0;) + for (int j = cm.keypresses.size(); --j >= 0;) { - if (keypress == cm->keypresses [j]) + if (keypress == cm.keypresses [j]) { - cm->keypresses.remove (j); + cm.keypresses.remove (j); sendChangeMessage(); } } @@ -212,7 +202,7 @@ void KeyPressMappingSet::invokeCommand (const CommandID commandID, info.millisecsSinceKeyPressed = millisecsSinceKeyPressed; info.originatingComponent = originatingComponent; - commandManager->invoke (info, false); + commandManager.invoke (info, false); } //============================================================================== @@ -247,8 +237,9 @@ bool KeyPressMappingSet::restoreFromXml (const XmlElement& xmlVersion) } else if (map->hasTagName ("UNMAPPING")) { - if (containsMapping (commandId, key)) - removeKeyPress (key); + for (int i = mappings.size(); --i >= 0;) + if (mappings.getUnchecked(i)->commandID == commandId) + mappings.getUnchecked(i)->keypresses.removeAllInstancesOf (key); } } } @@ -273,40 +264,39 @@ XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefault doc->setAttribute ("basedOnDefaults", saveDifferencesFromDefaultSet); - int i; - for (i = 0; i < mappings.size(); ++i) + for (int i = 0; i < mappings.size(); ++i) { - const CommandMapping* const cm = mappings.getUnchecked(i); + const CommandMapping& cm = *mappings.getUnchecked(i); - for (int j = 0; j < cm->keypresses.size(); ++j) + for (int j = 0; j < cm.keypresses.size(); ++j) { if (defaultSet == nullptr - || ! defaultSet->containsMapping (cm->commandID, cm->keypresses.getReference (j))) + || ! defaultSet->containsMapping (cm.commandID, cm.keypresses.getReference (j))) { XmlElement* const map = doc->createNewChildElement ("MAPPING"); - map->setAttribute ("commandId", String::toHexString ((int) cm->commandID)); - map->setAttribute ("description", commandManager->getDescriptionOfCommand (cm->commandID)); - map->setAttribute ("key", cm->keypresses.getReference (j).getTextDescription()); + map->setAttribute ("commandId", String::toHexString ((int) cm.commandID)); + map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID)); + map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription()); } } } if (defaultSet != nullptr) { - for (i = 0; i < defaultSet->mappings.size(); ++i) + for (int i = 0; i < defaultSet->mappings.size(); ++i) { - const CommandMapping* const cm = defaultSet->mappings.getUnchecked(i); + const CommandMapping& cm = *defaultSet->mappings.getUnchecked(i); - for (int j = 0; j < cm->keypresses.size(); ++j) + for (int j = 0; j < cm.keypresses.size(); ++j) { - if (! containsMapping (cm->commandID, cm->keypresses.getReference (j))) + if (! containsMapping (cm.commandID, cm.keypresses.getReference (j))) { XmlElement* const map = doc->createNewChildElement ("UNMAPPING"); - map->setAttribute ("commandId", String::toHexString ((int) cm->commandID)); - map->setAttribute ("description", commandManager->getDescriptionOfCommand (cm->commandID)); - map->setAttribute ("key", cm->keypresses.getReference (j).getTextDescription()); + map->setAttribute ("commandId", String::toHexString ((int) cm.commandID)); + map->setAttribute ("description", commandManager.getDescriptionOfCommand (cm.commandID)); + map->setAttribute ("key", cm.keypresses.getReference (j).getTextDescription()); } } } @@ -316,33 +306,30 @@ XmlElement* KeyPressMappingSet::createXml (const bool saveDifferencesFromDefault } //============================================================================== -bool KeyPressMappingSet::keyPressed (const KeyPress& key, - Component* originatingComponent) +bool KeyPressMappingSet::keyPressed (const KeyPress& key, Component* const originatingComponent) { bool commandWasDisabled = false; for (int i = 0; i < mappings.size(); ++i) { - CommandMapping* const cm = mappings.getUnchecked(i); + CommandMapping& cm = *mappings.getUnchecked(i); - if (cm->keypresses.contains (key)) + if (cm.keypresses.contains (key)) { - const ApplicationCommandInfo* const ci = commandManager->getCommandForID (cm->commandID); - - if (ci != nullptr - && (ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0) + if (const ApplicationCommandInfo* const ci = commandManager.getCommandForID (cm.commandID)) { - ApplicationCommandInfo info (0); - - if (commandManager->getTargetForCommand (cm->commandID, info) != 0) + if ((ci->flags & ApplicationCommandInfo::wantsKeyUpDownCallbacks) == 0) { - if ((info.flags & ApplicationCommandInfo::isDisabled) == 0) - { - invokeCommand (cm->commandID, key, true, 0, originatingComponent); - return true; - } - else + ApplicationCommandInfo info (0); + + if (commandManager.getTargetForCommand (cm.commandID, info) != nullptr) { + if ((info.flags & ApplicationCommandInfo::isDisabled) == 0) + { + invokeCommand (cm.commandID, key, true, 0, originatingComponent); + return true; + } + commandWasDisabled = true; } } @@ -363,13 +350,13 @@ bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* o for (int i = mappings.size(); --i >= 0;) { - CommandMapping* const cm = mappings.getUnchecked(i); + CommandMapping& cm = *mappings.getUnchecked(i); - if (cm->wantsKeyUpDownCallbacks) + if (cm.wantsKeyUpDownCallbacks) { - for (int j = cm->keypresses.size(); --j >= 0;) + for (int j = cm.keypresses.size(); --j >= 0;) { - const KeyPress key (cm->keypresses.getReference (j)); + const KeyPress key (cm.keypresses.getReference (j)); const bool isDown = key.isCurrentlyDown(); int keyPressEntryIndex = 0; @@ -408,7 +395,7 @@ bool KeyPressMappingSet::keyStateChanged (const bool /*isKeyDown*/, Component* o keysDown.remove (keyPressEntryIndex); } - invokeCommand (cm->commandID, key, isDown, millisecs, originatingComponent); + invokeCommand (cm.commandID, key, isDown, millisecs, originatingComponent); used = true; } } diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h index 99d2cae..d3351ac 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h @@ -1,39 +1,35 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ -#define __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ - -#include "../keyboard/juce_KeyListener.h" -#include "../commands/juce_ApplicationCommandManager.h" +#ifndef JUCE_KEYPRESSMAPPINGSET_H_INCLUDED +#define JUCE_KEYPRESSMAPPINGSET_H_INCLUDED //============================================================================== /** Manages and edits a list of keypresses, which it uses to invoke the appropriate - command in a ApplicationCommandManager. + command in an ApplicationCommandManager. Normally, you won't actually create a KeyPressMappingSet directly, because each ApplicationCommandManager contains its own KeyPressMappingSet, so typically @@ -87,7 +83,7 @@ */ class JUCE_API KeyPressMappingSet : public KeyListener, public ChangeBroadcaster, - public FocusChangeListener + private FocusChangeListener { public: //============================================================================== @@ -103,16 +99,16 @@ public: @see ApplicationCommandManager */ - explicit KeyPressMappingSet (ApplicationCommandManager* commandManager); + explicit KeyPressMappingSet (ApplicationCommandManager&); /** Creates an copy of a KeyPressMappingSet. */ - KeyPressMappingSet (const KeyPressMappingSet& other); + KeyPressMappingSet (const KeyPressMappingSet&); /** Destructor. */ ~KeyPressMappingSet(); //============================================================================== - ApplicationCommandManager* getCommandManager() const noexcept { return commandManager; } + ApplicationCommandManager& getCommandManager() const noexcept { return commandManager; } //============================================================================== /** Returns a list of keypresses that are assigned to a particular command. @@ -139,13 +135,11 @@ public: 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); @@ -157,14 +151,12 @@ public: 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. - */ + /** 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. */ @@ -172,7 +164,6 @@ public: //============================================================================== /** 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 noexcept; @@ -216,24 +207,24 @@ public: //============================================================================== /** @internal */ - bool keyPressed (const KeyPress& key, Component* originatingComponent); + bool keyPressed (const KeyPress&, Component*) override; /** @internal */ - bool keyStateChanged (bool isKeyDown, Component* originatingComponent); + bool keyStateChanged (bool isKeyDown, Component*) override; /** @internal */ - void globalFocusChanged (Component* focusedComponent); + void globalFocusChanged (Component*) override; private: //============================================================================== - ApplicationCommandManager* commandManager; + ApplicationCommandManager& commandManager; struct CommandMapping { CommandID commandID; - Array keypresses; + Array keypresses; bool wantsKeyUpDownCallbacks; }; - OwnedArray mappings; + OwnedArray mappings; struct KeyPressTime { @@ -241,19 +232,14 @@ private: uint32 timeWhenPressed; }; - OwnedArray keysDown; + OwnedArray keysDown; - void handleMessage (const Message& message); - - void invokeCommand (const CommandID commandID, - const KeyPress& keyPress, - const bool isKeyDown, - const int millisecsSinceKeyPressed, - Component* const originatingComponent) const; + void invokeCommand (const CommandID, const KeyPress&, const bool isKeyDown, + const int millisecsSinceKeyPressed, Component* originator) const; KeyPressMappingSet& operator= (const KeyPressMappingSet&); - JUCE_LEAK_DETECTOR (KeyPressMappingSet); + JUCE_LEAK_DETECTOR (KeyPressMappingSet) }; -#endif // __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ +#endif // JUCE_KEYPRESSMAPPINGSET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_CachedComponentImage.h b/JuceLibraryCode/modules/juce_gui_basics/components/juce_CachedComponentImage.h index dbe7845..3f273cb 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_CachedComponentImage.h +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_CachedComponentImage.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__ -#define __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__ - -class Component; +#ifndef JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED +#define JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED //============================================================================== @@ -52,11 +49,17 @@ public: */ virtual void paint (Graphics&) = 0; - /** Invalidates all cached image data. */ - virtual void invalidateAll() = 0; + /** Invalidates all cached image data. + @returns true if the peer should also be repainted, or false if this object + handles all repaint work internally. + */ + virtual bool invalidateAll() = 0; - /** Invalidates a section of the cached image data. */ - virtual void invalidate (const Rectangle& area) = 0; + /** Invalidates a section of the cached image data. + @returns true if the peer should also be repainted, or false if this object + handles all repaint work internally. + */ + virtual bool invalidate (const Rectangle& area) = 0; /** Called to indicate that the component is no longer active, so any cached data should be released if possible. @@ -64,4 +67,4 @@ public: virtual void releaseResources() = 0; }; -#endif // __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__ +#endif // JUCE_CACHEDCOMPONENTIMAGE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp index 3feb3cd..8734ad7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -76,20 +75,16 @@ public: if (checker.shouldBailOut()) return; + if (MouseListenerList* const list = comp.mouseListeners) { - MouseListenerList* const list = comp.mouseListeners; - - if (list != nullptr) + for (int i = list->listeners.size(); --i >= 0;) { - for (int i = list->listeners.size(); --i >= 0;) - { - (list->listeners.getUnchecked(i)->*eventMethod) (e); + (list->listeners.getUnchecked(i)->*eventMethod) (e); - if (checker.shouldBailOut()) - return; + if (checker.shouldBailOut()) + return; - i = jmin (i, list->listeners.size()); - } + i = jmin (i, list->listeners.size()); } } @@ -114,23 +109,19 @@ public: } } - static void sendWheelEvent (Component& comp, Component::BailOutChecker& checker, const MouseEvent& e, - const float wheelIncrementX, const float wheelIncrementY) + static void sendWheelEvent (Component& comp, Component::BailOutChecker& checker, + const MouseEvent& e, const MouseWheelDetails& wheel) { + if (MouseListenerList* const list = comp.mouseListeners) { - MouseListenerList* const list = comp.mouseListeners; - - if (list != nullptr) + for (int i = list->listeners.size(); --i >= 0;) { - for (int i = list->listeners.size(); --i >= 0;) - { - list->listeners.getUnchecked(i)->mouseWheelMove (e, wheelIncrementX, wheelIncrementY); + list->listeners.getUnchecked(i)->mouseWheelMove (e, wheel); - if (checker.shouldBailOut()) - return; + if (checker.shouldBailOut()) + return; - i = jmin (i, list->listeners.size()); - } + i = jmin (i, list->listeners.size()); } } @@ -144,7 +135,7 @@ public: for (int i = list->numDeepMouseListeners; --i >= 0;) { - list->listeners.getUnchecked(i)->mouseWheelMove (e, wheelIncrementX, wheelIncrementY); + list->listeners.getUnchecked(i)->mouseWheelMove (e, wheel); if (checker2.shouldBailOut()) return; @@ -162,8 +153,8 @@ private: class BailOutChecker2 { public: - BailOutChecker2 (Component::BailOutChecker& checker_, Component* const component) - : checker (checker_), safePointer (component) + BailOutChecker2 (Component::BailOutChecker& boc, Component* const comp) + : checker (boc), safePointer (comp) { } @@ -176,12 +167,85 @@ private: Component::BailOutChecker& checker; const WeakReference safePointer; - JUCE_DECLARE_NON_COPYABLE (BailOutChecker2); + JUCE_DECLARE_NON_COPYABLE (BailOutChecker2) }; - JUCE_DECLARE_NON_COPYABLE (MouseListenerList); + JUCE_DECLARE_NON_COPYABLE (MouseListenerList) }; +//============================================================================== +struct FocusRestorer +{ + FocusRestorer() : lastFocus (Component::getCurrentlyFocusedComponent()) {} + + ~FocusRestorer() + { + if (lastFocus != nullptr && ! lastFocus->isCurrentlyBlockedByAnotherModalComponent()) + lastFocus->grabKeyboardFocus(); + } + + WeakReference lastFocus; + + JUCE_DECLARE_NON_COPYABLE (FocusRestorer) +}; + +//============================================================================== +struct ScalingHelpers +{ + template + static PointOrRect unscaledScreenPosToScaled (float scale, PointOrRect pos) noexcept + { + return scale != 1.0f ? pos / scale : pos; + } + + template + static PointOrRect scaledScreenPosToUnscaled (float scale, PointOrRect pos) noexcept + { + return scale != 1.0f ? pos * scale : pos; + } + + // For these, we need to avoid getSmallestIntegerContainer being used, which causes + // judder when moving windows + static Rectangle unscaledScreenPosToScaled (float scale, const Rectangle& pos) noexcept + { + return scale != 1.0f ? Rectangle (roundToInt (pos.getX() / scale), + roundToInt (pos.getY() / scale), + roundToInt (pos.getWidth() / scale), + roundToInt (pos.getHeight() / scale)) : pos; + } + + static Rectangle scaledScreenPosToUnscaled (float scale, Rectangle& pos) noexcept + { + return scale != 1.0f ? Rectangle (roundToInt (pos.getX() * scale), + roundToInt (pos.getY() * scale), + roundToInt (pos.getWidth() * scale), + roundToInt (pos.getHeight() * scale)) : pos; + } + + template + static PointOrRect unscaledScreenPosToScaled (PointOrRect pos) noexcept + { + return unscaledScreenPosToScaled (Desktop::getInstance().getGlobalScaleFactor(), pos); + } + + template + static PointOrRect scaledScreenPosToUnscaled (PointOrRect pos) noexcept + { + return scaledScreenPosToUnscaled (Desktop::getInstance().getGlobalScaleFactor(), pos); + } + + template + static PointOrRect unscaledScreenPosToScaled (const Component& comp, PointOrRect pos) noexcept + { + return unscaledScreenPosToScaled (comp.getDesktopScaleFactor(), pos); + } + + template + static PointOrRect scaledScreenPosToUnscaled (const Component& comp, PointOrRect pos) noexcept + { + return scaledScreenPosToUnscaled (comp.getDesktopScaleFactor(), pos); + } +}; //============================================================================== struct Component::ComponentHelpers @@ -193,56 +257,106 @@ struct Component::ComponentHelpers } #endif - static Identifier getColourPropertyId (const int colourId) + static Identifier getColourPropertyId (int colourId) { - String s; - s.preallocateBytes (32); - s << "jcclr_" << String::toHexString (colourId); - return s; + char reversedHex[32]; + char* t = reversedHex; + + for (unsigned int v = (unsigned int) colourId;;) + { + *t++ = "0123456789abcdef" [(int) (v & 15)]; + v >>= 4; + + if (v == 0) + break; + } + + char destBuffer[32]; + char* dest = destBuffer; + memcpy (dest, "jcclr_", 6); + dest += 6; + + while (t > reversedHex) + *dest++ = *--t; + + *dest++ = 0; + return destBuffer; } //============================================================================== - static inline bool hitTest (Component& comp, const Point& localPoint) + static inline bool hitTest (Component& comp, Point localPoint) { return isPositiveAndBelow (localPoint.x, comp.getWidth()) - && isPositiveAndBelow (localPoint.y, comp.getHeight()) - && comp.hitTest (localPoint.x, localPoint.y); + && isPositiveAndBelow (localPoint.y, comp.getHeight()) + && comp.hitTest (localPoint.x, localPoint.y); } - static Point convertFromParentSpace (const Component& comp, const Point& pointInParentSpace) + // converts an unscaled position within a peer to the local position within that peer's component + template + static PointOrRect rawPeerPositionToLocal (const Component& comp, PointOrRect pos) noexcept { - if (comp.affineTransform == nullptr) - return pointInParentSpace - comp.getPosition(); + if (comp.isTransformed()) + pos = pos.transformedBy (comp.getTransform().inverted()); - return pointInParentSpace.toFloat().transformedBy (comp.affineTransform->inverted()).toInt() - comp.getPosition(); + return ScalingHelpers::unscaledScreenPosToScaled (comp, pos); } - static Rectangle convertFromParentSpace (const Component& comp, const Rectangle& areaInParentSpace) + // converts a position within a peer's component to the unscaled position within the peer + template + static PointOrRect localPositionToRawPeerPos (const Component& comp, PointOrRect pos) noexcept { - if (comp.affineTransform == nullptr) - return areaInParentSpace - comp.getPosition(); + if (comp.isTransformed()) + pos = pos.transformedBy (comp.getTransform()); - return areaInParentSpace.toFloat().transformed (comp.affineTransform->inverted()).getSmallestIntegerContainer() - comp.getPosition(); + return ScalingHelpers::scaledScreenPosToUnscaled (comp, pos); } - static Point convertToParentSpace (const Component& comp, const Point& pointInLocalSpace) + template + static PointOrRect convertFromParentSpace (const Component& comp, PointOrRect pointInParentSpace) { - if (comp.affineTransform == nullptr) - return pointInLocalSpace + comp.getPosition(); + if (comp.affineTransform != nullptr) + pointInParentSpace = pointInParentSpace.transformedBy (comp.affineTransform->inverted()); - return (pointInLocalSpace + comp.getPosition()).toFloat().transformedBy (*comp.affineTransform).toInt(); + if (comp.isOnDesktop()) + { + if (ComponentPeer* peer = comp.getPeer()) + pointInParentSpace = ScalingHelpers::unscaledScreenPosToScaled + (comp, peer->globalToLocal (ScalingHelpers::scaledScreenPosToUnscaled (pointInParentSpace))); + else + jassertfalse; + } + else + { + pointInParentSpace -= comp.getPosition(); + } + + return pointInParentSpace; } - static Rectangle convertToParentSpace (const Component& comp, const Rectangle& areaInLocalSpace) + template + static PointOrRect convertToParentSpace (const Component& comp, PointOrRect pointInLocalSpace) { - if (comp.affineTransform == nullptr) - return areaInLocalSpace + comp.getPosition(); + if (comp.isOnDesktop()) + { + if (ComponentPeer* peer = comp.getPeer()) + pointInLocalSpace = ScalingHelpers::unscaledScreenPosToScaled + (peer->localToGlobal (ScalingHelpers::scaledScreenPosToUnscaled (comp, pointInLocalSpace))); + else + jassertfalse; + } + else + { + pointInLocalSpace += comp.getPosition(); + } - return (areaInLocalSpace + comp.getPosition()).toFloat().transformed (*comp.affineTransform).getSmallestIntegerContainer(); + if (comp.affineTransform != nullptr) + pointInLocalSpace = pointInLocalSpace.transformedBy (*comp.affineTransform); + + return pointInLocalSpace; } - template - static Type convertFromDistantParentSpace (const Component* parent, const Component& target, const Type& coordInParent) + template + static PointOrRect convertFromDistantParentSpace (const Component* parent, const Component& target, const PointOrRect& coordInParent) { const Component* const directParent = target.getParentComponent(); jassert (directParent != nullptr); @@ -253,8 +367,8 @@ struct Component::ComponentHelpers return convertFromParentSpace (target, convertFromDistantParentSpace (parent, *directParent, coordInParent)); } - template - static Type convertCoordinate (const Component* target, const Component* source, Type p) + template + static PointOrRect convertCoordinate (const Component* target, const Component* source, PointOrRect p) { while (source != nullptr) { @@ -264,16 +378,8 @@ struct Component::ComponentHelpers if (source->isParentOf (target)) return convertFromDistantParentSpace (source, *target, p); - if (source->isOnDesktop()) - { - p = source->getPeer()->localToGlobal (p); - source = nullptr; - } - else - { - p = convertToParentSpace (*source, p); - source = source->getParentComponent(); - } + p = convertToParentSpace (*source, p); + source = source->getParentComponent(); } jassert (source == nullptr); @@ -282,10 +388,7 @@ struct Component::ComponentHelpers const Component* const topLevelComp = target->getTopLevelComponent(); - if (topLevelComp->isOnDesktop()) - p = topLevelComp->getPeer()->globalToLocal (p); - else - p = convertFromParentSpace (*topLevelComp, p); + p = convertFromParentSpace (*topLevelComp, p); if (topLevelComp == target) return p; @@ -297,16 +400,16 @@ struct Component::ComponentHelpers { Rectangle r (comp.getLocalBounds()); - Component* const p = comp.getParentComponent(); - - if (p != nullptr) + if (Component* const p = comp.getParentComponent()) r = r.getIntersection (convertFromParentSpace (comp, getUnclippedArea (*p))); return r; } - static void clipObscuredRegions (const Component& comp, Graphics& g, const Rectangle& clipRect, const Point& delta) + static bool clipObscuredRegions (const Component& comp, Graphics& g, const Rectangle& clipRect, Point delta) { + bool nothingChanged = true; + for (int i = comp.childComponentList.size(); --i >= 0;) { const Component& child = *comp.childComponentList.getUnchecked(i); @@ -320,19 +423,23 @@ struct Component::ComponentHelpers if (child.isOpaque() && child.componentTransparency == 0) { g.excludeClipRegion (newClip + delta); + nothingChanged = false; } else { const Point childPos (child.getPosition()); - clipObscuredRegions (child, g, newClip - childPos, childPos + delta); + if (clipObscuredRegions (child, g, newClip - childPos, childPos + delta)) + nothingChanged = false; } } } } + + return nothingChanged; } - static void subtractObscuredRegions (const Component& comp, RectangleList& result, - const Point& delta, const Rectangle& clipRect, + static void subtractObscuredRegions (const Component& comp, RectangleList& result, + Point delta, const Rectangle& clipRect, const Component* const compToAvoid) { for (int i = comp.childComponentList.size(); --i >= 0;) @@ -362,8 +469,10 @@ struct Component::ComponentHelpers static Rectangle getParentOrMainMonitorBounds (const Component& comp) { - return comp.getParentComponent() != nullptr ? comp.getParentComponent()->getLocalBounds() - : Desktop::getInstance().getMainMonitorArea(); + if (Component* p = comp.getParentComponent()) + return p->getLocalBounds(); + + return Desktop::getInstance().getDisplays().getMainDisplay().userArea; } }; @@ -422,13 +531,8 @@ void Component::setName (const String& name) componentName = name; if (flags.hasHeavyweightPeerFlag) - { - ComponentPeer* const peer = getPeer(); - - jassert (peer != nullptr); - if (peer != nullptr) + if (ComponentPeer* const peer = getPeer()) peer->setTitle (name); - } BailOutChecker checker (this); componentListeners.callChecked (checker, &ComponentListener::componentNameChanged, *this); @@ -478,10 +582,7 @@ void Component::setVisible (bool shouldBeVisible) if (safePointer != nullptr && flags.hasHeavyweightPeerFlag) { - ComponentPeer* const peer = getPeer(); - - jassert (peer != nullptr); - if (peer != nullptr) + if (ComponentPeer* const peer = getPeer()) { peer->setVisible (shouldBeVisible); internalHierarchyChanged(); @@ -510,17 +611,17 @@ bool Component::isShowing() const if (parentComponent != nullptr) return parentComponent->isShowing(); - const ComponentPeer* const peer = getPeer(); - return peer != nullptr && ! peer->isMinimised(); + if (const ComponentPeer* const peer = getPeer()) + return ! peer->isMinimised(); + + return false; } //============================================================================== void* Component::getWindowHandle() const { - const ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) + if (const ComponentPeer* const peer = getPeer()) return peer->getNativeHandle(); return nullptr; @@ -538,16 +639,11 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) else styleWanted |= ComponentPeer::windowIsSemiTransparent; - int currentStyleFlags = 0; - // don't use getPeer(), so that we only get the peer that's specifically // for this comp, and not for one of its parents. ComponentPeer* peer = ComponentPeer::getPeerFor (this); - if (peer != nullptr) - currentStyleFlags = peer->getStyleFlags(); - - if (styleWanted != currentStyleFlags || ! flags.hasHeavyweightPeerFlag) + if (peer == nullptr || styleWanted != peer->getStyleFlags()) { const WeakReference safePointer (this); @@ -563,8 +659,9 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) bool wasFullscreen = false; bool wasMinimised = false; - ComponentBoundsConstrainer* currentConstainer = nullptr; + ComponentBoundsConstrainer* currentConstrainer = nullptr; Rectangle oldNonFullScreenBounds; + int oldRenderingEngine = -1; if (peer != nullptr) { @@ -572,8 +669,9 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) wasFullscreen = peer->isFullScreen(); wasMinimised = peer->isMinimised(); - currentConstainer = peer->getConstrainer(); + currentConstrainer = peer->getConstrainer(); oldNonFullScreenBounds = peer->getNonFullScreenBounds(); + oldRenderingEngine = peer->getCurrentRenderingEngine(); flags.hasHeavyweightPeerFlag = false; Desktop::getInstance().removeDesktopComponent (this); @@ -597,7 +695,11 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) Desktop::getInstance().addDesktopComponent (this); bounds.setPosition (topLeft); - peer->setBounds (topLeft.x, topLeft.y, getWidth(), getHeight(), false); + peer->updateBounds(); + + if (oldRenderingEngine >= 0) + peer->setCurrentRenderingEngine (oldRenderingEngine); + peer->setVisible (isVisible()); peer = ComponentPeer::getPeerFor (this); @@ -618,7 +720,7 @@ void Component::addToDesktop (int styleWanted, void* nativeWindowToAttachTo) peer->setAlwaysOnTop (true); #endif - peer->setConstrainer (currentConstainer); + peer->setConstrainer (currentConstrainer); repaint(); internalHierarchyChanged(); @@ -653,7 +755,8 @@ ComponentPeer* Component::getPeer() const { if (flags.hasHeavyweightPeerFlag) return ComponentPeer::getPeerFor (this); - else if (parentComponent == nullptr) + + if (parentComponent == nullptr) return nullptr; return parentComponent->getPeer(); @@ -673,6 +776,8 @@ void Component::userTriedToCloseWindow() void Component::minimisationStateChanged (bool) {} +float Component::getDesktopScaleFactor() const { return Desktop::getInstance().getGlobalScaleFactor(); } + //============================================================================== void Component::setOpaque (const bool shouldBeOpaque) { @@ -681,15 +786,8 @@ void Component::setOpaque (const bool shouldBeOpaque) flags.opaqueFlag = shouldBeOpaque; if (flags.hasHeavyweightPeerFlag) - { - const ComponentPeer* const peer = ComponentPeer::getPeerFor (this); - - if (peer != nullptr) - { - // to make it recreate the heavyweight window - addToDesktop (peer->getStyleFlags()); - } - } + if (const ComponentPeer* const peer = ComponentPeer::getPeerFor (this)) + addToDesktop (peer->getStyleFlags()); // recreates the heavyweight window repaint(); } @@ -704,56 +802,64 @@ bool Component::isOpaque() const noexcept class StandardCachedComponentImage : public CachedComponentImage { public: - StandardCachedComponentImage (Component& owner_) noexcept : owner (owner_) {} + StandardCachedComponentImage (Component& c) noexcept : owner (c), scale (1.0f) {} - void paint (Graphics& g) + void paint (Graphics& g) override { - const Rectangle bounds (owner.getLocalBounds()); + scale = g.getInternalContext().getPhysicalPixelScaleFactor(); + const Rectangle compBounds (owner.getLocalBounds()); + const Rectangle imageBounds (compBounds * scale); - if (image.isNull() || image.getBounds() != bounds) + if (image.isNull() || image.getBounds() != imageBounds) { - image = Image (owner.isOpaque() ? Image::RGB : Image::ARGB, - jmax (1, bounds.getWidth()), jmax (1, bounds.getHeight()), ! owner.isOpaque()); + image = Image (owner.isOpaque() ? Image::RGB + : Image::ARGB, + jmax (1, imageBounds.getWidth()), + jmax (1, imageBounds.getHeight()), + ! owner.isOpaque()); validArea.clear(); } { Graphics imG (image); - LowLevelGraphicsContext* const lg = imG.getInternalContext(); + LowLevelGraphicsContext& lg = imG.getInternalContext(); - for (RectangleList::Iterator i (validArea); i.next();) - lg->excludeClipRectangle (*i.getRectangle()); + for (const Rectangle* i = validArea.begin(), * const e = validArea.end(); i != e; ++i) + lg.excludeClipRectangle (*i); - if (! lg->isClipEmpty()) + if (! lg.isClipEmpty()) { if (! owner.isOpaque()) { - lg->setFill (Colours::transparentBlack); - lg->fillRect (bounds, true); - lg->setFill (Colours::black); + lg.setFill (Colours::transparentBlack); + lg.fillRect (imageBounds, true); + lg.setFill (Colours::black); } + lg.addTransform (AffineTransform::scale (scale)); owner.paintEntireComponent (imG, true); } } - validArea = bounds; + validArea = imageBounds; g.setColour (Colours::black.withAlpha (owner.getAlpha())); - g.drawImageAt (image, 0, 0); + g.drawImageTransformed (image, AffineTransform::scale (compBounds.getWidth() / (float) imageBounds.getWidth(), + compBounds.getHeight() / (float) imageBounds.getHeight()), false); } - void invalidateAll() { validArea.clear(); } - void invalidate (const Rectangle& area) { validArea.subtract (area); } - void releaseResources() { image = Image::null; } + bool invalidateAll() override { validArea.clear(); return true; } + bool invalidate (const Rectangle& area) override { validArea.subtract (area * scale); return true; } + void releaseResources() override { image = Image::null; } private: Image image; - RectangleList validArea; + RectangleList validArea; Component& owner; + float scale; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (StandardCachedComponentImage) }; void Component::setCachedComponentImage (CachedComponentImage* newCachedImage) @@ -808,9 +914,7 @@ void Component::toFront (const bool setAsForeground) if (flags.hasHeavyweightPeerFlag) { - ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) + if (ComponentPeer* const peer = getPeer()) { peer->toFront (setAsForeground); @@ -930,10 +1034,7 @@ void Component::setAlwaysOnTop (const bool shouldStayOnTop) if (isOnDesktop()) { - ComponentPeer* const peer = getPeer(); - - jassert (peer != nullptr); - if (peer != nullptr) + if (ComponentPeer* const peer = getPeer()) { if (! peer->setAlwaysOnTop (shouldStayOnTop)) { @@ -983,10 +1084,10 @@ Rectangle Component::getScreenBounds() const { return localAreaToGlobal Rectangle Component::getParentMonitorArea() const { - return Desktop::getInstance().getMonitorAreaContaining (getScreenBounds().getCentre()); + return Desktop::getInstance().getDisplays().getDisplayContaining (getScreenBounds().getCentre()).userArea; } -Point Component::getLocalPoint (const Component* source, const Point& point) const +Point Component::getLocalPoint (const Component* source, Point point) const { return ComponentHelpers::convertCoordinate (this, source, point); } @@ -996,7 +1097,7 @@ Rectangle Component::getLocalArea (const Component* source, const Rectangle return ComponentHelpers::convertCoordinate (this, source, area); } -Point Component::localPointToGlobal (const Point& point) const +Point Component::localPointToGlobal (Point point) const { return ComponentHelpers::convertCoordinate (nullptr, this, point); } @@ -1007,17 +1108,17 @@ Rectangle Component::localAreaToGlobal (const Rectangle& area) const } // Deprecated methods... -Point Component::relativePositionToGlobal (const Point& relativePosition) const +Point Component::relativePositionToGlobal (Point relativePosition) const { return localPointToGlobal (relativePosition); } -Point Component::globalPositionToRelative (const Point& screenPosition) const +Point Component::globalPositionToRelative (Point screenPosition) const { return getLocalPoint (nullptr, screenPosition); } -Point Component::relativePositionToOtherComponent (const Component* const targetComponent, const Point& positionRelativeToThis) const +Point Component::relativePositionToOtherComponent (const Component* const targetComponent, Point positionRelativeToThis) const { return targetComponent == nullptr ? localPointToGlobal (positionRelativeToThis) : targetComponent->getLocalPoint (this, positionRelativeToThis); @@ -1069,19 +1170,8 @@ void Component::setBounds (const int x, const int y, int w, int h) } if (flags.hasHeavyweightPeerFlag) - { - ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) - { - if (wasMoved && wasResized) - peer->setBounds (getX(), getY(), getWidth(), getHeight(), false); - else if (wasMoved) - peer->setPosition (getX(), getY()); - else if (wasResized) - peer->setSize (getWidth(), getHeight()); - } - } + if (ComponentPeer* const peer = getPeer()) + peer->updateBounds(); sendMovedResizedMessages (wasMoved, wasResized); } @@ -1135,7 +1225,7 @@ void Component::setTopLeftPosition (const int x, const int y) setBounds (x, y, getWidth(), getHeight()); } -void Component::setTopLeftPosition (const Point& pos) +void Component::setTopLeftPosition (Point pos) { setBounds (pos.x, pos.y, getWidth(), getHeight()); } @@ -1199,7 +1289,7 @@ void Component::setBoundsInset (const BorderSize& borders) } void Component::setBoundsToFit (int x, int y, int width, int height, - const Justification& justification, + Justification justification, const bool onlyReduceInSize) { // it's no good calling this method unless both the component and @@ -1240,11 +1330,6 @@ void Component::setBoundsToFit (int x, int y, int width, int height, } //============================================================================== -bool Component::isTransformed() const noexcept -{ - return affineTransform != nullptr; -} - void Component::setTransform (const AffineTransform& newTransform) { // If you pass in a transform with no inverse, the component will have no dimensions, @@ -1278,6 +1363,11 @@ void Component::setTransform (const AffineTransform& newTransform) } } +bool Component::isTransformed() const noexcept +{ + return affineTransform != nullptr; +} + AffineTransform Component::getTransform() const { return affineTransform != nullptr ? *affineTransform : AffineTransform::identity; @@ -1318,7 +1408,7 @@ void Component::getInterceptsMouseClicks (bool& allowsClicksOnThisComponent, allowsClicksOnChildComponents = flags.allowChildMouseClicksFlag; } -bool Component::contains (const Point& point) +bool Component::contains (Point point) { if (ComponentHelpers::hitTest (*this, point)) { @@ -1326,18 +1416,14 @@ bool Component::contains (const Point& point) return parentComponent->contains (ComponentHelpers::convertToParentSpace (*this, point)); if (flags.hasHeavyweightPeerFlag) - { - const ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) - return peer->contains (point, true); - } + if (const ComponentPeer* const peer = getPeer()) + return peer->contains (ComponentHelpers::localPositionToRawPeerPos (*this, point), true); } return false; } -bool Component::reallyContains (const Point& point, const bool returnTrueIfWithinAChild) +bool Component::reallyContains (Point point, const bool returnTrueIfWithinAChild) { if (! contains (point)) return false; @@ -1348,7 +1434,7 @@ bool Component::reallyContains (const Point& point, const bool returnTrueIf return (compAtPosition == this) || (returnTrueIfWithinAChild && isParentOf (compAtPosition)); } -Component* Component::getComponentAt (const Point& position) +Component* Component::getComponentAt (Point position) { if (flags.visibleFlag && ComponentHelpers::hitTest (*this, position)) { @@ -1373,28 +1459,25 @@ Component* Component::getComponentAt (const int x, const int y) } //============================================================================== -void Component::addChildComponent (Component* const child, int zOrder) +void Component::addChildComponent (Component& child, int zOrder) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. CHECK_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN - jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager() || ! isShowing()); - - - if (child != nullptr && child->parentComponent != this) + if (child.parentComponent != this) { - if (child->parentComponent != nullptr) - child->parentComponent->removeChildComponent (child); + if (child.parentComponent != nullptr) + child.parentComponent->removeChildComponent (&child); else - child->removeFromDesktop(); + child.removeFromDesktop(); - child->parentComponent = this; + child.parentComponent = this; - if (child->isVisible()) - child->repaintParent(); + if (child.isVisible()) + child.repaintParent(); - if (! child->isAlwaysOnTop()) + if (! child.isAlwaysOnTop()) { if (zOrder < 0 || zOrder > childComponentList.size()) zOrder = childComponentList.size(); @@ -1408,27 +1491,36 @@ void Component::addChildComponent (Component* const child, int zOrder) } } - childComponentList.insert (zOrder, child); + childComponentList.insert (zOrder, &child); - child->internalHierarchyChanged(); + child.internalHierarchyChanged(); internalChildrenChanged(); } } +void Component::addAndMakeVisible (Component& child, int zOrder) +{ + child.setVisible (true); + addChildComponent (child, zOrder); +} + +void Component::addChildComponent (Component* const child, int zOrder) +{ + if (child != nullptr) + addChildComponent (*child, zOrder); +} + void Component::addAndMakeVisible (Component* const child, int zOrder) { if (child != nullptr) - { - child->setVisible (true); - addChildComponent (child, zOrder); - } + addAndMakeVisible (*child, zOrder); } -void Component::addChildAndSetID (Component* const child, const String& componentID) +void Component::addChildAndSetID (Component* const child, const String& childID) { if (child != nullptr) { - child->setComponentID (componentID); + child->setComponentID (childID); addAndMakeVisible (child); } } @@ -1527,7 +1619,7 @@ int Component::getIndexOfChildComponent (const Component* const child) const noe return childComponentList.indexOf (const_cast (child)); } -Component* Component::findChildWithID (const String& targetID) const noexcept +Component* Component::findChildWithID (StringRef targetID) const noexcept { for (int i = childComponentList.size(); --i >= 0;) { @@ -1674,10 +1766,10 @@ void Component::exitModalState (const int returnValue) class ExitModalStateMessage : public CallbackMessage { public: - ExitModalStateMessage (Component* const target_, const int result_) - : target (target_), result (result_) {} + ExitModalStateMessage (Component* const c, const int res) + : target (c), result (res) {} - void messageCallback() + void messageCallback() override { if (target.get() != nullptr) // (get() required for VS2003 bug) target->exitModalState (result); @@ -1767,9 +1859,7 @@ void Component::setAlpha (const float newAlpha) if (flags.hasHeavyweightPeerFlag) { - ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) + if (ComponentPeer* const peer = getPeer()) peer->setAlpha (newAlpha); } else @@ -1819,12 +1909,9 @@ void Component::internalRepaintUnchecked (const Rectangle& area, const bool if (flags.visibleFlag) { if (cachedImage != nullptr) - { - if (isEntireComponent) - cachedImage->invalidateAll(); - else - cachedImage->invalidate (area); - } + if (! (isEntireComponent ? cachedImage->invalidateAll() + : cachedImage->invalidate (area))) + return; if (flags.hasHeavyweightPeerFlag) { @@ -1832,10 +1919,15 @@ void Component::internalRepaintUnchecked (const Rectangle& area, const bool // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. CHECK_MESSAGE_MANAGER_IS_LOCKED - ComponentPeer* const peer = getPeer(); + if (ComponentPeer* const peer = getPeer()) + { + // Tweak the scaling so that the component's integer size exactly aligns with the peer's scaled size + const Rectangle peerBounds (peer->getBounds()); + const Rectangle scaled (area * Point (peerBounds.getWidth() / (float) getWidth(), + peerBounds.getHeight() / (float) getHeight())); - if (peer != nullptr) - peer->repaint (area); + peer->repaint (affineTransform != nullptr ? scaled.transformedBy (*affineTransform) : scaled); + } } else { @@ -1848,9 +1940,9 @@ void Component::internalRepaintUnchecked (const Rectangle& area, const bool //============================================================================== void Component::paint (Graphics&) { - // all painting is done in the subclasses - - jassert (! isOpaque()); // if your component's opaque, you've gotta paint it! + // if your component is marked as opaque, you must implement a paint + // method and ensure that its entire area is completely painted. + jassert (getBounds().isEmpty() || ! isOpaque()); } void Component::paintOverChildren (Graphics&) @@ -1861,7 +1953,7 @@ void Component::paintOverChildren (Graphics&) //============================================================================== void Component::paintWithinParentContext (Graphics& g) { - g.setOrigin (getX(), getY()); + g.setOrigin (getPosition()); if (cachedImage != nullptr) cachedImage->paint (g); @@ -1880,9 +1972,8 @@ void Component::paintComponentAndChildren (Graphics& g) else { g.saveState(); - ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, Point()); - if (! g.isClipEmpty()) + if (ComponentHelpers::clipObscuredRegions (*this, g, clipBounds, Point()) || ! g.isClipEmpty()) paint (g); g.restoreState(); @@ -1949,14 +2040,23 @@ void Component::paintEntireComponent (Graphics& g, const bool ignoreAlphaLevel) if (effect != nullptr) { + const float scale = g.getInternalContext().getPhysicalPixelScaleFactor(); + + const Rectangle scaledBounds (getLocalBounds() * scale); + Image effectImage (flags.opaqueFlag ? Image::RGB : Image::ARGB, - getWidth(), getHeight(), ! flags.opaqueFlag); + scaledBounds.getWidth(), scaledBounds.getHeight(), ! flags.opaqueFlag); { Graphics g2 (effectImage); + g2.addTransform (AffineTransform::scale (scaledBounds.getWidth() / (float) getWidth(), + scaledBounds.getHeight() / (float) getHeight())); paintComponentAndChildren (g2); } - effect->applyEffect (effectImage, g, ignoreAlphaLevel ? 1.0f : getAlpha()); + g.saveState(); + g.addTransform (AffineTransform::scale (1.0f / scale)); + effect->applyEffect (effectImage, g, scale, ignoreAlphaLevel ? 1.0f : getAlpha()); + g.restoreState(); } else if (componentTransparency > 0 && ! ignoreAlphaLevel) { @@ -1984,23 +2084,31 @@ void Component::setPaintingIsUnclipped (const bool shouldPaintWithoutClipping) n //============================================================================== Image Component::createComponentSnapshot (const Rectangle& areaToGrab, - const bool clipImageToComponentBounds) + bool clipImageToComponentBounds, float scaleFactor) { Rectangle r (areaToGrab); if (clipImageToComponentBounds) r = r.getIntersection (getLocalBounds()); - Image componentImage (flags.opaqueFlag ? Image::RGB : Image::ARGB, - jmax (1, r.getWidth()), - jmax (1, r.getHeight()), - true); + if (r.isEmpty()) + return Image(); - Graphics imageContext (componentImage); - imageContext.setOrigin (-r.getX(), -r.getY()); - paintEntireComponent (imageContext, true); + const int w = roundToInt (scaleFactor * r.getWidth()); + const int h = roundToInt (scaleFactor * r.getHeight()); - return componentImage; + Image image (flags.opaqueFlag ? Image::RGB : Image::ARGB, w, h, true); + + Graphics g (image); + + if (w != getWidth() || h != getHeight()) + g.addTransform (AffineTransform::scale (w / (float) r.getWidth(), + h / (float) r.getHeight())); + g.setOrigin (-r.getPosition()); + + paintEntireComponent (g, true); + + return image; } void Component::setComponentEffect (ImageEffectFilter* const newEffect) @@ -2015,17 +2123,10 @@ void Component::setComponentEffect (ImageEffectFilter* const newEffect) //============================================================================== LookAndFeel& Component::getLookAndFeel() const noexcept { - const Component* c = this; - - do - { + for (const Component* c = this; c != nullptr; c = c->parentComponent) if (c->lookAndFeel != nullptr) return *(c->lookAndFeel); - c = c->parentComponent; - } - while (c != nullptr); - return LookAndFeel::getDefaultLookAndFeel(); } @@ -2049,23 +2150,26 @@ void Component::sendLookAndFeelChange() if (safePointer != nullptr) { - for (int i = childComponentList.size(); --i >= 0;) + colourChanged(); + + if (safePointer != nullptr) { - childComponentList.getUnchecked (i)->sendLookAndFeelChange(); + for (int i = childComponentList.size(); --i >= 0;) + { + childComponentList.getUnchecked (i)->sendLookAndFeelChange(); - if (safePointer == nullptr) - return; + if (safePointer == nullptr) + return; - i = jmin (i, childComponentList.size()); + i = jmin (i, childComponentList.size()); + } } } } Colour Component::findColour (const int colourId, const bool inheritFromParent) const { - const var* const v = properties.getVarPointer (ComponentHelpers::getColourPropertyId (colourId)); - - if (v != nullptr) + if (const var* const v = properties.getVarPointer (ComponentHelpers::getColourPropertyId (colourId))) return Colour ((uint32) static_cast (*v)); if (inheritFromParent && parentComponent != nullptr @@ -2086,7 +2190,7 @@ void Component::removeColour (const int colourId) colourChanged(); } -void Component::setColour (const int colourId, const Colour& colour) +void Component::setColour (const int colourId, Colour colour) { if (properties.set (ComponentHelpers::getColourPropertyId (colourId), (int) colour.getARGB())) colourChanged(); @@ -2116,8 +2220,8 @@ MarkerList* Component::getMarkers (bool /*xAxis*/) } //============================================================================== -Component::Positioner::Positioner (Component& component_) noexcept - : component (component_) +Component::Positioner::Positioner (Component& c) noexcept + : component (c) { } @@ -2142,10 +2246,10 @@ Rectangle Component::getLocalBounds() const noexcept Rectangle Component::getBoundsInParent() const noexcept { return affineTransform == nullptr ? bounds - : bounds.toFloat().transformed (*affineTransform).getSmallestIntegerContainer(); + : bounds.transformedBy (*affineTransform); } -void Component::getVisibleArea (RectangleList& result, const bool includeSiblings) const +void Component::getVisibleArea (RectangleList& result, const bool includeSiblings) const { result.clear(); const Rectangle unclipped (ComponentHelpers::getUnclippedArea (*this)); @@ -2176,14 +2280,19 @@ void Component::mouseDrag (const MouseEvent&) {} void Component::mouseMove (const MouseEvent&) {} void Component::mouseDoubleClick (const MouseEvent&) {} -void Component::mouseWheelMove (const MouseEvent& e, float wheelIncrementX, float wheelIncrementY) +void Component::mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel) { // the base class just passes this event up to its parent.. if (parentComponent != nullptr) - parentComponent->mouseWheelMove (e.getEventRelativeTo (parentComponent), - wheelIncrementX, wheelIncrementY); + parentComponent->mouseWheelMove (e.getEventRelativeTo (parentComponent), wheel); } +void Component::mouseMagnify (const MouseEvent& e, float magnifyAmount) +{ + // the base class just passes this event up to its parent.. + if (parentComponent != nullptr) + parentComponent->mouseMagnify (e.getEventRelativeTo (parentComponent), magnifyAmount); +} //============================================================================== void Component::resized() {} @@ -2195,7 +2304,10 @@ void Component::addComponentListener (ComponentListener* const newListener) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. - CHECK_MESSAGE_MANAGER_IS_LOCKED + #if JUCE_DEBUG || JUCE_LOG_ASSERTIONS + if (getParentComponent() != nullptr) + CHECK_MESSAGE_MANAGER_IS_LOCKED; + #endif componentListeners.add (newListener); } @@ -2219,9 +2331,7 @@ bool Component::canModalEventBeSentToComponent (const Component*) void Component::internalModalInputAttempt() { - Component* const current = getCurrentlyModalComponent(); - - if (current != nullptr) + if (Component* const current = getCurrentlyModalComponent()) current->inputAttemptWhenModal(); } @@ -2231,10 +2341,10 @@ void Component::postCommandMessage (const int commandId) class CustomCommandMessage : public CallbackMessage { public: - CustomCommandMessage (Component* const target_, const int commandId_) - : target (target_), commandId (commandId_) {} + CustomCommandMessage (Component* const c, const int command) + : target (c), commandId (command) {} - void messageCallback() + void messageCallback() override { if (target.get() != nullptr) // (get() required for VS2003 bug) target->handleCommandMessage (commandId); @@ -2282,7 +2392,7 @@ void Component::removeMouseListener (MouseListener* const listenerToRemove) } //============================================================================== -void Component::internalMouseEnter (MouseInputSource& source, const Point& relativePos, const Time& time) +void Component::internalMouseEnter (MouseInputSource source, Point relativePos, Time time) { if (isCurrentlyBlockedByAnotherModalComponent()) { @@ -2308,7 +2418,7 @@ void Component::internalMouseEnter (MouseInputSource& source, const Point& MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseEnter, me); } -void Component::internalMouseExit (MouseInputSource& source, const Point& relativePos, const Time& time) +void Component::internalMouseExit (MouseInputSource source, Point relativePos, Time time) { if (flags.repaintOnMouseActivityFlag) repaint(); @@ -2328,14 +2438,14 @@ void Component::internalMouseExit (MouseInputSource& source, const Point& r MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseExit, me); } -void Component::internalMouseDown (MouseInputSource& source, const Point& relativePos, const Time& time) +void Component::internalMouseDown (MouseInputSource source, Point relativePos, Time time) { Desktop& desktop = Desktop::getInstance(); - BailOutChecker checker (this); if (isCurrentlyBlockedByAnotherModalComponent()) { + flags.mouseDownWasBlocked = true; internalModalInputAttempt(); if (checker.shouldBailOut()) @@ -2355,6 +2465,8 @@ void Component::internalMouseDown (MouseInputSource& source, const Point& r } } + flags.mouseDownWasBlocked = false; + for (Component* c = this; c != nullptr; c = c->parentComponent) { if (c->isBroughtToFrontOnMouseClick()) @@ -2390,49 +2502,50 @@ void Component::internalMouseDown (MouseInputSource& source, const Point& r MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseDown, me); } -void Component::internalMouseUp (MouseInputSource& source, const Point& relativePos, const Time& time, const ModifierKeys& oldModifiers) +void Component::internalMouseUp (MouseInputSource source, Point relativePos, + Time time, const ModifierKeys oldModifiers) { - if (! isCurrentlyBlockedByAnotherModalComponent()) + if (flags.mouseDownWasBlocked && isCurrentlyBlockedByAnotherModalComponent()) + return; + + BailOutChecker checker (this); + + if (flags.repaintOnMouseActivityFlag) + repaint(); + + const MouseEvent me (source, relativePos, + oldModifiers, this, this, time, + getLocalPoint (nullptr, source.getLastMouseDownPosition()), + source.getLastMouseDownTime(), + source.getNumberOfMultipleClicks(), + source.hasMouseMovedSignificantlySincePressed()); + mouseUp (me); + + if (checker.shouldBailOut()) + return; + + Desktop& desktop = Desktop::getInstance(); + desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseUp, me); + + MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseUp, me); + + if (checker.shouldBailOut()) + return; + + // check for double-click + if (me.getNumberOfClicks() >= 2) { - BailOutChecker checker (this); - - if (flags.repaintOnMouseActivityFlag) - repaint(); - - const MouseEvent me (source, relativePos, - oldModifiers, this, this, time, - getLocalPoint (nullptr, source.getLastMouseDownPosition()), - source.getLastMouseDownTime(), - source.getNumberOfMultipleClicks(), - source.hasMouseMovedSignificantlySincePressed()); - mouseUp (me); + mouseDoubleClick (me); if (checker.shouldBailOut()) return; - Desktop& desktop = Desktop::getInstance(); - desktop.getMouseListeners().callChecked (checker, &MouseListener::mouseUp, me); - - MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseUp, me); - - if (checker.shouldBailOut()) - return; - - // check for double-click - if (me.getNumberOfClicks() >= 2) - { - mouseDoubleClick (me); - - if (checker.shouldBailOut()) - return; - - desktop.mouseListeners.callChecked (checker, &MouseListener::mouseDoubleClick, me); - MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseDoubleClick, me); - } + desktop.mouseListeners.callChecked (checker, &MouseListener::mouseDoubleClick, me); + MouseListenerList::sendMouseEvent (*this, checker, &MouseListener::mouseDoubleClick, me); } } -void Component::internalMouseDrag (MouseInputSource& source, const Point& relativePos, const Time& time) +void Component::internalMouseDrag (MouseInputSource source, Point relativePos, Time time) { if (! isCurrentlyBlockedByAnotherModalComponent()) { @@ -2455,7 +2568,7 @@ void Component::internalMouseDrag (MouseInputSource& source, const Point& r } } -void Component::internalMouseMove (MouseInputSource& source, const Point& relativePos, const Time& time) +void Component::internalMouseMove (MouseInputSource source, Point relativePos, Time time) { Desktop& desktop = Desktop::getInstance(); @@ -2481,40 +2594,49 @@ void Component::internalMouseMove (MouseInputSource& source, const Point& r } } -void Component::internalMouseWheel (MouseInputSource& source, const Point& relativePos, - const Time& time, const float amountX, const float amountY) +void Component::internalMouseWheel (MouseInputSource source, Point relativePos, + Time time, const MouseWheelDetails& wheel) { Desktop& desktop = Desktop::getInstance(); BailOutChecker checker (this); - const float wheelIncrementX = amountX / 256.0f; - const float wheelIncrementY = amountY / 256.0f; - const MouseEvent me (source, relativePos, source.getCurrentModifiers(), this, this, time, relativePos, time, 0, false); if (isCurrentlyBlockedByAnotherModalComponent()) { // allow blocked mouse-events to go to global listeners.. - desktop.mouseListeners.callChecked (checker, &MouseListener::mouseWheelMove, me, wheelIncrementX, wheelIncrementY); + desktop.mouseListeners.callChecked (checker, &MouseListener::mouseWheelMove, me, wheel); } else { - mouseWheelMove (me, wheelIncrementX, wheelIncrementY); + mouseWheelMove (me, wheel); if (checker.shouldBailOut()) return; - desktop.mouseListeners.callChecked (checker, &MouseListener::mouseWheelMove, me, wheelIncrementX, wheelIncrementY); + desktop.mouseListeners.callChecked (checker, &MouseListener::mouseWheelMove, me, wheel); if (! checker.shouldBailOut()) - MouseListenerList::sendWheelEvent (*this, checker, me, wheelIncrementX, wheelIncrementY); + MouseListenerList::sendWheelEvent (*this, checker, me, wheel); + } +} + +void Component::internalMagnifyGesture (MouseInputSource source, Point relativePos, + Time time, float amount) +{ + if (! isCurrentlyBlockedByAnotherModalComponent()) + { + const MouseEvent me (source, relativePos, source.getCurrentModifiers(), + this, this, time, relativePos, time, 0, false); + + mouseMagnify (me, amount); } } void Component::sendFakeMouseMove() const { - MouseInputSource& mainMouse = Desktop::getInstance().getMainMouseSource(); + MouseInputSource mainMouse = Desktop::getInstance().getMainMouseSource(); if (! mainMouse.isDragging()) mainMouse.triggerFakeMove(); @@ -2548,12 +2670,11 @@ void Component::internalBroughtToFront() // When brought to the front and there's a modal component blocking this one, // we need to bring the modal one to the front instead.. - Component* const cm = getCurrentlyModalComponent(); - - if (cm != nullptr && cm->getTopLevelComponent() != getTopLevelComponent()) - ModalComponentManager::getInstance()->bringModalComponentsToFront (false); // very important that this is false, otherwise in Windows, - // non-front components can't get focus when another modal comp is - // active, and therefore can't receive mouse-clicks + if (Component* const cm = getCurrentlyModalComponent()) + if (cm->getTopLevelComponent() != getTopLevelComponent()) + ModalComponentManager::getInstance()->bringModalComponentsToFront (false); // very important that this is false, otherwise in Windows, + // non-front components can't get focus when another modal comp is + // active, and therefore can't receive mouse-clicks } //============================================================================== @@ -2658,9 +2779,7 @@ void Component::takeKeyboardFocus (const FocusChangeType cause) if (currentlyFocusedComponent != this) { // get the focus onto our desktop window - ComponentPeer* const peer = getPeer(); - - if (peer != nullptr) + if (ComponentPeer* const peer = getPeer()) { const WeakReference safePointer (this); peer->grabFocus(); @@ -2783,6 +2902,12 @@ Component* JUCE_CALLTYPE Component::getCurrentlyFocusedComponent() noexcept return currentlyFocusedComponent; } +void JUCE_CALLTYPE Component::unfocusAllComponents() +{ + if (Component* c = getCurrentlyFocusedComponent()) + c->giveAwayFocus (true); +} + void Component::giveAwayFocus (const bool sendFocusLossEvent) { Component* const componentLosingFocus = currentlyFocusedComponent; @@ -2827,9 +2952,7 @@ void Component::sendEnablementChangeMessage() for (int i = getNumChildComponents(); --i >= 0;) { - Component* const c = getChildComponent (i); - - if (c != nullptr) + if (Component* const c = getChildComponent (i)) { c->sendEnablementChangeMessage(); @@ -2842,16 +2965,15 @@ void Component::sendEnablementChangeMessage() //============================================================================== bool Component::isMouseOver (const bool includeChildren) const { - const Desktop& desktop = Desktop::getInstance(); + const Array& mouseSources = Desktop::getInstance().getMouseSources(); - for (int i = desktop.getNumMouseSources(); --i >= 0;) + for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi) { - const MouseInputSource* const mi = desktop.getMouseSource(i); - Component* const c = mi->getComponentUnderMouse(); if ((c == this || (includeChildren && isParentOf (c))) - && c->reallyContains (c->getLocalPoint (nullptr, mi->getScreenPosition()), false)) + && c->reallyContains (c->getLocalPoint (nullptr, mi->getScreenPosition()), false) + && (mi->isMouse() || mi->isDragging())) return true; } @@ -2860,25 +2982,22 @@ bool Component::isMouseOver (const bool includeChildren) const bool Component::isMouseButtonDown() const { - const Desktop& desktop = Desktop::getInstance(); - - for (int i = desktop.getNumMouseSources(); --i >= 0;) - { - const MouseInputSource* const mi = desktop.getMouseSource(i); + const Array& mouseSources = Desktop::getInstance().getMouseSources(); + for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi) if (mi->isDragging() && mi->getComponentUnderMouse() == this) return true; - } return false; } bool Component::isMouseOverOrDragging() const { - const Desktop& desktop = Desktop::getInstance(); + const Array& mouseSources = Desktop::getInstance().getMouseSources(); - for (int i = desktop.getNumMouseSources(); --i >= 0;) - if (desktop.getMouseSource(i)->getComponentUnderMouse() == this) + for (MouseInputSource* mi = mouseSources.begin(), * const e = mouseSources.end(); mi != e; ++mi) + if (mi->getComponentUnderMouse() == this + && (mi->isMouse() || mi->isDragging())) return true; return false; @@ -2906,7 +3025,7 @@ void Component::addKeyListener (KeyListener* const newListener) void Component::removeKeyListener (KeyListener* const listenerToRemove) { if (keyListeners != nullptr) - keyListeners->removeValue (listenerToRemove); + keyListeners->removeFirstMatchingValue (listenerToRemove); } bool Component::keyPressed (const KeyPress&) { return false; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h index 517edde..1bc092e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h @@ -1,52 +1,34 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_COMPONENT_JUCEHEADER__ -#define __JUCE_COMPONENT_JUCEHEADER__ - -#include "../mouse/juce_MouseCursor.h" -#include "../mouse/juce_MouseListener.h" -#include "../mouse/juce_MouseEvent.h" -#include "juce_ComponentListener.h" -#include "../keyboard/juce_KeyListener.h" -#include "../keyboard/juce_KeyboardFocusTraverser.h" -#include "juce_ModalComponentManager.h" - -class LookAndFeel; -class MouseInputSource; -class MouseInputSourceInternal; -class ComponentPeer; -class MarkerList; -class RelativeRectangle; -class CachedComponentImage; +#ifndef JUCE_COMPONENT_H_INCLUDED +#define JUCE_COMPONENT_H_INCLUDED //============================================================================== /** The base class for all JUCE user-interface objects. - */ class JUCE_API Component : public MouseListener { @@ -82,13 +64,11 @@ public: //============================================================================== /** 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 noexcept { return componentName; } @@ -139,8 +119,7 @@ public: */ bool isVisible() const noexcept { return flags.visibleFlag; } - /** Called when this component's visiblility changes. - + /** Called when this component's visibility changes. @see setVisible, isVisible */ virtual void visibilityChanged(); @@ -189,7 +168,6 @@ public: void removeFromDesktop(); /** Returns true if this component is currently showing on the desktop. - @see addToDesktop, removeFromDesktop */ bool isOnDesktop() const noexcept; @@ -200,7 +178,7 @@ public: 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. + This may return nullptr if there isn't a desktop component. @see addToDesktop, isOnDesktop */ @@ -216,13 +194,20 @@ public: 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); + /** Returns the default scale factor to use for this component when it is placed + on the desktop. + The default implementation of this method just returns the value from + Desktop::getGlobalScaleFactor(), but it can be overridden if a particular component + has different requirements. The method only used if this component is added + to the desktop - it has no effect for child components. + */ + virtual float getDesktopScaleFactor() const; + //============================================================================== /** Brings the component to the front of its siblings. @@ -246,19 +231,16 @@ public: 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 noexcept; @@ -271,7 +253,7 @@ public: bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to its bounding box. */ - inline int getX() const noexcept { return bounds.getX(); } + int getX() const noexcept { return bounds.getX(); } /** Returns the y coordinate of the top of this component. This is a distance in pixels from the top edge of the component's parent. @@ -280,13 +262,13 @@ public: bounds will no longer be a direct reflection of the position at which it appears within its parent, as the transform will be applied to its bounding box. */ - inline int getY() const noexcept { return bounds.getY(); } + int getY() const noexcept { return bounds.getY(); } /** Returns the component's width in pixels. */ - inline int getWidth() const noexcept { return bounds.getWidth(); } + int getWidth() const noexcept { return bounds.getWidth(); } /** Returns the component's height in pixels. */ - inline int getHeight() const noexcept { return bounds.getHeight(); } + int getHeight() const noexcept { return bounds.getHeight(); } /** Returns the x coordinate of the component's right-hand edge. This is a distance in pixels from the left edge of the component's parent. @@ -298,7 +280,7 @@ public: int getRight() const noexcept { return bounds.getRight(); } /** Returns the component's top-left position as a Point. */ - const Point& getPosition() const noexcept { return bounds.getPosition(); } + Point getPosition() const noexcept { return bounds.getPosition(); } /** Returns the y coordinate of the bottom edge of this component. This is a distance in pixels from the top edge of the component's parent. @@ -341,15 +323,15 @@ public: 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; + void getVisibleArea (RectangleList& result, bool includeSiblings) const; //============================================================================== - /** Returns this component's x coordinate relative the the screen's top-left origin. + /** Returns this component's x coordinate relative the screen's top-left origin. @see getX, localPointToGlobal */ int getScreenX() const; - /** Returns this component's y coordinate relative the the screen's top-left origin. + /** Returns this component's y coordinate relative the screen's top-left origin. @see getY, localPointToGlobal */ int getScreenY() const; @@ -371,7 +353,7 @@ public: screen coordinate. */ Point getLocalPoint (const Component* sourceComponent, - const Point& pointRelativeToSourceComponent) const; + Point pointRelativeToSourceComponent) const; /** Converts a rectangle to be relative to this component's coordinate space. @@ -389,7 +371,7 @@ public: /** Converts a point relative to this component's top-left into a screen coordinate. @see getLocalPoint, localAreaToGlobal */ - Point localPointToGlobal (const Point& localPoint) const; + Point localPointToGlobal (Point localPoint) const; /** Converts a rectangle from this component's coordinate space to a screen coordinate. @@ -429,7 +411,7 @@ public: @see setBounds, ComponentListener::componentMovedOrResized */ - void setTopLeftPosition (const Point& newTopLeftPosition); + void setTopLeftPosition (Point newTopLeftPosition); /** Moves the component to a new position. @@ -576,7 +558,7 @@ public: @see setBounds */ void setBoundsToFit (int x, int y, int width, int height, - const Justification& justification, + Justification justification, bool onlyReduceInSize); /** Changes the position of the component's centre. @@ -638,13 +620,11 @@ public: //============================================================================== /** Returns a proportion of the component's width. - This is a handy equivalent of (getWidth() * proportion). */ int proportionOfWidth (float proportion) const noexcept; /** Returns a proportion of the component's height. - This is a handy equivalent of (getHeight() * proportion). */ int proportionOfHeight (float proportion) const noexcept; @@ -703,7 +683,7 @@ public: /** Looks for a child component with the specified ID. @see setComponentID, getComponentID */ - Component* findChildWithID (const String& componentID) const noexcept; + Component* findChildWithID (StringRef componentID) const noexcept; /** Adds a child component to this one. @@ -723,13 +703,38 @@ public: */ void addChildComponent (Component* child, int zOrder = -1); - /** Adds a child component to this one, and also makes the child visible if it isn't. + /** Adds a child component to this one. - Quite a useful function, this is just the same as calling setVisible (true) on the child - and then addChildComponent(). See addChildComponent() for more details. + Adding a child component does not mean that the component will own or delete the child - it's + your responsibility to delete the component. Note that it's safe to delete a component + without first removing it from its parent - doing so will automatically remove it and + send out the appropriate notifications before the deletion completes. + + If the child is already a child of this component, then no action will be taken, and its + z-order will be left unchanged. + + @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 it current parent. + @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, addChildAndSetID, 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 already. + + This is the same as calling setVisible (true) on the child and then addChildComponent(). + See addChildComponent() for more details. */ void addAndMakeVisible (Component* child, int zOrder = -1); + /** Adds a child component to this one, and also makes the child visible if it isn't already. + + This is the same as calling setVisible (true) on the child and then addChildComponent(). + See addChildComponent() for more details. + */ + void addAndMakeVisible (Component& child, int zOrder = -1); + /** Adds a child component to this one, makes it visible, and sets its component ID. @see addAndMakeVisible, addChildComponent */ @@ -762,13 +767,11 @@ public: 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(); @@ -785,20 +788,13 @@ public: 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 = nullptr) const + TargetClass* findParentComponentOfClass() const { - (void) dummyParameter; - for (Component* p = parentComponent; p != nullptr; p = p->parentComponent) - { - TargetClass* const target = dynamic_cast (p); - if (target != nullptr) + if (TargetClass* const target = dynamic_cast (p)) return target; - } return nullptr; } @@ -831,9 +827,9 @@ public: */ virtual void parentHierarchyChanged(); - /** Subclasses can use this callback to be told when children are added or removed. - - @see parentHierarchyChanged + /** Subclasses can use this callback to be told when children are added or removed, or + when their z-order changes. + @see parentHierarchyChanged, ComponentListener::componentChildrenChanged */ virtual void childrenChanged(); @@ -918,7 +914,7 @@ public: which might be in the way - for that, see reallyContains() @see hitTest, reallyContains, getComponentAt */ - bool contains (const Point& localPoint); + bool contains (Point localPoint); /** Returns true if a given point lies in this component, taking any overlapping siblings into account. @@ -928,7 +924,7 @@ public: this determines whether that is counted as a hit. @see contains, getComponentAt */ - bool reallyContains (const Point& localPoint, bool returnTrueIfWithinAChild); + bool reallyContains (Point localPoint, bool returnTrueIfWithinAChild); /** Returns the component at a certain point within this one. @@ -951,7 +947,7 @@ public: instead call getComponentAt on the top-level parent of this component. @see hitTest, contains, reallyContains */ - Component* getComponentAt (const Point& position); + Component* getComponentAt (Point position); //============================================================================== /** Marks the whole component as needing to be redrawn. @@ -1039,7 +1035,8 @@ public: @see paintEntireComponent */ Image createComponentSnapshot (const Rectangle& areaToGrab, - bool clipImageToComponentBounds = true); + bool clipImageToComponentBounds = true, + float scaleFactor = 1.0f); /** Draws this component and all its subcomponents onto the specified graphics context. @@ -1091,7 +1088,6 @@ public: void setComponentEffect (ImageEffectFilter* newEffect); /** Returns the current component effect. - @see setComponentEffect */ ImageEffectFilter* getComponentEffect() const noexcept { return effect; } @@ -1183,7 +1179,6 @@ public: void setBroughtToFrontOnMouseClick (bool shouldBeBroughtToFront) noexcept; /** Indicates whether the component should be brought to the front when clicked-on. - @see setBroughtToFrontOnMouseClick */ bool isBroughtToFrontOnMouseClick() const noexcept; @@ -1222,7 +1217,6 @@ public: void setMouseClickGrabsKeyboardFocus (bool shouldGrabFocus); /** Returns the last value set with setMouseClickGrabsKeyboardFocus(). - See setMouseClickGrabsKeyboardFocus() for more info. */ bool getMouseClickGrabsKeyboardFocus() const noexcept; @@ -1262,11 +1256,13 @@ public: 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() noexcept; + /** If any component has keyboard focus, this will defocus it. */ + static void JUCE_CALLTYPE unfocusAllComponents(); + //============================================================================== /** Tries to move the keyboard focus to one of this component's siblings. @@ -1468,19 +1464,20 @@ public: //============================================================================== - /** Called when the mouse moves inside this component. + /** 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 + @param event 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); + virtual void mouseMove (const MouseEvent& event) override; - /** Called when the mouse first enters this component. + /** 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. @@ -1490,16 +1487,13 @@ public: 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 + @param event 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); + virtual void mouseEnter (const MouseEvent& event) override; - /** Called when the mouse moves out of this component. + /** Called when the mouse moves out of a component. This will be called when the mouse moves off the edge of this component. @@ -1508,16 +1502,13 @@ public: 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 + @param event 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); + virtual void mouseExit (const MouseEvent& event) override; - /** Called when a mouse button is pressed while it's over this component. + /** 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) @@ -1526,10 +1517,11 @@ public: 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 + @param event 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); + virtual void mouseDown (const MouseEvent& event) override; /** Called when the mouse is moved while a button is held down. @@ -1537,14 +1529,11 @@ public: 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 + @param event 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); + virtual void mouseDrag (const MouseEvent& event) override; /** Called when a mouse button is released. @@ -1555,46 +1544,54 @@ public: 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 + @param event 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); + virtual void mouseUp (const MouseEvent& event) override; - /** Called when a mouse button has been double-clicked in this component. + /** 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. - 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 + @param event 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); + virtual void mouseDoubleClick (const MouseEvent& event) override; /** 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 + If not overridden, a 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. + child components which aren't interested in them. (Bear in mind that if + you attach a component as a mouse-listener to other components, then + those wheel moves will also end up calling this method and being passed up + to the parents, which may not be what you intended to happen). - @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 + @param event details about the mouse event + @param wheel details about the mouse wheel movement */ - virtual void mouseWheelMove (const MouseEvent& e, - float wheelIncrementX, - float wheelIncrementY); + virtual void mouseWheelMove (const MouseEvent& event, + const MouseWheelDetails& wheel) override; + + /** Called when a pinch-to-zoom mouse-gesture is used. + + If not overridden, a component will forward this message to its parent, so + that parent components can collect gesture messages that are unused by child + components. + + @param event details about the mouse event + @param scaleFactor a multiplier to indicate by how much the size of the target + should be changed. A value of 1.0 would indicate no change, + values greater than 1.0 mean it should be enlarged. + */ + virtual void mouseMagnify (const MouseEvent& event, float scaleFactor); //============================================================================== /** Ensures that a non-stop stream of mouse-drag events will be sent during the @@ -1666,7 +1663,6 @@ public: void addKeyListener (KeyListener* newListener); /** Removes a previously-registered key listener. - @see addKeyListener */ void removeKeyListener (KeyListener* listenerToRemove); @@ -1738,13 +1734,11 @@ public: }; /** 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); @@ -1884,7 +1878,6 @@ public: void addComponentListener (ComponentListener* newListener); /** Removes a component listener. - @see addComponentListener */ void removeComponentListener (ComponentListener* listenerToRemove); @@ -1960,7 +1953,7 @@ public: /** Ends a component's modal state. - If this component is currently modal, this will turn of its modalness, and return + If this component is currently modal, this will turn off its modalness, and return a value to the runModalLoop() method that might have be running its modal loop. @see runModalLoop, enterModalState, isCurrentlyModal @@ -2071,10 +2064,9 @@ public: @see findColour, isColourSpecified, colourChanged, LookAndFeel::findColour, LookAndFeel::setColour */ - void setColour (int colourId, const Colour& colour); + void setColour (int colourId, Colour newColour); /** 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); @@ -2090,15 +2082,14 @@ public: void copyAllExplicitColoursTo (Component& target) const; /** This method is called when a colour is changed by the setColour() method. - @see setColour, findColour */ virtual void colourChanged(); //============================================================================== /** Components can implement this method to provide a MarkerList. - The default implementation of this method returns 0, but you can override it to - return a pointer to the component's marker list. If xAxis is true, it should + The default implementation of this method returns nullptr, but you can override + it to return a pointer to the component's marker list. If xAxis is true, it should return the X marker list; if false, it should return the Y markers. */ virtual MarkerList* getMarkers (bool xAxis); @@ -2155,7 +2146,7 @@ public: const ComponentType* operator->() const noexcept { return getComponent(); } /** If the component is valid, this deletes it and sets this pointer to null. */ - void deleteAndZero() { delete getComponent(); jassert (getComponent() == nullptr); } + void deleteAndZero() { delete getComponent(); } bool operator== (ComponentType* component) const noexcept { return weakRef == component; } bool operator!= (ComponentType* component) const noexcept { return weakRef != component; } @@ -2183,7 +2174,7 @@ public: private: const WeakReference safePointer; - JUCE_DECLARE_NON_COPYABLE (BailOutChecker); + JUCE_DECLARE_NON_COPYABLE (BailOutChecker) }; //============================================================================== @@ -2216,7 +2207,7 @@ public: private: Component& component; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner) }; /** Returns the Positioner object that has been set for this component. @@ -2245,12 +2236,10 @@ public: CachedComponentImage* getCachedComponentImage() const noexcept { return cachedImage; } //============================================================================== - #ifndef DOXYGEN // These methods are deprecated - use localPointToGlobal, getLocalPoint, getLocalPoint, etc instead. - JUCE_DEPRECATED (Point relativePositionToGlobal (const Point&) const); - JUCE_DEPRECATED (Point globalPositionToRelative (const Point&) const); - JUCE_DEPRECATED (Point relativePositionToOtherComponent (const Component*, const Point&) const); - #endif + JUCE_DEPRECATED (Point relativePositionToGlobal (Point) const); + JUCE_DEPRECATED (Point globalPositionToRelative (Point) const); + JUCE_DEPRECATED (Point relativePositionToOtherComponent (const Component*, Point) const); private: //============================================================================== @@ -2275,7 +2264,7 @@ private: class MouseListenerList; friend class MouseListenerList; - friend class ScopedPointer ; + friend struct ContainerDeletePolicy; ScopedPointer mouseListeners; ScopedPointer > keyListeners; ListenerList componentListeners; @@ -2302,6 +2291,7 @@ private: bool isDisabledFlag : 1; bool childCompFocusedFlag : 1; bool dontClipGraphicsFlag : 1; + bool mouseDownWasBlocked : 1; #if JUCE_DEBUG bool isInsidePaintCall : 1; #endif @@ -2316,13 +2306,14 @@ private: uint8 componentTransparency; //============================================================================== - void internalMouseEnter (MouseInputSource&, const Point&, const Time&); - void internalMouseExit (MouseInputSource&, const Point&, const Time&); - void internalMouseDown (MouseInputSource&, const Point&, const Time&); - void internalMouseUp (MouseInputSource&, const Point&, const Time&, const ModifierKeys& oldModifiers); - void internalMouseDrag (MouseInputSource&, const Point&, const Time&); - void internalMouseMove (MouseInputSource&, const Point&, const Time&); - void internalMouseWheel (MouseInputSource&, const Point&, const Time&, float amountX, float amountY); + void internalMouseEnter (MouseInputSource, Point, Time); + void internalMouseExit (MouseInputSource, Point, Time); + void internalMouseDown (MouseInputSource, Point, Time); + void internalMouseUp (MouseInputSource, Point, Time, const ModifierKeys oldModifiers); + void internalMouseDrag (MouseInputSource, Point, Time); + void internalMouseMove (MouseInputSource, Point, Time); + void internalMouseWheel (MouseInputSource, Point, Time, const MouseWheelDetails&); + void internalMagnifyGesture (MouseInputSource, Point, Time, float); void internalBroughtToFront(); void internalFocusGain (const FocusChangeType, const WeakReference&); void internalFocusGain (const FocusChangeType); @@ -2353,7 +2344,7 @@ private: /* Components aren't allowed to have copy constructors, as this would mess up parent hierarchies. You might need to give your subclasses a private dummy constructor to avoid compiler warnings. */ - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Component); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Component) //============================================================================== #if JUCE_CATCH_DEPRECATED_CODE_MISUSE @@ -2364,8 +2355,8 @@ private: virtual void filesDropped (const StringArray&, int, int) {} // This is included here to cause an error if you use or overload it - it has been deprecated in - // favour of contains (const Point&) - void contains (int, int); + // favour of contains (Point) + void contains (int, int) JUCE_DELETED_FUNCTION; #endif protected: @@ -2376,4 +2367,4 @@ protected: }; -#endif // __JUCE_COMPONENT_JUCEHEADER__ +#endif // JUCE_COMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.cpp index 169d50e..3e5cd35 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.h b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.h index 7ad0304..56c0631 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.h +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ComponentListener.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_COMPONENTLISTENER_JUCEHEADER__ -#define __JUCE_COMPONENTLISTENER_JUCEHEADER__ - -class Component; +#ifndef JUCE_COMPONENTLISTENER_H_INCLUDED +#define JUCE_COMPONENTLISTENER_H_INCLUDED //============================================================================== @@ -71,9 +68,10 @@ public: */ virtual void componentVisibilityChanged (Component& component); - /** Called when the component has children added or removed. + /** Called when the component has children added or removed, or their z-order + changes. - @param component the component whose children were changed + @param component the component whose children have changed @see Component::childrenChanged, Component::addChildComponent, Component::removeChildComponent */ @@ -110,4 +108,4 @@ public: }; -#endif // __JUCE_COMPONENTLISTENER_JUCEHEADER__ +#endif // JUCE_COMPONENTLISTENER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.cpp index 4f5e62e..5dedd42 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.cpp @@ -1,35 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ Desktop::Desktop() - : mouseClickCounter (0), + : mouseSources (new MouseInputSource::SourceList()), + mouseClickCounter (0), mouseWheelCounter (0), kioskModeComponent (nullptr), - allowedOrientations (allOrientations) + kioskModeReentrant (false), + allowedOrientations (allOrientations), + masterScaleFactor ((float) getDefaultMasterScale()) { - createMouseInputSources(); - refreshMonitorSizes(); + displays = new Displays (*this); } Desktop::~Desktop() @@ -54,79 +55,6 @@ Desktop& JUCE_CALLTYPE Desktop::getInstance() Desktop* Desktop::instance = nullptr; -//============================================================================== -void Desktop::refreshMonitorSizes() -{ - Array > oldClipped, oldUnclipped; - oldClipped.swapWithArray (monitorCoordsClipped); - oldUnclipped.swapWithArray (monitorCoordsUnclipped); - - getCurrentMonitorPositions (monitorCoordsClipped, true); - getCurrentMonitorPositions (monitorCoordsUnclipped, false); - jassert (monitorCoordsClipped.size() == monitorCoordsUnclipped.size()); - - if (oldClipped != monitorCoordsClipped - || oldUnclipped != monitorCoordsUnclipped) - { - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - { - ComponentPeer* const p = ComponentPeer::getPeer (i); - if (p != nullptr) - p->handleScreenSizeChange(); - } - } -} - -int Desktop::getNumDisplayMonitors() const noexcept -{ - return monitorCoordsClipped.size(); -} - -Rectangle Desktop::getDisplayMonitorCoordinates (const int index, const bool clippedToWorkArea) const noexcept -{ - return clippedToWorkArea ? monitorCoordsClipped [index] - : monitorCoordsUnclipped [index]; -} - -RectangleList Desktop::getAllMonitorDisplayAreas (const bool clippedToWorkArea) const -{ - RectangleList rl; - - for (int i = 0; i < getNumDisplayMonitors(); ++i) - rl.addWithoutMerging (getDisplayMonitorCoordinates (i, clippedToWorkArea)); - - return rl; -} - -Rectangle Desktop::getMainMonitorArea (const bool clippedToWorkArea) const noexcept -{ - return getDisplayMonitorCoordinates (0, clippedToWorkArea); -} - -Rectangle Desktop::getMonitorAreaContaining (const Point& position, const bool clippedToWorkArea) const -{ - Rectangle best (getMainMonitorArea (clippedToWorkArea)); - double bestDistance = 1.0e10; - - for (int i = getNumDisplayMonitors(); --i >= 0;) - { - const Rectangle rect (getDisplayMonitorCoordinates (i, clippedToWorkArea)); - - if (rect.contains (position)) - return rect; - - const double distance = rect.getCentre().getDistanceFrom (position); - - if (distance < bestDistance) - { - bestDistance = distance; - best = rect; - } - } - - return best; -} - //============================================================================== int Desktop::getNumComponents() const noexcept { @@ -138,7 +66,7 @@ Component* Desktop::getComponent (const int index) const noexcept return desktopComponents [index]; } -Component* Desktop::findComponentAt (const Point& screenPosition) const +Component* Desktop::findComponentAt (Point screenPosition) const { for (int i = desktopComponents.size(); --i >= 0;) { @@ -162,7 +90,7 @@ LookAndFeel& Desktop::getDefaultLookAndFeel() noexcept if (currentLookAndFeel == nullptr) { if (defaultLookAndFeel == nullptr) - defaultLookAndFeel = new LookAndFeel(); + defaultLookAndFeel = new LookAndFeel_V2(); currentLookAndFeel = defaultLookAndFeel; } @@ -175,12 +103,8 @@ void Desktop::setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel) currentLookAndFeel = newDefaultLookAndFeel; for (int i = getNumComponents(); --i >= 0;) - { - Component* const c = getComponent (i); - - if (c != nullptr) + if (Component* const c = getComponent (i)) c->sendLookAndFeelChange(); - } } //============================================================================== @@ -193,7 +117,7 @@ void Desktop::addDesktopComponent (Component* const c) void Desktop::removeDesktopComponent (Component* const c) { - desktopComponents.removeValue (c); + desktopComponents.removeFirstMatchingValue (c); } void Desktop::componentBroughtToFront (Component* const c) @@ -225,110 +149,34 @@ Point Desktop::getMousePosition() return getInstance().getMainMouseSource().getScreenPosition(); } +void Desktop::setMousePosition (Point newPosition) +{ + getInstance().getMainMouseSource().setScreenPosition (newPosition); +} + Point Desktop::getLastMouseDownPosition() { return getInstance().getMainMouseSource().getLastMouseDownPosition(); } -int Desktop::getMouseButtonClickCounter() -{ - return getInstance().mouseClickCounter; -} +int Desktop::getMouseButtonClickCounter() const noexcept { return mouseClickCounter; } +int Desktop::getMouseWheelMoveCounter() const noexcept { return mouseWheelCounter; } -void Desktop::incrementMouseClickCounter() noexcept -{ - ++mouseClickCounter; -} +void Desktop::incrementMouseClickCounter() noexcept { ++mouseClickCounter; } +void Desktop::incrementMouseWheelCounter() noexcept { ++mouseWheelCounter; } -int Desktop::getNumDraggingMouseSources() const noexcept -{ - int num = 0; - for (int i = mouseSources.size(); --i >= 0;) - if (mouseSources.getUnchecked(i)->isDragging()) - ++num; - - return num; -} - -MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept -{ - int num = 0; - for (int i = mouseSources.size(); --i >= 0;) - { - MouseInputSource* const mi = mouseSources.getUnchecked(i); - - if (mi->isDragging()) - { - if (index == num) - return mi; - - ++num; - } - } - - return nullptr; -} +const Array& Desktop::getMouseSources() const noexcept { return mouseSources->sourceArray; } +int Desktop::getNumMouseSources() const noexcept { return mouseSources->sources.size(); } +int Desktop::getNumDraggingMouseSources() const noexcept { return mouseSources->getNumDraggingMouseSources(); } +MouseInputSource* Desktop::getMouseSource (int index) const noexcept { return mouseSources->getMouseSource (index); } +MouseInputSource* Desktop::getDraggingMouseSource (int index) const noexcept { return mouseSources->getDraggingMouseSource (index); } +MouseInputSource Desktop::getMainMouseSource() const noexcept { return MouseInputSource (mouseSources->sources.getUnchecked(0)); } +void Desktop::beginDragAutoRepeat (int interval) { mouseSources->beginDragAutoRepeat (interval); } //============================================================================== -class MouseDragAutoRepeater : public Timer -{ -public: - MouseDragAutoRepeater() {} - - void timerCallback() - { - Desktop& desktop = Desktop::getInstance(); - int numMiceDown = 0; - - for (int i = desktop.getNumMouseSources(); --i >= 0;) - { - MouseInputSource* const source = desktop.getMouseSource(i); - if (source->isDragging()) - { - source->triggerFakeMove(); - ++numMiceDown; - } - } - - if (numMiceDown == 0) - desktop.beginDragAutoRepeat (0); - } - -private: - JUCE_DECLARE_NON_COPYABLE (MouseDragAutoRepeater); -}; - -void Desktop::beginDragAutoRepeat (const int interval) -{ - if (interval > 0) - { - if (dragRepeater == nullptr) - dragRepeater = new MouseDragAutoRepeater(); - - if (dragRepeater->getTimerInterval() != interval) - dragRepeater->startTimer (interval); - } - else - { - dragRepeater = nullptr; - } -} - -//============================================================================== -void Desktop::addFocusChangeListener (FocusChangeListener* const listener) -{ - focusListeners.add (listener); -} - -void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) -{ - focusListeners.remove (listener); -} - -void Desktop::triggerFocusCallback() -{ - triggerAsyncUpdate(); -} +void Desktop::addFocusChangeListener (FocusChangeListener* const listener) { focusListeners.add (listener); } +void Desktop::removeFocusChangeListener (FocusChangeListener* const listener) { focusListeners.remove (listener); } +void Desktop::triggerFocusCallback() { triggerAsyncUpdate(); } void Desktop::handleAsyncUpdate() { @@ -381,9 +229,7 @@ void Desktop::sendMouseMove() lastFakeMouseMove = getMousePosition(); - Component* const target = findComponentAt (lastFakeMouseMove); - - if (target != nullptr) + if (Component* const target = findComponentAt (lastFakeMouseMove)) { Component::BailOutChecker checker (target); const Point pos (target->getLocalPoint (nullptr, lastFakeMouseMove)); @@ -400,19 +246,116 @@ void Desktop::sendMouseMove() } } + +//============================================================================== +Desktop::Displays::Displays (Desktop& desktop) { init (desktop); } +Desktop::Displays::~Displays() {} + +const Desktop::Displays::Display& Desktop::Displays::getMainDisplay() const noexcept +{ + jassert (displays.getReference(0).isMain); + return displays.getReference(0); +} + +const Desktop::Displays::Display& Desktop::Displays::getDisplayContaining (Point position) const noexcept +{ + const Display* best = &displays.getReference(0); + double bestDistance = 1.0e10; + + for (int i = displays.size(); --i >= 0;) + { + const Display& d = displays.getReference(i); + + if (d.totalArea.contains (position)) + { + best = &d; + break; + } + + const double distance = d.totalArea.getCentre().getDistanceFrom (position); + + if (distance < bestDistance) + { + bestDistance = distance; + best = &d; + } + } + + return *best; +} + +RectangleList Desktop::Displays::getRectangleList (bool userAreasOnly) const +{ + RectangleList rl; + + for (int i = 0; i < displays.size(); ++i) + { + const Display& d = displays.getReference(i); + rl.addWithoutMerging (userAreasOnly ? d.userArea : d.totalArea); + } + + return rl; +} + +Rectangle Desktop::Displays::getTotalBounds (bool userAreasOnly) const +{ + return getRectangleList (userAreasOnly).getBounds(); +} + +bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept; +bool operator== (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept +{ + return d1.userArea == d2.userArea + && d1.totalArea == d2.totalArea + && d1.scale == d2.scale + && d1.isMain == d2.isMain; +} + +bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept; +bool operator!= (const Desktop::Displays::Display& d1, const Desktop::Displays::Display& d2) noexcept +{ + return ! (d1 == d2); +} + +void Desktop::Displays::init (Desktop& desktop) +{ + findDisplays (desktop.getGlobalScaleFactor()); + jassert (displays.size() > 0); +} + +void Desktop::Displays::refresh() +{ + Array oldDisplays; + oldDisplays.swapWith (displays); + + init (Desktop::getInstance()); + + if (oldDisplays != displays) + { + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + if (ComponentPeer* const peer = ComponentPeer::getPeer (i)) + peer->handleScreenSizeChange(); + } +} + //============================================================================== void Desktop::setKioskModeComponent (Component* componentToUse, const bool allowMenusAndBars) { + if (kioskModeReentrant) + return; + + const ScopedValueSetter setter (kioskModeReentrant, true, false); + if (kioskModeComponent != componentToUse) { // agh! Don't delete or remove a component from the desktop while it's still the kiosk component! jassert (kioskModeComponent == nullptr || ComponentPeer::getPeerFor (kioskModeComponent) != nullptr); - if (kioskModeComponent != nullptr) + if (Component* const oldKioskComp = kioskModeComponent) { - setKioskComponent (kioskModeComponent, false, allowMenusAndBars); - - kioskModeComponent->setBounds (kioskComponentOriginalBounds); + kioskModeComponent = nullptr; // (to make sure that isKioskMode() returns false when resizing the old one) + setKioskComponent (oldKioskComp, false, allowMenusAndBars); + oldKioskComp->setBounds (kioskComponentOriginalBounds); } kioskModeComponent = componentToUse; @@ -423,7 +366,6 @@ void Desktop::setKioskModeComponent (Component* componentToUse, const bool allow jassert (ComponentPeer::getPeerFor (kioskModeComponent) != nullptr); kioskComponentOriginalBounds = kioskModeComponent->getBounds(); - setKioskComponent (kioskModeComponent, true, allowMenusAndBars); } } @@ -441,7 +383,17 @@ void Desktop::setOrientationsEnabled (const int newOrientations) bool Desktop::isOrientationEnabled (const DisplayOrientation orientation) const noexcept { // Make sure you only pass one valid flag in here... - jassert (orientation == upright || orientation == upsideDown || orientation == rotatedClockwise || orientation == rotatedAntiClockwise); + jassert (orientation == upright || orientation == upsideDown + || orientation == rotatedClockwise || orientation == rotatedAntiClockwise); return (allowedOrientations & orientation) != 0; } + +void Desktop::setGlobalScaleFactor (float newScaleFactor) noexcept +{ + if (masterScaleFactor != newScaleFactor) + { + masterScaleFactor = newScaleFactor; + displays->refresh(); + } +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.h b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.h index 7be50d5..0591162 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.h +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Desktop.h @@ -1,36 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DESKTOP_JUCEHEADER__ -#define __JUCE_DESKTOP_JUCEHEADER__ - -#include "juce_Component.h" -#include "../layout/juce_ComponentAnimator.h" -class MouseInputSource; -class MouseInputSourceInternal; -class MouseListener; +#ifndef JUCE_DESKTOP_H_INCLUDED +#define JUCE_DESKTOP_H_INCLUDED //============================================================================== @@ -62,42 +55,13 @@ class JUCE_API Desktop : private DeletedAtShutdown, { public: //============================================================================== - /** There's only one dektop object, and this method will return it. - */ + /** There's only one desktop 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. - */ - RectangleList getAllMonitorDisplayAreas (bool clippedToWorkArea = true) const; - - /** 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. - */ - Rectangle getMainMonitorArea (bool clippedToWorkArea = true) const noexcept; - - /** 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. - */ - 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. + The coordinates are relative to the top-left of the main monitor. Note that this is just a shortcut for calling getMainMouseSource().getScreenPosition(), and you should only resort to grabbing the global mouse position if there's really no @@ -106,10 +70,9 @@ public: static Point getMousePosition(); /** Makes the mouse pointer jump to a given location. - - The co-ordinates are relative to the top-left of the main monitor. + The coordinates are relative to the top-left of the main monitor. */ - static void setMousePosition (const Point& newPosition); + static void setMousePosition (Point newPosition); /** Returns the last position at which a mouse button was pressed. @@ -120,12 +83,17 @@ public: */ static Point getLastMouseDownPosition(); - /** Returns the number of times the mouse button has been clicked since the - app started. - + /** Returns the number of times the mouse button has been clicked since the app started. Each mouse-down event increments this number by 1. + @see getMouseWheelMoveCounter */ - static int getMouseButtonClickCounter(); + int getMouseButtonClickCounter() const noexcept; + + /** Returns the number of times the mouse wheel has been moved since the app started. + Each mouse-wheel event increments this number by 1. + @see getMouseButtonClickCounter + */ + int getMouseWheelMoveCounter() const noexcept; //============================================================================== /** This lets you prevent the screensaver from becoming active. @@ -199,7 +167,7 @@ public: /** 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. + has been set, this returns nullptr. */ Component* getKioskModeComponent() const noexcept { return kioskModeComponent; } @@ -225,9 +193,9 @@ public: 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. + Returns nullptr if the coordinates are inside a non-Juce window. */ - Component* findComponentAt (const Point& screenPosition) const; + Component* findComponentAt (Point screenPosition) const; /** The Desktop object has a ComponentAnimator instance which can be used for performing your animations. @@ -257,13 +225,22 @@ public: void setDefaultLookAndFeel (LookAndFeel* newDefaultLookAndFeel); //============================================================================== + /** Provides access to the array of mouse sources, for iteration. + In a traditional single-mouse system, there might be only one MouseInputSource. On a + multi-touch system, there could be one input source per potential finger. The number + of mouse sources returned here may increase dynamically as the program runs. + To find out how many mouse events are currently happening, use getNumDraggingMouseSources(). + */ + const Array& getMouseSources() const noexcept; + /** 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. + In a traditional single-mouse system, there might be only one MouseInputSource. On a + multi-touch system, there could be one input source per potential finger. The number + of mouse sources returned here may increase dynamically as the program runs. To find out how many mouse events are currently happening, use getNumDraggingMouseSources(). @see getMouseSource */ - int getNumMouseSources() const noexcept { return mouseSources.size(); } + int getNumMouseSources() const noexcept; /** Returns one of the system's MouseInputSource objects. The index should be from 0 to getNumMouseSources() - 1. Out-of-range indexes will return @@ -271,12 +248,12 @@ public: 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 noexcept { return mouseSources [index]; } + MouseInputSource* getMouseSource (int index) const noexcept; /** Returns the main mouse input device that the system is using. @see getNumMouseSources() */ - MouseInputSource& getMainMouseSource() const noexcept { return *mouseSources.getUnchecked(0); } + MouseInputSource getMainMouseSource() const noexcept; /** 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 @@ -338,12 +315,82 @@ public: bool isOrientationEnabled (DisplayOrientation orientation) const noexcept; //============================================================================== - /** Tells this object to refresh its idea of what the screen resolution is. + class JUCE_API Displays + { + public: + /** Contains details about a display device. */ + struct Display + { + /** This is the bounds of the area of this display which isn't covered by + OS-dependent objects like the taskbar, menu bar, etc. */ + Rectangle userArea; - (Called internally by the native code). + /** This is the total physical area of this display, including any taskbars, etc */ + Rectangle totalArea; + + /** This is the scale-factor of this display. + If you create a component with size 1x1, this scale factor indicates the actual + size of the component in terms of physical pixels. + For higher-resolution displays, it may be a value greater than 1.0 + */ + double scale; + + /** The DPI of the display. + This is the number of physical pixels per inch. To get the number of logical + pixels per inch, divide this by the Display::scale value. + */ + double dpi; + + /** This will be true if this is the user's main screen. */ + bool isMain; + }; + + /** Returns the display which acts as user's main screen. */ + const Display& getMainDisplay() const noexcept; + + /** Returns the display which contains a particular point. + If the point lies outside all the displays, the nearest one will be returned. + */ + const Display& getDisplayContaining (Point position) const noexcept; + + /** Returns a RectangleList made up of all the displays. */ + RectangleList getRectangleList (bool userAreasOnly) const; + + /** Returns the smallest bounding box which contains all the displays. */ + Rectangle getTotalBounds (bool userAreasOnly) const; + + /** The list of displays. */ + Array displays; + + #ifndef DOXYGEN + /** @internal */ + void refresh(); + #endif + + private: + friend class Desktop; + friend struct ContainerDeletePolicy; + Displays (Desktop&); + ~Displays(); + + void init (Desktop&); + void findDisplays (float masterScale); + }; + + const Displays& getDisplays() const noexcept { return *displays; } + + //============================================================================== + /** Sets a global scale factor to be used for all desktop windows. + Setting this will also scale the monitor sizes that are returned by getDisplays(). */ - void refreshMonitorSizes(); + void setGlobalScaleFactor (float newScaleFactor) noexcept; + /** Returns the current global scale factor, as set by setGlobalScaleFactor(). + @see setGlobalScaleFactor + */ + float getGlobalScaleFactor() const noexcept { return masterScaleFactor; } + + //============================================================================== /** True if the OS supports semitransparent windows */ static bool canUseSemiTransparentWindows() noexcept; @@ -353,60 +400,59 @@ private: friend class Component; friend class ComponentPeer; - friend class MouseInputSource; friend class MouseInputSourceInternal; friend class DeletedAtShutdown; friend class TopLevelWindowManager; - OwnedArray mouseSources; - void createMouseInputSources(); + ScopedPointer mouseSources; - ListenerList mouseListeners; - ListenerList focusListeners; + ListenerList mouseListeners; + ListenerList focusListeners; - Array desktopComponents; - Array > monitorCoordsClipped, monitorCoordsUnclipped; + Array desktopComponents; + Array peers; + + ScopedPointer displays; Point lastFakeMouseMove; void sendMouseMove(); - int mouseClickCounter; + int mouseClickCounter, mouseWheelCounter; void incrementMouseClickCounter() noexcept; - - ScopedPointer dragRepeater; + void incrementMouseWheelCounter() noexcept; ScopedPointer defaultLookAndFeel; WeakReference currentLookAndFeel; Component* kioskModeComponent; Rectangle kioskComponentOriginalBounds; + bool kioskModeReentrant; int allowedOrientations; + float masterScaleFactor; ComponentAnimator animator; - void timerCallback(); + void timerCallback() override; void resetTimer(); - ListenerList & getMouseListeners(); - - int getNumDisplayMonitors() const noexcept; - Rectangle getDisplayMonitorCoordinates (int index, bool clippedToWorkArea) const noexcept; - static void getCurrentMonitorPositions (Array >&, const bool clipToWorkArea); + ListenerList& getMouseListeners(); void addDesktopComponent (Component*); void removeDesktopComponent (Component*); void componentBroughtToFront (Component*); - static void setKioskComponent (Component*, bool enableOrDisable, bool allowMenusAndBars); + void setKioskComponent (Component*, bool enableOrDisable, bool allowMenusAndBars); void triggerFocusCallback(); - void handleAsyncUpdate(); + void handleAsyncUpdate() override; + + static double getDefaultMasterScale(); Desktop(); ~Desktop(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Desktop) }; -#endif // __JUCE_DESKTOP_JUCEHEADER__ +#endif // JUCE_DESKTOP_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp index 923e144..a6e7b07 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -34,21 +33,21 @@ public: jassert (comp != nullptr); } - void componentMovedOrResized (bool, bool) {} + void componentMovedOrResized (bool, bool) override {} - void componentPeerChanged() + void componentPeerChanged() override { if (! component->isShowing()) cancel(); } - void componentVisibilityChanged() + void componentVisibilityChanged() override { if (! component->isShowing()) cancel(); } - void componentBeingDeleted (Component& comp) + void componentBeingDeleted (Component& comp) override { ComponentMovementWatcher::componentBeingDeleted (comp); @@ -64,7 +63,9 @@ public: if (isActive) { isActive = false; - ModalComponentManager::getInstance()->triggerAsyncUpdate(); + + if (ModalComponentManager* mcm = ModalComponentManager::getInstanceWithoutCreating()) + mcm->triggerAsyncUpdate(); } } @@ -74,7 +75,7 @@ public: bool isActive, autoDelete; private: - JUCE_DECLARE_NON_COPYABLE (ModalItem); + JUCE_DECLARE_NON_COPYABLE (ModalItem) }; //============================================================================== @@ -84,6 +85,7 @@ ModalComponentManager::ModalComponentManager() ModalComponentManager::~ModalComponentManager() { + stack.clear(); clearSingletonInstance(); } @@ -232,11 +234,22 @@ void ModalComponentManager::bringModalComponentsToFront (bool topOneShouldGrabFo } } +bool ModalComponentManager::cancelAllModalComponents() +{ + const int numModal = getNumModalComponents(); + + for (int i = numModal; --i >= 0;) + if (Component* const c = getModalComponent(i)) + c->exitModalState (0); + + return numModal > 0; +} + #if JUCE_MODAL_LOOPS_PERMITTED class ModalComponentManager::ReturnValueRetriever : public ModalComponentManager::Callback { public: - ReturnValueRetriever (int& value_, bool& finished_) : value (value_), finished (finished_) {} + ReturnValueRetriever (int& v, bool& done) : value (v), finished (done) {} void modalStateFinished (int returnValue) { @@ -248,7 +261,7 @@ private: int& value; bool& finished; - JUCE_DECLARE_NON_COPYABLE (ReturnValueRetriever); + JUCE_DECLARE_NON_COPYABLE (ReturnValueRetriever) }; int ModalComponentManager::runEventLoopForCurrentComponent() @@ -256,29 +269,25 @@ int ModalComponentManager::runEventLoopForCurrentComponent() // This can only be run from the message thread! jassert (MessageManager::getInstance()->isThisTheMessageThread()); - Component* currentlyModal = getModalComponent (0); - - if (currentlyModal == nullptr) - return 0; - - WeakReference prevFocused (Component::getCurrentlyFocusedComponent()); - int returnValue = 0; - bool finished = false; - attachCallback (currentlyModal, new ReturnValueRetriever (returnValue, finished)); - JUCE_TRY + if (Component* currentlyModal = getModalComponent (0)) { - while (! finished) - { - if (! MessageManager::getInstance()->runDispatchLoopUntil (20)) - break; - } - } - JUCE_CATCH_EXCEPTION + FocusRestorer focusRestorer; - if (prevFocused != nullptr) - prevFocused->grabKeyboardFocus(); + bool finished = false; + attachCallback (currentlyModal, new ReturnValueRetriever (returnValue, finished)); + + JUCE_TRY + { + while (! finished) + { + if (! MessageManager::getInstance()->runDispatchLoopUntil (20)) + break; + } + } + JUCE_CATCH_EXCEPTION + } return returnValue; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.h b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.h index d9430a0..b21421e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.h +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_ModalComponentManager.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ -#define __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ +#ifndef JUCE_MODALCOMPONENTMANAGER_H_INCLUDED +#define JUCE_MODALCOMPONENTMANAGER_H_INCLUDED //============================================================================== @@ -38,8 +37,8 @@ @see Component::enterModalState, Component::exitModalState, Component::isCurrentlyModal, Component::getCurrentlyModalComponent, Component::isCurrentlyBlockedByAnotherModalComponent */ -class JUCE_API ModalComponentManager : public AsyncUpdater, - public DeletedAtShutdown +class JUCE_API ModalComponentManager : private AsyncUpdater, + private DeletedAtShutdown { public: //============================================================================== @@ -108,6 +107,11 @@ public: /** Brings any modal components to the front. */ void bringModalComponentsToFront (bool topOneShouldGrabFocus = true); + /** Calls exitModalState (0) on any components that are currently modal. + @returns true if any components were modal; false if nothing needed cancelling + */ + bool cancelAllModalComponents(); + #if JUCE_MODAL_LOOPS_PERMITTED /** Runs the event loop until the currently topmost modal component is dismissed, and returns the exit code for that component. @@ -128,7 +132,7 @@ protected: ~ModalComponentManager(); /** @internal */ - void handleAsyncUpdate(); + void handleAsyncUpdate() override; private: //============================================================================== @@ -136,14 +140,14 @@ private: class ReturnValueRetriever; friend class Component; - friend class OwnedArray ; - OwnedArray stack; + friend struct ContainerDeletePolicy; + OwnedArray stack; void startModal (Component*, bool autoDelete); void endModal (Component*, int returnValue); void endModal (Component*); - JUCE_DECLARE_NON_COPYABLE (ModalComponentManager); + JUCE_DECLARE_NON_COPYABLE (ModalComponentManager) }; //============================================================================== @@ -282,8 +286,8 @@ private: public: typedef void (*FunctionType) (int, ParamType); - FunctionCaller1 (FunctionType& function_, ParamType& param_) - : function (function_), param (param_) {} + FunctionCaller1 (FunctionType& f, ParamType& p1) + : function (f), param (p1) {} void modalStateFinished (int returnValue) { function (returnValue, param); } @@ -291,7 +295,7 @@ private: const FunctionType function; ParamType param; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller1); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller1) }; template @@ -300,8 +304,8 @@ private: public: typedef void (*FunctionType) (int, ParamType1, ParamType2); - FunctionCaller2 (FunctionType& function_, ParamType1& param1_, ParamType2& param2_) - : function (function_), param1 (param1_), param2 (param2_) {} + FunctionCaller2 (FunctionType& f, ParamType1& p1, ParamType2& p2) + : function (f), param1 (p1), param2 (p2) {} void modalStateFinished (int returnValue) { function (returnValue, param1, param2); } @@ -310,7 +314,7 @@ private: ParamType1 param1; ParamType2 param2; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller2); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FunctionCaller2) }; template @@ -319,8 +323,8 @@ private: public: typedef void (*FunctionType) (int, ComponentType*); - ComponentCaller1 (FunctionType& function_, ComponentType* comp_) - : function (function_), comp (comp_) {} + ComponentCaller1 (FunctionType& f, ComponentType* c) + : function (f), comp (c) {} void modalStateFinished (int returnValue) { @@ -331,7 +335,7 @@ private: const FunctionType function; WeakReference comp; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller1); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller1) }; template @@ -340,8 +344,8 @@ private: public: typedef void (*FunctionType) (int, ComponentType*, ParamType1); - ComponentCaller2 (FunctionType& function_, ComponentType* comp_, ParamType1 param1_) - : function (function_), comp (comp_), param1 (param1_) {} + ComponentCaller2 (FunctionType& f, ComponentType* c, ParamType1 p1) + : function (f), comp (c), param1 (p1) {} void modalStateFinished (int returnValue) { @@ -353,13 +357,13 @@ private: WeakReference comp; ParamType1 param1; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller2); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentCaller2) }; ModalCallbackFunction(); ~ModalCallbackFunction(); - JUCE_DECLARE_NON_COPYABLE (ModalCallbackFunction); + JUCE_DECLARE_NON_COPYABLE (ModalCallbackFunction) }; -#endif // __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ +#endif // JUCE_MODALCOMPONENTMANAGER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.cpp index 87b4f6a..3e88318 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -30,8 +29,8 @@ Drawable::Drawable() } Drawable::Drawable (const Drawable& other) + : Component (other.getName()) { - setName (other.getName()); setComponentID (other.getComponentID()); } @@ -74,7 +73,8 @@ void Drawable::drawAt (Graphics& g, float x, float y, float opacity) const draw (g, opacity, AffineTransform::translation (x, y)); } -void Drawable::drawWithin (Graphics& g, const Rectangle& destArea, const RectanglePlacement& placement, float opacity) const +void Drawable::drawWithin (Graphics& g, const Rectangle& destArea, + RectanglePlacement placement, float opacity) const { draw (g, opacity, placement.getTransformToFit (getDrawableBounds(), destArea)); } @@ -87,8 +87,7 @@ DrawableComposite* Drawable::getParent() const void Drawable::transformContextToCorrectOrigin (Graphics& g) { - g.setOrigin (originRelativeToComponent.x, - originRelativeToComponent.y); + g.setOrigin (originRelativeToComponent); } void Drawable::parentHierarchyChanged() @@ -109,12 +108,24 @@ void Drawable::setBoundsToEnclose (const Rectangle& area) } //============================================================================== -void Drawable::setOriginWithOriginalSize (const Point& originWithinParent) +bool Drawable::replaceColour (Colour original, Colour replacement) +{ + bool changed = false; + + for (int i = getNumChildComponents(); --i >= 0;) + if (Drawable* d = dynamic_cast (getChildComponent(i))) + changed = d->replaceColour (original, replacement) || changed; + + return changed; +} + +//============================================================================== +void Drawable::setOriginWithOriginalSize (Point originWithinParent) { setTransform (AffineTransform::translation (originWithinParent.x, originWithinParent.y)); } -void Drawable::setTransformToFit (const Rectangle& area, const RectanglePlacement& placement) +void Drawable::setTransformToFit (const Rectangle& area, RectanglePlacement placement) { if (! area.isEmpty()) setTransform (placement.getTransformToFit (getDrawableBounds(), area)); diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h index 3fbb73d..b8956d5 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h @@ -1,36 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DRAWABLE_JUCEHEADER__ -#define __JUCE_DRAWABLE_JUCEHEADER__ - -#include "../components/juce_Component.h" -#include "../positioning/juce_RelativeCoordinate.h" -#include "../positioning/juce_RelativeCoordinatePositioner.h" -#include "../layout/juce_ComponentBuilder.h" -class DrawableComposite; +#ifndef JUCE_DRAWABLE_H_INCLUDED +#define JUCE_DRAWABLE_H_INCLUDED //============================================================================== @@ -74,7 +67,7 @@ public: /** 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 + The coordinates 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 @@ -105,7 +98,7 @@ public: */ void drawWithin (Graphics& g, const Rectangle& destArea, - const RectanglePlacement& placement, + RectanglePlacement placement, float opacity) const; @@ -113,12 +106,12 @@ public: /** Resets any transformations on this drawable, and positions its origin within its parent component. */ - void setOriginWithOriginalSize (const Point& originWithinParent); + void setOriginWithOriginalSize (Point originWithinParent); /** Sets a transform for this drawable that will position it within the specified area of its parent component. */ - void setTransformToFit (const Rectangle& areaInParent, const RectanglePlacement& placement); + void setTransformToFit (const Rectangle& areaInParent, RectanglePlacement placement); /** Returns the DrawableComposite that contains this object, if there is one. */ DrawableComposite* getParent() const; @@ -149,13 +142,16 @@ public: into a Drawable tree. The object returned must be deleted by the caller. If something goes wrong - while parsing, it may return 0. + while parsing, it may return nullptr. 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); + /** Parses an SVG path string and returns it. */ + static Path parseSVGPath (const String& svgPath); + //============================================================================== /** Tries to create a Drawable from a previously-saved ValueTree. The ValueTree must have been created by the createValueTree() method. @@ -179,6 +175,11 @@ public: */ virtual Rectangle getDrawableBounds() const = 0; + /** Recursively replaces a colour that might be used for filling or stroking. + return true if any instances of this colour were found. + */ + virtual bool replaceColour (Colour originalColour, Colour replacementColour); + //============================================================================== /** Internal class used to manage ValueTrees that represent Drawables. */ class ValueTreeWrapperBase @@ -209,7 +210,7 @@ protected: /** @internal */ void transformContextToCorrectOrigin (Graphics&); /** @internal */ - void parentHierarchyChanged(); + void parentHierarchyChanged() override; /** @internal */ void setBoundsToEnclose (const Rectangle&); @@ -221,9 +222,9 @@ protected: class Positioner : public RelativeCoordinatePositionerBase { public: - Positioner (DrawableType& component_) - : RelativeCoordinatePositionerBase (component_), - owner (component_) + Positioner (DrawableType& c) + : RelativeCoordinatePositionerBase (c), + owner (c) {} bool registerCoordinates() { return owner.registerCoordinates (*this); } @@ -241,18 +242,18 @@ protected: private: DrawableType& owner; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Positioner) }; Drawable (const Drawable&); #endif private: - void nonConstDraw (Graphics& g, float opacity, const AffineTransform& transform); + void nonConstDraw (Graphics&, float opacity, const AffineTransform&); Drawable& operator= (const Drawable&); - JUCE_LEAK_DETECTOR (Drawable); + JUCE_LEAK_DETECTOR (Drawable) }; -#endif // __JUCE_DRAWABLE_JUCEHEADER__ +#endif // JUCE_DRAWABLE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp index 0c30762..688507f 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -41,12 +40,8 @@ DrawableComposite::DrawableComposite (const DrawableComposite& other) updateBoundsReentrant (false) { for (int i = 0; i < other.getNumChildComponents(); ++i) - { - const Drawable* const d = dynamic_cast (other.getChildComponent(i)); - - if (d != nullptr) + if (const Drawable* const d = dynamic_cast (other.getChildComponent(i))) addAndMakeVisible (d->createCopy()); - } } DrawableComposite::~DrawableComposite() @@ -65,13 +60,9 @@ Rectangle DrawableComposite::getDrawableBounds() const Rectangle r; for (int i = getNumChildComponents(); --i >= 0;) - { - const Drawable* const d = dynamic_cast (getChildComponent(i)); - - if (d != nullptr) - r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformed (d->getTransform()) + if (const Drawable* const d = dynamic_cast (getChildComponent(i))) + r = r.getUnion (d->isTransformed() ? d->getDrawableBounds().transformedBy (d->getTransform()) : d->getDrawableBounds()); - } return r; } @@ -200,12 +191,8 @@ void DrawableComposite::updateBoundsToFitChildren() originRelativeToComponent -= delta; for (int i = getNumChildComponents(); --i >= 0;) - { - Component* const c = getChildComponent(i); - - if (c != nullptr) + if (Component* const c = getChildComponent(i)) c->setBounds (c->getBounds() - delta); - } } setBounds (childArea); @@ -271,24 +258,24 @@ void DrawableComposite::ValueTreeWrapper::resetBoundingBoxToContentArea (UndoMan RelativeRectangle DrawableComposite::ValueTreeWrapper::getContentArea() const { - MarkerList::ValueTreeWrapper markersX (getMarkerList (true)); - MarkerList::ValueTreeWrapper markersY (getMarkerList (false)); + MarkerList::ValueTreeWrapper marksX (getMarkerList (true)); + MarkerList::ValueTreeWrapper marksY (getMarkerList (false)); - return RelativeRectangle (markersX.getMarker (markersX.getMarkerState (0)).position, - markersX.getMarker (markersX.getMarkerState (1)).position, - markersY.getMarker (markersY.getMarkerState (0)).position, - markersY.getMarker (markersY.getMarkerState (1)).position); + return RelativeRectangle (marksX.getMarker (marksX.getMarkerState (0)).position, + marksX.getMarker (marksX.getMarkerState (1)).position, + marksY.getMarker (marksY.getMarkerState (0)).position, + marksY.getMarker (marksY.getMarkerState (1)).position); } void DrawableComposite::ValueTreeWrapper::setContentArea (const RelativeRectangle& newArea, UndoManager* undoManager) { - MarkerList::ValueTreeWrapper markersX (getMarkerListCreating (true, nullptr)); - MarkerList::ValueTreeWrapper markersY (getMarkerListCreating (false, nullptr)); + MarkerList::ValueTreeWrapper marksX (getMarkerListCreating (true, nullptr)); + MarkerList::ValueTreeWrapper marksY (getMarkerListCreating (false, nullptr)); - markersX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager); - markersX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager); - markersY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager); - markersY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager); + marksX.setMarker (MarkerList::Marker (contentLeftMarkerName, newArea.left), undoManager); + marksX.setMarker (MarkerList::Marker (contentRightMarkerName, newArea.right), undoManager); + marksY.setMarker (MarkerList::Marker (contentTopMarkerName, newArea.top), undoManager); + marksY.setMarker (MarkerList::Marker (contentBottomMarkerName, newArea.bottom), undoManager); } MarkerList::ValueTreeWrapper DrawableComposite::ValueTreeWrapper::getMarkerList (bool xAxis) const diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.h index 7b3fa4e..1de9111 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableComposite.h @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ -#define __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ - -#include "juce_Drawable.h" -#include "../positioning/juce_MarkerList.h" -#include "../positioning/juce_RelativeParallelogram.h" -#include "../positioning/juce_RelativeRectangle.h" +#ifndef JUCE_DRAWABLECOMPOSITE_H_INCLUDED +#define JUCE_DRAWABLECOMPOSITE_H_INCLUDED //============================================================================== @@ -46,7 +40,7 @@ public: DrawableComposite(); /** Creates a copy of a DrawableComposite. */ - DrawableComposite (const DrawableComposite& other); + DrawableComposite (const DrawableComposite&); /** Destructor. */ ~DrawableComposite(); @@ -108,13 +102,13 @@ public: /** @internal */ Rectangle getDrawableBounds() const; /** @internal */ - void childBoundsChanged (Component*); + void childBoundsChanged (Component*) override; /** @internal */ - void childrenChanged(); + void childrenChanged() override; /** @internal */ - void parentHierarchyChanged(); + void parentHierarchyChanged() override; /** @internal */ - MarkerList* getMarkers (bool xAxis); + MarkerList* getMarkers (bool xAxis) override; //============================================================================== /** Internally-used class for wrapping a DrawableComposite's state into a ValueTree. */ @@ -155,8 +149,8 @@ private: void updateBoundsToFitChildren(); DrawableComposite& operator= (const DrawableComposite&); - JUCE_LEAK_DETECTOR (DrawableComposite); + JUCE_LEAK_DETECTOR (DrawableComposite) }; -#endif // __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ +#endif // JUCE_DRAWABLECOMPOSITE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp index c0a18eb..7c29dee 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -63,7 +62,7 @@ void DrawableImage::setOpacity (const float newOpacity) opacity = newOpacity; } -void DrawableImage::setOverlayColour (const Colour& newOverlayColour) +void DrawableImage::setOverlayColour (Colour newOverlayColour) { overlayColour = newOverlayColour; } @@ -206,7 +205,7 @@ Colour DrawableImage::ValueTreeWrapper::getOverlayColour() const return Colour::fromString (state [overlay].toString()); } -void DrawableImage::ValueTreeWrapper::setOverlayColour (const Colour& newColour, UndoManager* undoManager) +void DrawableImage::ValueTreeWrapper::setOverlayColour (Colour newColour, UndoManager* undoManager) { if (newColour.isTransparent()) state.removeProperty (overlay, undoManager); diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h index f1aa98f..6089dee 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableImage.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ -#define __JUCE_DRAWABLEIMAGE_JUCEHEADER__ - -#include "juce_Drawable.h" -#include "../positioning/juce_RelativeParallelogram.h" +#ifndef JUCE_DRAWABLEIMAGE_H_INCLUDED +#define JUCE_DRAWABLEIMAGE_H_INCLUDED //============================================================================== @@ -41,7 +37,7 @@ class JUCE_API DrawableImage : public Drawable public: //============================================================================== DrawableImage(); - DrawableImage (const DrawableImage& other); + DrawableImage (const DrawableImage&); /** Destructor. */ ~DrawableImage(); @@ -68,10 +64,10 @@ public: 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); + void setOverlayColour (Colour newOverlayColour); /** Returns the overlay colour. */ - const Colour& getOverlayColour() const noexcept { return overlayColour; } + Colour getOverlayColour() const noexcept { return overlayColour; } /** Sets the bounding box within which the image should be displayed. */ void setBoundingBox (const RelativeParallelogram& newBounds); @@ -84,17 +80,17 @@ public: //============================================================================== /** @internal */ - void paint (Graphics& g); + void paint (Graphics&) override; /** @internal */ - bool hitTest (int x, int y); + bool hitTest (int x, int y) override; /** @internal */ - Drawable* createCopy() const; + Drawable* createCopy() const override; /** @internal */ - Rectangle getDrawableBounds() const; + Rectangle getDrawableBounds() const override; /** @internal */ - void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder); + void refreshFromValueTree (const ValueTree& tree, ComponentBuilder&); /** @internal */ - ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const; + ValueTree createValueTree (ComponentBuilder::ImageProvider*) const override; /** @internal */ static const Identifier valueTreeType; @@ -106,19 +102,19 @@ public: ValueTreeWrapper (const ValueTree& state); var getImageIdentifier() const; - void setImageIdentifier (const var& newIdentifier, UndoManager* undoManager); - Value getImageIdentifierValue (UndoManager* undoManager); + void setImageIdentifier (const var&, UndoManager*); + Value getImageIdentifierValue (UndoManager*); float getOpacity() const; - void setOpacity (float newOpacity, UndoManager* undoManager); - Value getOpacityValue (UndoManager* undoManager); + void setOpacity (float newOpacity, UndoManager*); + Value getOpacityValue (UndoManager*); Colour getOverlayColour() const; - void setOverlayColour (const Colour& newColour, UndoManager* undoManager); - Value getOverlayColourValue (UndoManager* undoManager); + void setOverlayColour (Colour newColour, UndoManager*); + Value getOverlayColourValue (UndoManager*); RelativeParallelogram getBoundingBox() const; - void setBoundingBox (const RelativeParallelogram& newBounds, UndoManager* undoManager); + void setBoundingBox (const RelativeParallelogram&, UndoManager*); static const Identifier opacity, overlay, image, topLeft, topRight, bottomLeft; }; @@ -135,8 +131,8 @@ private: void recalculateCoordinates (Expression::Scope*); DrawableImage& operator= (const DrawableImage&); - JUCE_LEAK_DETECTOR (DrawableImage); + JUCE_LEAK_DETECTOR (DrawableImage) }; -#endif // __JUCE_DRAWABLEIMAGE_JUCEHEADER__ +#endif // JUCE_DRAWABLEIMAGE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp index 9890d6b..6dc46ba 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawablePath.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -78,9 +77,9 @@ void DrawablePath::applyRelativePath (const RelativePointPath& newRelativePath, class DrawablePath::RelativePositioner : public RelativeCoordinatePositionerBase { public: - RelativePositioner (DrawablePath& component_) - : RelativeCoordinatePositionerBase (component_), - owner (component_) + RelativePositioner (DrawablePath& comp) + : RelativeCoordinatePositionerBase (comp), + owner (comp) { } @@ -121,7 +120,7 @@ public: private: DrawablePath& owner; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner) }; void DrawablePath::setPath (const RelativePointPath& newRelativePath) @@ -174,20 +173,20 @@ void DrawablePath::ValueTreeWrapper::setUsesNonZeroWinding (bool b, UndoManager* state.setProperty (nonZeroWinding, b, undoManager); } -void DrawablePath::ValueTreeWrapper::readFrom (const RelativePointPath& relativePath, UndoManager* undoManager) +void DrawablePath::ValueTreeWrapper::readFrom (const RelativePointPath& p, UndoManager* undoManager) { - setUsesNonZeroWinding (relativePath.usesNonZeroWinding, undoManager); + setUsesNonZeroWinding (p.usesNonZeroWinding, undoManager); ValueTree pathTree (getPathState()); pathTree.removeAllChildren (undoManager); - for (int i = 0; i < relativePath.elements.size(); ++i) - pathTree.addChild (relativePath.elements.getUnchecked(i)->createTree(), -1, undoManager); + for (int i = 0; i < p.elements.size(); ++i) + pathTree.addChild (p.elements.getUnchecked(i)->createTree(), -1, undoManager); } -void DrawablePath::ValueTreeWrapper::writeTo (RelativePointPath& relativePath) const +void DrawablePath::ValueTreeWrapper::writeTo (RelativePointPath& p) const { - relativePath.usesNonZeroWinding = usesNonZeroWinding(); + p.usesNonZeroWinding = usesNonZeroWinding(); RelativePoint points[3]; const ValueTree pathTree (state.getChildWithName (path)); @@ -210,7 +209,7 @@ void DrawablePath::ValueTreeWrapper::writeTo (RelativePointPath& relativePath) c else if (t == Element::cubicToElement) newElement = new RelativePointPath::CubicTo (points[0], points[1], points[2]); else jassertfalse; - relativePath.addElement (newElement); + p.addElement (newElement); } } @@ -403,13 +402,13 @@ namespace DrawablePathHelpers } } -float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Point& targetPoint, Expression::Scope* scope) const +float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (Point targetPoint, Expression::Scope* scope) const { using namespace DrawablePathHelpers; - const Identifier type (state.getType()); + const Identifier pointType (state.getType()); float bestProp = 0; - if (type == cubicToElement) + if (pointType == cubicToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getControlPoint (1)), rp4 (getEndPoint()); @@ -430,7 +429,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po } } } - else if (type == quadraticToElement) + else if (pointType == quadraticToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getControlPoint (0)), rp3 (getEndPoint()); const Point points[] = { rp1.resolve (scope), rp2.resolve (scope), rp3.resolve (scope) }; @@ -450,7 +449,7 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po } } } - else if (type == lineToElement) + else if (pointType == lineToElement) { RelativePoint rp1 (getStartPoint()), rp2 (getEndPoint()); const Line line (rp1.resolve (scope), rp2.resolve (scope)); @@ -460,12 +459,12 @@ float DrawablePath::ValueTreeWrapper::Element::findProportionAlongLine (const Po return bestProp; } -ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::Scope* scope, UndoManager* undoManager) +ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (Point targetPoint, Expression::Scope* scope, UndoManager* undoManager) { ValueTree newTree; - const Identifier type (state.getType()); + const Identifier pointType (state.getType()); - if (type == cubicToElement) + if (pointType == cubicToElement) { float bestProp = findProportionAlongLine (targetPoint, scope); @@ -493,7 +492,7 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point line (rp1.resolve (scope), rp2.resolve (scope)); @@ -528,7 +527,7 @@ ValueTree DrawablePath::ValueTreeWrapper::Element::insertPoint (const Point& targetPoint, Expression::Scope*, UndoManager*); + ValueTree insertPoint (Point targetPoint, Expression::Scope*, UndoManager*); void removePoint (UndoManager* undoManager); - float findProportionAlongLine (const Point& targetPoint, Expression::Scope*) const; + float findProportionAlongLine (Point targetPoint, Expression::Scope*) const; static const Identifier mode, startSubPathElement, closeSubPathElement, lineToElement, quadraticToElement, cubicToElement; @@ -142,8 +138,8 @@ private: void applyRelativePath (const RelativePointPath&, Expression::Scope*); DrawablePath& operator= (const DrawablePath&); - JUCE_LEAK_DETECTOR (DrawablePath); + JUCE_LEAK_DETECTOR (DrawablePath) }; -#endif // __JUCE_DRAWABLEPATH_JUCEHEADER__ +#endif // JUCE_DRAWABLEPATH_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp index c9aa4bd..5a8c1c8 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -72,8 +71,8 @@ void DrawableRectangle::rebuildPath() } else { - setPositioner (0); - recalculateCoordinates (0); + setPositioner (nullptr); + recalculateCoordinates (nullptr); } } diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.h index 1912c20..9bae02e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableRectangle.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DRAWABLERECTANGLE_JUCEHEADER__ -#define __JUCE_DRAWABLERECTANGLE_JUCEHEADER__ - -#include "juce_DrawableShape.h" -#include "../positioning/juce_RelativeParallelogram.h" +#ifndef JUCE_DRAWABLERECTANGLE_H_INCLUDED +#define JUCE_DRAWABLERECTANGLE_H_INCLUDED //============================================================================== @@ -43,7 +39,7 @@ class JUCE_API DrawableRectangle : public DrawableShape public: //============================================================================== DrawableRectangle(); - DrawableRectangle (const DrawableRectangle& other); + DrawableRectangle (const DrawableRectangle&); /** Destructor. */ ~DrawableRectangle(); @@ -100,8 +96,8 @@ private: void recalculateCoordinates (Expression::Scope*); DrawableRectangle& operator= (const DrawableRectangle&); - JUCE_LEAK_DETECTOR (DrawableRectangle); + JUCE_LEAK_DETECTOR (DrawableRectangle) }; -#endif // __JUCE_DRAWABLERECTANGLE_JUCEHEADER__ +#endif // JUCE_DRAWABLERECTANGLE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp index 0327247..5813493 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -46,11 +45,11 @@ DrawableShape::~DrawableShape() class DrawableShape::RelativePositioner : public RelativeCoordinatePositionerBase { public: - RelativePositioner (DrawableShape& component_, const DrawableShape::RelativeFillType& fill_, bool isMainFill_) - : RelativeCoordinatePositionerBase (component_), - owner (component_), - fill (fill_), - isMainFill (isMainFill_) + RelativePositioner (DrawableShape& comp, const DrawableShape::RelativeFillType& f, bool isMain) + : RelativeCoordinatePositionerBase (comp), + owner (comp), + fill (f), + isMainFill (isMain) { } @@ -79,7 +78,7 @@ private: const DrawableShape::RelativeFillType fill; const bool isMainFill; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (RelativePositioner) }; void DrawableShape::setFill (const FillType& newFill) @@ -189,8 +188,8 @@ Rectangle DrawableShape::getDrawableBounds() const { if (isStrokeVisible()) return strokePath.getBounds(); - else - return path.getBounds(); + + return path.getBounds(); } bool DrawableShape::hitTest (int x, int y) @@ -453,3 +452,21 @@ void DrawableShape::FillAndStrokeState::setStrokeType (const PathStrokeType& new state.setProperty (capStyle, newStrokeType.getEndStyle() == PathStrokeType::butt ? "butt" : (newStrokeType.getEndStyle() == PathStrokeType::square ? "square" : "round"), undoManager); } + +static bool replaceColourInFill (DrawableShape::RelativeFillType& fill, Colour original, Colour replacement) +{ + if (fill.fill.colour == original && fill.fill.isColour()) + { + fill = FillType (replacement); + return true; + } + + return false; +} + +bool DrawableShape::replaceColour (Colour original, Colour replacement) +{ + bool changed1 = replaceColourInFill (mainFill, original, replacement); + bool changed2 = replaceColourInFill (strokeFill, original, replacement); + return changed1 || changed2; +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.h index 64099ab..ed893f1 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableShape.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DRAWABLESHAPE_JUCEHEADER__ -#define __JUCE_DRAWABLESHAPE_JUCEHEADER__ - -#include "juce_Drawable.h" -#include "../positioning/juce_RelativeCoordinatePositioner.h" +#ifndef JUCE_DRAWABLESHAPE_H_INCLUDED +#define JUCE_DRAWABLESHAPE_H_INCLUDED //============================================================================== @@ -146,11 +142,13 @@ public: }; /** @internal */ - Rectangle getDrawableBounds() const; + Rectangle getDrawableBounds() const override; /** @internal */ - void paint (Graphics& g); + void paint (Graphics&) override; /** @internal */ - bool hitTest (int x, int y); + bool hitTest (int x, int y) override; + /** @internal */ + bool replaceColour (Colour originalColour, Colour replacementColour) override; protected: //============================================================================== @@ -181,4 +179,4 @@ private: }; -#endif // __JUCE_DRAWABLESHAPE_JUCEHEADER__ +#endif // JUCE_DRAWABLESHAPE_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp index e2b3ee1..e9c9802 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -43,6 +42,7 @@ DrawableText::DrawableText (const DrawableText& other) colour (other.colour), justification (other.justification) { + refreshBounds(); } DrawableText::~DrawableText() @@ -59,7 +59,7 @@ void DrawableText::setText (const String& newText) } } -void DrawableText::setColour (const Colour& newColour) +void DrawableText::setColour (Colour newColour) { if (colour != newColour) { @@ -84,7 +84,7 @@ void DrawableText::setFont (const Font& newFont, bool applySizeAndScale) } } -void DrawableText::setJustification (const Justification& newJustification) +void DrawableText::setJustification (Justification newJustification) { justification = newJustification; repaint(); @@ -159,28 +159,21 @@ void DrawableText::recalculateCoordinates (Expression::Scope* scope) repaint(); } -const AffineTransform DrawableText::getArrangementAndTransform (GlyphArrangement& glyphs) const -{ - const float w = Line (resolvedPoints[0], resolvedPoints[1]).getLength(); - const float h = Line (resolvedPoints[0], resolvedPoints[2]).getLength(); - - glyphs.addFittedText (scaledFont, text, 0, 0, w, h, justification, 0x100000); - - return AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y, - w, 0, resolvedPoints[1].x, resolvedPoints[1].y, - 0, h, resolvedPoints[2].x, resolvedPoints[2].y); -} - //============================================================================== void DrawableText::paint (Graphics& g) { transformContextToCorrectOrigin (g); + const float w = Line (resolvedPoints[0], resolvedPoints[1]).getLength(); + const float h = Line (resolvedPoints[0], resolvedPoints[2]).getLength(); + + g.addTransform (AffineTransform::fromTargetPoints (0, 0, resolvedPoints[0].x, resolvedPoints[0].y, + w, 0, resolvedPoints[1].x, resolvedPoints[1].y, + 0, h, resolvedPoints[2].x, resolvedPoints[2].y)); + g.setFont (scaledFont); g.setColour (colour); - GlyphArrangement ga; - const AffineTransform transform (getArrangementAndTransform (ga)); - ga.draw (g, transform); + g.drawFittedText (text, Rectangle (w, h).getSmallestIntegerContainer(), justification, 0x100000); } Rectangle DrawableText::getDrawableBounds() const @@ -233,7 +226,7 @@ Colour DrawableText::ValueTreeWrapper::getColour() const return Colour::fromString (state [colour].toString()); } -void DrawableText::ValueTreeWrapper::setColour (const Colour& newColour, UndoManager* undoManager) +void DrawableText::ValueTreeWrapper::setColour (Colour newColour, UndoManager* undoManager) { state.setProperty (colour, newColour.toString(), undoManager); } @@ -243,7 +236,7 @@ Justification DrawableText::ValueTreeWrapper::getJustification() const return Justification ((int) state [justification]); } -void DrawableText::ValueTreeWrapper::setJustification (const Justification& newJustification, UndoManager* undoManager) +void DrawableText::ValueTreeWrapper::setJustification (Justification newJustification, UndoManager* undoManager) { state.setProperty (justification, newJustification.getFlags(), undoManager); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h index b3bd73f..ac44fb4 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_DrawableText.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ -#define __JUCE_DRAWABLETEXT_JUCEHEADER__ - -#include "juce_Drawable.h" -#include "../positioning/juce_RelativeParallelogram.h" +#ifndef JUCE_DRAWABLETEXT_H_INCLUDED +#define JUCE_DRAWABLETEXT_H_INCLUDED //============================================================================== @@ -42,7 +38,7 @@ public: //============================================================================== /** Creates a DrawableText object. */ DrawableText(); - DrawableText (const DrawableText& other); + DrawableText (const DrawableText&); /** Destructor. */ ~DrawableText(); @@ -51,11 +47,14 @@ public: /** Sets the text to display.*/ void setText (const String& newText); + /** Returns the currently displayed text */ + const String& getText() const noexcept { return text;} + /** Sets the colour of the text. */ - void setColour (const Colour& newColour); + void setColour (Colour newColour); /** Returns the current text colour. */ - const Colour& getColour() const noexcept { return colour; } + Colour getColour() const noexcept { return colour; } /** Sets the font to use. Note that the font height and horizontal scale are set as RelativeCoordinates using @@ -65,8 +64,14 @@ public: */ void setFont (const Font& newFont, bool applySizeAndScale); + /** Returns the current font. */ + const Font& getFont() const noexcept { return font; } + /** Changes the justification of the text within the bounding box. */ - void setJustification (const Justification& newJustification); + void setJustification (Justification newJustification); + + /** Returns the current justification. */ + Justification getJustification() const noexcept { return justification; } /** Returns the parallelogram that defines the text bounding box. */ const RelativeParallelogram& getBoundingBox() const noexcept { return bounds; } @@ -82,17 +87,17 @@ public: //============================================================================== /** @internal */ - void paint (Graphics& g); + void paint (Graphics&) override; /** @internal */ - Drawable* createCopy() const; + Drawable* createCopy() const override; /** @internal */ void refreshFromValueTree (const ValueTree& tree, ComponentBuilder& builder); /** @internal */ - ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const; + ValueTree createValueTree (ComponentBuilder::ImageProvider* imageProvider) const override; /** @internal */ static const Identifier valueTreeType; /** @internal */ - Rectangle getDrawableBounds() const; + Rectangle getDrawableBounds() const override; //============================================================================== /** Internally-used class for wrapping a DrawableText's state into a ValueTree. */ @@ -106,10 +111,10 @@ public: Value getTextValue (UndoManager* undoManager); Colour getColour() const; - void setColour (const Colour& newColour, UndoManager* undoManager); + void setColour (Colour newColour, UndoManager* undoManager); Justification getJustification() const; - void setJustification (const Justification& newJustification, UndoManager* undoManager); + void setJustification (Justification newJustification, UndoManager* undoManager); Font getFont() const; void setFont (const Font& newFont, UndoManager* undoManager); @@ -141,11 +146,10 @@ private: bool registerCoordinates (RelativeCoordinatePositionerBase&); void recalculateCoordinates (Expression::Scope*); void refreshBounds(); - const AffineTransform getArrangementAndTransform (GlyphArrangement& glyphs) const; DrawableText& operator= (const DrawableText&); - JUCE_LEAK_DETECTOR (DrawableText); + JUCE_LEAK_DETECTOR (DrawableText) }; -#endif // __JUCE_DRAWABLETEXT_JUCEHEADER__ +#endif // JUCE_DRAWABLETEXT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index 4445749..a61f0ac 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -27,54 +26,68 @@ class SVGState { public: //============================================================================== - SVGState (const XmlElement* const topLevel) - : topLevelXml (topLevel), + explicit SVGState (const XmlElement* const topLevel) + : topLevelXml (topLevel, nullptr), elementX (0), elementY (0), width (512), height (512), viewBoxW (0), viewBoxH (0) { } - //============================================================================== - Drawable* parseSVGElement (const XmlElement& xml) + struct XmlPath { - if (! xml.hasTagName ("svg")) + XmlPath (const XmlElement* e, const XmlPath* p) noexcept : xml (e), parent (p) {} + + const XmlElement& operator*() const noexcept { jassert (xml != nullptr); return *xml; } + const XmlElement* operator->() const noexcept { return xml; } + XmlPath getChild (const XmlElement* e) const noexcept { return XmlPath (e, this); } + + const XmlElement* xml; + const XmlPath* parent; + }; + + //============================================================================== + Drawable* parseSVGElement (const XmlPath& xml) + { + if (! xml->hasTagNameIgnoringNamespace ("svg")) return nullptr; DrawableComposite* const drawable = new DrawableComposite(); - drawable->setName (xml.getStringAttribute ("id")); + drawable->setName (xml->getStringAttribute ("id")); SVGState newState (*this); - if (xml.hasAttribute ("transform")) + if (xml->hasAttribute ("transform")) newState.addTransform (xml); - newState.elementX = getCoordLength (xml.getStringAttribute ("x", String (newState.elementX)), viewBoxW); - newState.elementY = getCoordLength (xml.getStringAttribute ("y", String (newState.elementY)), viewBoxH); - newState.width = getCoordLength (xml.getStringAttribute ("width", String (newState.width)), viewBoxW); - newState.height = getCoordLength (xml.getStringAttribute ("height", String (newState.height)), viewBoxH); + newState.elementX = getCoordLength (xml->getStringAttribute ("x", String (newState.elementX)), viewBoxW); + newState.elementY = getCoordLength (xml->getStringAttribute ("y", String (newState.elementY)), viewBoxH); + newState.width = getCoordLength (xml->getStringAttribute ("width", String (newState.width)), viewBoxW); + newState.height = getCoordLength (xml->getStringAttribute ("height", String (newState.height)), viewBoxH); - if (newState.width <= 0) newState.width = 100; + if (newState.width <= 0) newState.width = 100; if (newState.height <= 0) newState.height = 100; - if (xml.hasAttribute ("viewBox")) - { - const String viewBoxAtt (xml.getStringAttribute ("viewBox")); - String::CharPointerType viewParams (viewBoxAtt.getCharPointer()); - float vx, vy, vw, vh; + Point viewboxXY; - if (parseCoords (viewParams, vx, vy, true) - && parseCoords (viewParams, vw, vh, true) - && vw > 0 - && vh > 0) + if (xml->hasAttribute ("viewBox")) + { + const String viewBoxAtt (xml->getStringAttribute ("viewBox")); + String::CharPointerType viewParams (viewBoxAtt.getCharPointer()); + Point vwh; + + if (parseCoords (viewParams, viewboxXY, true) + && parseCoords (viewParams, vwh, true) + && vwh.x > 0 + && vwh.y > 0) { - newState.viewBoxW = vw; - newState.viewBoxH = vh; + newState.viewBoxW = vwh.x; + newState.viewBoxH = vwh.y; int placementFlags = 0; - const String aspect (xml.getStringAttribute ("preserveAspectRatio")); + const String aspect (xml->getStringAttribute ("preserveAspectRatio")); if (aspect.containsIgnoreCase ("none")) { @@ -82,122 +95,46 @@ public: } else { - if (aspect.containsIgnoreCase ("slice")) - placementFlags |= RectanglePlacement::fillDestination; + if (aspect.containsIgnoreCase ("slice")) placementFlags |= RectanglePlacement::fillDestination; - if (aspect.containsIgnoreCase ("xMin")) - placementFlags |= RectanglePlacement::xLeft; - else if (aspect.containsIgnoreCase ("xMax")) - placementFlags |= RectanglePlacement::xRight; - else - placementFlags |= RectanglePlacement::xMid; + if (aspect.containsIgnoreCase ("xMin")) placementFlags |= RectanglePlacement::xLeft; + else if (aspect.containsIgnoreCase ("xMax")) placementFlags |= RectanglePlacement::xRight; + else placementFlags |= RectanglePlacement::xMid; - if (aspect.containsIgnoreCase ("yMin")) - placementFlags |= RectanglePlacement::yTop; - else if (aspect.containsIgnoreCase ("yMax")) - placementFlags |= RectanglePlacement::yBottom; - else - placementFlags |= RectanglePlacement::yMid; + if (aspect.containsIgnoreCase ("yMin")) placementFlags |= RectanglePlacement::yTop; + else if (aspect.containsIgnoreCase ("yMax")) placementFlags |= RectanglePlacement::yBottom; + else placementFlags |= RectanglePlacement::yMid; } - const RectanglePlacement placement (placementFlags); - - newState.transform - = placement.getTransformToFit (Rectangle (vx, vy, vw, vh), - Rectangle (0.0f, 0.0f, newState.width, newState.height)) - .followedBy (newState.transform); + newState.transform = RectanglePlacement (placementFlags) + .getTransformToFit (Rectangle (viewboxXY.x, viewboxXY.y, vwh.x, vwh.y), + Rectangle (newState.width, newState.height)) + .followedBy (newState.transform); } } else { - if (viewBoxW == 0) - newState.viewBoxW = newState.width; - - if (viewBoxH == 0) - newState.viewBoxH = newState.height; + if (viewBoxW == 0) newState.viewBoxW = newState.width; + if (viewBoxH == 0) newState.viewBoxH = newState.height; } - newState.parseSubElements (xml, drawable); + newState.parseSubElements (xml, *drawable); - drawable->resetContentAreaAndBoundingBoxToFitChildren(); - return drawable; - } + drawable->setContentArea (RelativeRectangle (RelativeCoordinate (viewboxXY.x), + RelativeCoordinate (viewboxXY.x + newState.viewBoxW), + RelativeCoordinate (viewboxXY.y), + RelativeCoordinate (viewboxXY.y + newState.viewBoxH))); + drawable->resetBoundingBoxToContentArea(); -private: - //============================================================================== - const XmlElement* const topLevelXml; - float elementX, elementY, width, height, viewBoxW, viewBoxH; - AffineTransform transform; - String cssStyleText; - - //============================================================================== - void parseSubElements (const XmlElement& xml, DrawableComposite* const parentDrawable) - { - forEachXmlChildElement (xml, e) - { - Drawable* d = nullptr; - - if (e->hasTagName ("g")) d = parseGroupElement (*e); - else if (e->hasTagName ("svg")) d = parseSVGElement (*e); - else if (e->hasTagName ("path")) d = parsePath (*e); - else if (e->hasTagName ("rect")) d = parseRect (*e); - else if (e->hasTagName ("circle")) d = parseCircle (*e); - else if (e->hasTagName ("ellipse")) d = parseEllipse (*e); - else if (e->hasTagName ("line")) d = parseLine (*e); - else if (e->hasTagName ("polyline")) d = parsePolygon (*e, true); - else if (e->hasTagName ("polygon")) d = parsePolygon (*e, false); - else if (e->hasTagName ("text")) d = parseText (*e); - else if (e->hasTagName ("switch")) d = parseSwitch (*e); - else if (e->hasTagName ("style")) parseCSSStyle (*e); - - parentDrawable->addAndMakeVisible (d); - } - } - - DrawableComposite* parseSwitch (const XmlElement& xml) - { - const XmlElement* const group = xml.getChildByName ("g"); - - if (group != nullptr) - return parseGroupElement (*group); - - return nullptr; - } - - DrawableComposite* parseGroupElement (const XmlElement& xml) - { - DrawableComposite* const drawable = new DrawableComposite(); - - drawable->setName (xml.getStringAttribute ("id")); - - if (xml.hasAttribute ("transform")) - { - SVGState newState (*this); - newState.addTransform (xml); - - newState.parseSubElements (xml, drawable); - } - else - { - parseSubElements (xml, drawable); - } - - drawable->resetContentAreaAndBoundingBoxToFitChildren(); return drawable; } //============================================================================== - Drawable* parsePath (const XmlElement& xml) const + void parsePathString (Path& path, const String& pathString) const { - const String dAttribute (xml.getStringAttribute ("d").trimStart()); - String::CharPointerType d (dAttribute.getCharPointer()); - Path path; + String::CharPointerType d (pathString.getCharPointer().findEndOfWhitespace()); - if (getStyleAttribute (&xml, "fill-rule").trim().equalsIgnoreCase ("evenodd")) - path.setUsingNonZeroWinding (false); - - float lastX = 0, lastY = 0; - float lastX2 = 0, lastY2 = 0; + Point subpathStart, last, last2, p1, p2, p3; juce_wchar lastCommandChar = 0; bool isRelative = true; bool carryOn = true; @@ -206,8 +143,6 @@ private: while (! d.isEmpty()) { - float x, y, x2, y2, x3, y3; - if (validCommandChars.indexOf (*d) >= 0) { lastCommandChar = d.getAndAdvance(); @@ -220,45 +155,36 @@ private: case 'm': case 'L': case 'l': - if (parseCoords (d, x, y, false)) + if (parseCoordsOrSkip (d, p1, false)) { if (isRelative) - { - x += lastX; - y += lastY; - } + p1 += last; if (lastCommandChar == 'M' || lastCommandChar == 'm') { - path.startNewSubPath (x, y); + subpathStart = p1; + path.startNewSubPath (p1); lastCommandChar = 'l'; } else - path.lineTo (x, y); + path.lineTo (p1); - lastX2 = lastX; - lastY2 = lastY; - lastX = x; - lastY = y; + last2 = last; + last = p1; } - else - { - ++d; - } - break; case 'H': case 'h': - if (parseCoord (d, x, false, true)) + if (parseCoord (d, p1.x, false, true)) { if (isRelative) - x += lastX; + p1.x += last.x; - path.lineTo (x, lastY); + path.lineTo (p1.x, last.y); - lastX2 = lastX; - lastX = x; + last2.x = last.x; + last.x = p1.x; } else { @@ -268,15 +194,15 @@ private: case 'V': case 'v': - if (parseCoord (d, y, false, false)) + if (parseCoord (d, p1.y, false, false)) { if (isRelative) - y += lastY; + p1.y += last.y; - path.lineTo (lastX, y); + path.lineTo (last.x, p1.y); - lastY2 = lastY; - lastY = y; + last2.y = last.y; + last.y = p1.y; } else { @@ -286,115 +212,79 @@ private: case 'C': case 'c': - if (parseCoords (d, x, y, false) - && parseCoords (d, x2, y2, false) - && parseCoords (d, x3, y3, false)) + if (parseCoordsOrSkip (d, p1, false) + && parseCoordsOrSkip (d, p2, false) + && parseCoordsOrSkip (d, p3, false)) { if (isRelative) { - x += lastX; - y += lastY; - x2 += lastX; - y2 += lastY; - x3 += lastX; - y3 += lastY; + p1 += last; + p2 += last; + p3 += last; } - path.cubicTo (x, y, x2, y2, x3, y3); + path.cubicTo (p1, p2, p3); - lastX2 = x2; - lastY2 = y2; - lastX = x3; - lastY = y3; - } - else - { - ++d; + last2 = p2; + last = p3; } break; case 'S': case 's': - if (parseCoords (d, x, y, false) - && parseCoords (d, x3, y3, false)) + if (parseCoordsOrSkip (d, p1, false) + && parseCoordsOrSkip (d, p3, false)) { if (isRelative) { - x += lastX; - y += lastY; - x3 += lastX; - y3 += lastY; + p1 += last; + p3 += last; } - x2 = lastX + (lastX - lastX2); - y2 = lastY + (lastY - lastY2); - path.cubicTo (x2, y2, x, y, x3, y3); + p2 = last + (last - last2); + path.cubicTo (p2, p1, p3); - lastX2 = x; - lastY2 = y; - lastX = x3; - lastY = y3; - } - else - { - ++d; + last2 = p1; + last = p3; } break; case 'Q': case 'q': - if (parseCoords (d, x, y, false) - && parseCoords (d, x2, y2, false)) + if (parseCoordsOrSkip (d, p1, false) + && parseCoordsOrSkip (d, p2, false)) { if (isRelative) { - x += lastX; - y += lastY; - x2 += lastX; - y2 += lastY; + p1 += last; + p2 += last; } - path.quadraticTo (x, y, x2, y2); + path.quadraticTo (p1, p2); - lastX2 = x; - lastY2 = y; - lastX = x2; - lastY = y2; - } - else - { - ++d; + last2 = p1; + last = p2; } break; case 'T': case 't': - if (parseCoords (d, x, y, false)) + if (parseCoordsOrSkip (d, p1, false)) { if (isRelative) - { - x += lastX; - y += lastY; - } + p1 += last; - x2 = lastX + (lastX - lastX2); - y2 = lastY + (lastY - lastY2); - path.quadraticTo (x2, y2, x, y); + p2 = last + (last - last2); + path.quadraticTo (p2, p1); - lastX2 = x2; - lastY2 = y2; - lastX = x; - lastY = y; - } - else - { - ++d; + last2 = p2; + last = p1; } break; case 'A': case 'a': - if (parseCoords (d, x, y, false)) + if (parseCoordsOrSkip (d, p1, false)) { String num; @@ -410,20 +300,17 @@ private: { const bool sweep = num.getIntValue() != 0; - if (parseCoords (d, x2, y2, false)) + if (parseCoordsOrSkip (d, p2, false)) { if (isRelative) - { - x2 += lastX; - y2 += lastY; - } + p2 += last; - if (lastX != x2 || lastY != y2) + if (last != p2) { double centreX, centreY, startAngle, deltaAngle; - double rx = x, ry = y; + double rx = p1.x, ry = p1.y; - endpointToCentreParameters (lastX, lastY, x2, y2, + endpointToCentreParameters (last.x, last.y, p2.x, p2.y, angle, largeArc, sweep, rx, ry, centreX, centreY, startAngle, deltaAngle); @@ -433,29 +320,25 @@ private: angle, (float) startAngle, (float) (startAngle + deltaAngle), false); - path.lineTo (x2, y2); + path.lineTo (p2); } - lastX2 = lastX; - lastY2 = lastY; - lastX = x2; - lastY = y2; + last2 = last; + last = p2; } } } } } - else - { - ++d; - } break; case 'Z': case 'z': path.closeSubPath(); + last = last2 = subpathStart; d = d.findEndOfWhitespace(); + lastCommandChar = 'M'; break; default: @@ -467,78 +350,157 @@ private: break; } + // paths that finish back at their start position often seem to be + // left without a 'z', so need to be closed explicitly.. + if (path.getCurrentPosition() == subpathStart) + path.closeSubPath(); + } + +private: + //============================================================================== + const XmlPath topLevelXml; + float elementX, elementY, width, height, viewBoxW, viewBoxH; + AffineTransform transform; + String cssStyleText; + + //============================================================================== + void parseSubElements (const XmlPath& xml, DrawableComposite& parentDrawable) + { + forEachXmlChildElement (*xml, e) + parentDrawable.addAndMakeVisible (parseSubElement (xml.getChild (e))); + } + + Drawable* parseSubElement (const XmlPath& xml) + { + const String tag (xml->getTagNameWithoutNamespace()); + + if (tag == "g") return parseGroupElement (xml); + if (tag == "svg") return parseSVGElement (xml); + if (tag == "path") return parsePath (xml); + if (tag == "rect") return parseRect (xml); + if (tag == "circle") return parseCircle (xml); + if (tag == "ellipse") return parseEllipse (xml); + if (tag == "line") return parseLine (xml); + if (tag == "polyline") return parsePolygon (xml, true); + if (tag == "polygon") return parsePolygon (xml, false); + if (tag == "text") return parseText (xml); + if (tag == "switch") return parseSwitch (xml); + if (tag == "style") parseCSSStyle (xml); + + return nullptr; + } + + DrawableComposite* parseSwitch (const XmlPath& xml) + { + if (const XmlElement* const group = xml->getChildByName ("g")) + return parseGroupElement (xml.getChild (group)); + + return nullptr; + } + + DrawableComposite* parseGroupElement (const XmlPath& xml) + { + DrawableComposite* const drawable = new DrawableComposite(); + + drawable->setName (xml->getStringAttribute ("id")); + + if (xml->hasAttribute ("transform")) + { + SVGState newState (*this); + newState.addTransform (xml); + + newState.parseSubElements (xml, *drawable); + } + else + { + parseSubElements (xml, *drawable); + } + + drawable->resetContentAreaAndBoundingBoxToFitChildren(); + return drawable; + } + + //============================================================================== + Drawable* parsePath (const XmlPath& xml) const + { + Path path; + parsePathString (path, xml->getStringAttribute ("d")); + + if (getStyleAttribute (xml, "fill-rule").trim().equalsIgnoreCase ("evenodd")) + path.setUsingNonZeroWinding (false); + return parseShape (xml, path); } - Drawable* parseRect (const XmlElement& xml) const + Drawable* parseRect (const XmlPath& xml) const { Path rect; - const bool hasRX = xml.hasAttribute ("rx"); - const bool hasRY = xml.hasAttribute ("ry"); + const bool hasRX = xml->hasAttribute ("rx"); + const bool hasRY = xml->hasAttribute ("ry"); if (hasRX || hasRY) { - float rx = getCoordLength (xml.getStringAttribute ("rx"), viewBoxW); - float ry = getCoordLength (xml.getStringAttribute ("ry"), viewBoxH); + float rx = getCoordLength (xml, "rx", viewBoxW); + float ry = getCoordLength (xml, "ry", viewBoxH); if (! hasRX) rx = ry; else if (! hasRY) ry = rx; - rect.addRoundedRectangle (getCoordLength (xml.getStringAttribute ("x"), viewBoxW), - getCoordLength (xml.getStringAttribute ("y"), viewBoxH), - getCoordLength (xml.getStringAttribute ("width"), viewBoxW), - getCoordLength (xml.getStringAttribute ("height"), viewBoxH), + rect.addRoundedRectangle (getCoordLength (xml, "x", viewBoxW), + getCoordLength (xml, "y", viewBoxH), + getCoordLength (xml, "width", viewBoxW), + getCoordLength (xml, "height", viewBoxH), rx, ry); } else { - rect.addRectangle (getCoordLength (xml.getStringAttribute ("x"), viewBoxW), - getCoordLength (xml.getStringAttribute ("y"), viewBoxH), - getCoordLength (xml.getStringAttribute ("width"), viewBoxW), - getCoordLength (xml.getStringAttribute ("height"), viewBoxH)); + rect.addRectangle (getCoordLength (xml, "x", viewBoxW), + getCoordLength (xml, "y", viewBoxH), + getCoordLength (xml, "width", viewBoxW), + getCoordLength (xml, "height", viewBoxH)); } return parseShape (xml, rect); } - Drawable* parseCircle (const XmlElement& xml) const + Drawable* parseCircle (const XmlPath& xml) const { Path circle; - const float cx = getCoordLength (xml.getStringAttribute ("cx"), viewBoxW); - const float cy = getCoordLength (xml.getStringAttribute ("cy"), viewBoxH); - const float radius = getCoordLength (xml.getStringAttribute ("r"), viewBoxW); + const float cx = getCoordLength (xml, "cx", viewBoxW); + const float cy = getCoordLength (xml, "cy", viewBoxH); + const float radius = getCoordLength (xml, "r", viewBoxW); circle.addEllipse (cx - radius, cy - radius, radius * 2.0f, radius * 2.0f); return parseShape (xml, circle); } - Drawable* parseEllipse (const XmlElement& xml) const + Drawable* parseEllipse (const XmlPath& xml) const { Path ellipse; - const float cx = getCoordLength (xml.getStringAttribute ("cx"), viewBoxW); - const float cy = getCoordLength (xml.getStringAttribute ("cy"), viewBoxH); - const float radiusX = getCoordLength (xml.getStringAttribute ("rx"), viewBoxW); - const float radiusY = getCoordLength (xml.getStringAttribute ("ry"), viewBoxH); + const float cx = getCoordLength (xml, "cx", viewBoxW); + const float cy = getCoordLength (xml, "cy", viewBoxH); + const float radiusX = getCoordLength (xml, "rx", viewBoxW); + const float radiusY = getCoordLength (xml, "ry", viewBoxH); ellipse.addEllipse (cx - radiusX, cy - radiusY, radiusX * 2.0f, radiusY * 2.0f); return parseShape (xml, ellipse); } - Drawable* parseLine (const XmlElement& xml) const + Drawable* parseLine (const XmlPath& xml) const { Path line; - const float x1 = getCoordLength (xml.getStringAttribute ("x1"), viewBoxW); - const float y1 = getCoordLength (xml.getStringAttribute ("y1"), viewBoxH); - const float x2 = getCoordLength (xml.getStringAttribute ("x2"), viewBoxW); - const float y2 = getCoordLength (xml.getStringAttribute ("y2"), viewBoxH); + const float x1 = getCoordLength (xml, "x1", viewBoxW); + const float y1 = getCoordLength (xml, "y1", viewBoxH); + const float x2 = getCoordLength (xml, "x2", viewBoxW); + const float y2 = getCoordLength (xml, "y2", viewBoxH); line.startNewSubPath (x1, y1); line.lineTo (x2, y2); @@ -546,29 +508,26 @@ private: return parseShape (xml, line); } - Drawable* parsePolygon (const XmlElement& xml, const bool isPolyline) const + Drawable* parsePolygon (const XmlPath& xml, const bool isPolyline) const { - const String pointsAtt (xml.getStringAttribute ("points")); + const String pointsAtt (xml->getStringAttribute ("points")); String::CharPointerType points (pointsAtt.getCharPointer()); Path path; - float x, y; + Point p; - if (parseCoords (points, x, y, true)) + if (parseCoords (points, p, true)) { - float firstX = x; - float firstY = y; - float lastX = 0, lastY = 0; + Point first (p), last; - path.startNewSubPath (x, y); + path.startNewSubPath (first); - while (parseCoords (points, x, y, true)) + while (parseCoords (points, p, true)) { - lastX = x; - lastY = y; - path.lineTo (x, y); + last = p; + path.lineTo (p); } - if ((! isPolyline) || (firstX == lastX && firstY == lastY)) + if ((! isPolyline) || first == last) path.closeSubPath(); } @@ -576,10 +535,10 @@ private: } //============================================================================== - Drawable* parseShape (const XmlElement& xml, Path& path, + Drawable* parseShape (const XmlPath& xml, Path& path, const bool shouldParseTransform = true) const { - if (shouldParseTransform && xml.hasAttribute ("transform")) + if (shouldParseTransform && xml->hasAttribute ("transform")) { SVGState newState (*this); newState.addTransform (xml); @@ -588,7 +547,7 @@ private: } DrawablePath* dp = new DrawablePath(); - dp->setName (xml.getStringAttribute ("id")); + dp->setName (xml->getStringAttribute ("id")); dp->setFill (Colours::transparentBlack); path.applyTransform (transform); @@ -607,64 +566,201 @@ private: } dp->setFill (getPathFillType (path, - getStyleAttribute (&xml, "fill"), - getStyleAttribute (&xml, "fill-opacity"), - getStyleAttribute (&xml, "opacity"), + getStyleAttribute (xml, "fill"), + getStyleAttribute (xml, "fill-opacity"), + getStyleAttribute (xml, "opacity"), containsClosedSubPath ? Colours::black : Colours::transparentBlack)); - const String strokeType (getStyleAttribute (&xml, "stroke")); + const String strokeType (getStyleAttribute (xml, "stroke")); if (strokeType.isNotEmpty() && ! strokeType.equalsIgnoreCase ("none")) { dp->setStrokeFill (getPathFillType (path, strokeType, - getStyleAttribute (&xml, "stroke-opacity"), - getStyleAttribute (&xml, "opacity"), + getStyleAttribute (xml, "stroke-opacity"), + getStyleAttribute (xml, "opacity"), Colours::transparentBlack)); - dp->setStrokeType (getStrokeFor (&xml)); + dp->setStrokeType (getStrokeFor (xml)); } return dp; } - const XmlElement* findLinkedElement (const XmlElement* e) const + struct SetGradientStopsOp { - const String id (e->getStringAttribute ("xlink:href")); + const SVGState* state; + ColourGradient* gradient; - if (! id.startsWithChar ('#')) - return nullptr; - - return findElementForId (topLevelXml, id.substring (1)); - } - - void addGradientStopsIn (ColourGradient& cg, const XmlElement* const fillXml) const - { - if (fillXml == 0) - return; - - forEachXmlChildElementWithTagName (*fillXml, e, "stop") + void operator() (const XmlPath& xml) { - int index = 0; - Colour col (parseColour (getStyleAttribute (e, "stop-color"), index, Colours::black)); + state->addGradientStopsIn (*gradient, xml); + } + }; - const String opacity (getStyleAttribute (e, "stop-opacity", "1")); - col = col.withMultipliedAlpha (jlimit (0.0f, 1.0f, opacity.getFloatValue())); + void addGradientStopsIn (ColourGradient& cg, const XmlPath& fillXml) const + { + if (fillXml.xml != nullptr) + { + forEachXmlChildElementWithTagName (*fillXml, e, "stop") + { + int index = 0; + Colour col (parseColour (getStyleAttribute (fillXml.getChild (e), "stop-color"), index, Colours::black)); - double offset = e->getDoubleAttribute ("offset"); + const String opacity (getStyleAttribute (fillXml.getChild (e), "stop-opacity", "1")); + col = col.withMultipliedAlpha (jlimit (0.0f, 1.0f, opacity.getFloatValue())); - if (e->getStringAttribute ("offset").containsChar ('%')) - offset *= 0.01; + double offset = e->getDoubleAttribute ("offset"); - cg.addColour (jlimit (0.0, 1.0, offset), col); + if (e->getStringAttribute ("offset").containsChar ('%')) + offset *= 0.01; + + cg.addColour (jlimit (0.0, 1.0, offset), col); + } } } + FillType getGradientFillType (const XmlPath& fillXml, + const Path& path, + const float opacity) const + { + ColourGradient gradient; + + { + const String id (fillXml->getStringAttribute ("xlink:href")); + + if (id.startsWithChar ('#')) + { + SetGradientStopsOp op = { this, &gradient, }; + findElementForId (topLevelXml, id.substring (1), op); + } + } + + addGradientStopsIn (gradient, fillXml); + + if (gradient.getNumColours() > 0) + { + gradient.addColour (0.0, gradient.getColour (0)); + gradient.addColour (1.0, gradient.getColour (gradient.getNumColours() - 1)); + } + else + { + gradient.addColour (0.0, Colours::black); + gradient.addColour (1.0, Colours::black); + } + + if (opacity < 1.0f) + gradient.multiplyOpacity (opacity); + + jassert (gradient.getNumColours() > 0); + + gradient.isRadial = fillXml->hasTagNameIgnoringNamespace ("radialGradient"); + + float gradientWidth = viewBoxW; + float gradientHeight = viewBoxH; + float dx = 0.0f; + float dy = 0.0f; + + const bool userSpace = fillXml->getStringAttribute ("gradientUnits").equalsIgnoreCase ("userSpaceOnUse"); + + if (! userSpace) + { + const Rectangle bounds (path.getBounds()); + dx = bounds.getX(); + dy = bounds.getY(); + gradientWidth = bounds.getWidth(); + gradientHeight = bounds.getHeight(); + } + + if (gradient.isRadial) + { + if (userSpace) + gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("cx", "50%"), gradientWidth), + dy + getCoordLength (fillXml->getStringAttribute ("cy", "50%"), gradientHeight)); + else + gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("cx", "50%"), 1.0f), + dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("cy", "50%"), 1.0f)); + + const float radius = getCoordLength (fillXml->getStringAttribute ("r", "50%"), gradientWidth); + gradient.point2 = gradient.point1 + Point (radius, 0.0f); + + //xxx (the fx, fy focal point isn't handled properly here..) + } + else + { + if (userSpace) + { + gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x1", "0%"), gradientWidth), + dy + getCoordLength (fillXml->getStringAttribute ("y1", "0%"), gradientHeight)); + + gradient.point2.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x2", "100%"), gradientWidth), + dy + getCoordLength (fillXml->getStringAttribute ("y2", "0%"), gradientHeight)); + } + else + { + gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x1", "0%"), 1.0f), + dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y1", "0%"), 1.0f)); + + gradient.point2.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x2", "100%"), 1.0f), + dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y2", "0%"), 1.0f)); + } + + if (gradient.point1 == gradient.point2) + return Colour (gradient.getColour (gradient.getNumColours() - 1)); + } + + FillType type (gradient); + + const AffineTransform gradientTransform (parseTransform (fillXml->getStringAttribute ("gradientTransform")) + .followedBy (transform)); + + if (gradient.isRadial) + { + type.transform = gradientTransform; + } + else + { + // Transform the perpendicular vector into the new coordinate space for the gradient. + // This vector is now the slope of the linear gradient as it should appear in the new coord space + const Point perpendicular (Point (gradient.point2.y - gradient.point1.y, + gradient.point1.x - gradient.point2.x) + .transformedBy (gradientTransform.withAbsoluteTranslation (0, 0))); + + const Point newGradPoint1 (gradient.point1.transformedBy (gradientTransform)); + const Point newGradPoint2 (gradient.point2.transformedBy (gradientTransform)); + + // Project the transformed gradient vector onto the transformed slope of the linear + // gradient as it should appear in the new coordinate space + const float scale = perpendicular.getDotProduct (newGradPoint2 - newGradPoint1) + / perpendicular.getDotProduct (perpendicular); + + type.gradient->point1 = newGradPoint1; + type.gradient->point2 = newGradPoint2 - perpendicular * scale; + } + + return type; + } + + struct GetFillTypeOp + { + const SVGState* state; + FillType* dest; + const Path* path; + float opacity; + + void operator() (const XmlPath& xml) + { + if (xml->hasTagNameIgnoringNamespace ("linearGradient") + || xml->hasTagNameIgnoringNamespace ("radialGradient")) + *dest = state->getGradientFillType (xml, *path, opacity); + } + }; + FillType getPathFillType (const Path& path, const String& fill, const String& fillOpacity, const String& overallOpacity, - const Colour& defaultColour) const + const Colour defaultColour) const { float opacity = 1.0f; @@ -679,106 +775,21 @@ private: const String id (fill.fromFirstOccurrenceOf ("#", false, false) .upToLastOccurrenceOf (")", false, false).trim()); - const XmlElement* const fillXml = findElementForId (topLevelXml, id); + FillType result; + GetFillTypeOp op = { this, &result, &path, opacity }; - if (fillXml != nullptr - && (fillXml->hasTagName ("linearGradient") - || fillXml->hasTagName ("radialGradient"))) - { - const XmlElement* inheritedFrom = findLinkedElement (fillXml); - - ColourGradient gradient; - - addGradientStopsIn (gradient, inheritedFrom); - addGradientStopsIn (gradient, fillXml); - - if (gradient.getNumColours() > 0) - { - gradient.addColour (0.0, gradient.getColour (0)); - gradient.addColour (1.0, gradient.getColour (gradient.getNumColours() - 1)); - } - else - { - gradient.addColour (0.0, Colours::black); - gradient.addColour (1.0, Colours::black); - } - - if (overallOpacity.isNotEmpty()) - gradient.multiplyOpacity (overallOpacity.getFloatValue()); - - jassert (gradient.getNumColours() > 0); - - gradient.isRadial = fillXml->hasTagName ("radialGradient"); - - float gradientWidth = viewBoxW; - float gradientHeight = viewBoxH; - float dx = 0.0f; - float dy = 0.0f; - - const bool userSpace = fillXml->getStringAttribute ("gradientUnits").equalsIgnoreCase ("userSpaceOnUse"); - - if (! userSpace) - { - const Rectangle bounds (path.getBounds()); - dx = bounds.getX(); - dy = bounds.getY(); - gradientWidth = bounds.getWidth(); - gradientHeight = bounds.getHeight(); - } - - if (gradient.isRadial) - { - if (userSpace) - gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("cx", "50%"), gradientWidth), - dy + getCoordLength (fillXml->getStringAttribute ("cy", "50%"), gradientHeight)); - else - gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("cx", "50%"), 1.0f), - dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("cy", "50%"), 1.0f)); - - const float radius = getCoordLength (fillXml->getStringAttribute ("r", "50%"), gradientWidth); - gradient.point2 = gradient.point1 + Point (radius, 0.0f); - - //xxx (the fx, fy focal point isn't handled properly here..) - } - else - { - if (userSpace) - { - gradient.point1.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x1", "0%"), gradientWidth), - dy + getCoordLength (fillXml->getStringAttribute ("y1", "0%"), gradientHeight)); - - gradient.point2.setXY (dx + getCoordLength (fillXml->getStringAttribute ("x2", "100%"), gradientWidth), - dy + getCoordLength (fillXml->getStringAttribute ("y2", "0%"), gradientHeight)); - } - else - { - gradient.point1.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x1", "0%"), 1.0f), - dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y1", "0%"), 1.0f)); - - gradient.point2.setXY (dx + gradientWidth * getCoordLength (fillXml->getStringAttribute ("x2", "100%"), 1.0f), - dy + gradientHeight * getCoordLength (fillXml->getStringAttribute ("y2", "0%"), 1.0f)); - } - - if (gradient.point1 == gradient.point2) - return Colour (gradient.getColour (gradient.getNumColours() - 1)); - } - - FillType type (gradient); - type.transform = parseTransform (fillXml->getStringAttribute ("gradientTransform")) - .followedBy (transform); - return type; - } + if (findElementForId (topLevelXml, id, op)) + return result; } if (fill.equalsIgnoreCase ("none")) return Colours::transparentBlack; int i = 0; - const Colour colour (parseColour (fill, i, defaultColour)); - return colour.withMultipliedAlpha (opacity); + return parseColour (fill, i, defaultColour).withMultipliedAlpha (opacity); } - PathStrokeType getStrokeFor (const XmlElement* const xml) const + PathStrokeType getStrokeFor (const XmlPath& xml) const { const String strokeWidth (getStyleAttribute (xml, "stroke-width")); const String cap (getStyleAttribute (xml, "stroke-linecap")); @@ -810,32 +821,32 @@ private: } //============================================================================== - Drawable* parseText (const XmlElement& xml) + Drawable* parseText (const XmlPath& xml) { Array xCoords, yCoords, dxCoords, dyCoords; - getCoordList (xCoords, getInheritedAttribute (&xml, "x"), true, true); - getCoordList (yCoords, getInheritedAttribute (&xml, "y"), true, false); - getCoordList (dxCoords, getInheritedAttribute (&xml, "dx"), true, true); - getCoordList (dyCoords, getInheritedAttribute (&xml, "dy"), true, false); + getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true); + getCoordList (yCoords, getInheritedAttribute (xml, "y"), true, false); + getCoordList (dxCoords, getInheritedAttribute (xml, "dx"), true, true); + getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false); //xxx not done text yet! - forEachXmlChildElement (xml, e) + forEachXmlChildElement (*xml, e) { if (e->isTextElement()) { const String text (e->getText()); Path path; - Drawable* s = parseShape (*e, path); + Drawable* s = parseShape (xml.getChild (e), path); delete s; // xxx not finished! } - else if (e->hasTagName ("tspan")) + else if (e->hasTagNameIgnoringNamespace ("tspan")) { - Drawable* s = parseText (*e); + Drawable* s = parseText (xml.getChild (e)); delete s; // xxx not finished! } } @@ -844,9 +855,9 @@ private: } //============================================================================== - void addTransform (const XmlElement& xml) + void addTransform (const XmlPath& xml) { - transform = parseTransform (xml.getStringAttribute ("transform")) + transform = parseTransform (xml->getStringAttribute ("transform")) .followedBy (transform); } @@ -865,10 +876,19 @@ private: return true; } - bool parseCoords (String::CharPointerType& s, float& x, float& y, const bool allowUnits) const + bool parseCoords (String::CharPointerType& s, Point& p, const bool allowUnits) const { - return parseCoord (s, x, allowUnits, true) - && parseCoord (s, y, allowUnits, false); + return parseCoord (s, p.x, allowUnits, true) + && parseCoord (s, p.y, allowUnits, false); + } + + bool parseCoordsOrSkip (String::CharPointerType& s, Point& p, const bool allowUnits) const + { + if (parseCoords (s, p, allowUnits)) + return true; + + if (! s.isEmpty()) ++s; + return false; } float getCoordLength (const String& s, const float sizeForProportions) const @@ -893,6 +913,11 @@ private: return n; } + float getCoordLength (const XmlPath& xml, const char* attName, const float sizeForProportions) const + { + return getCoordLength (xml->getStringAttribute (attName), sizeForProportions); + } + void getCoordList (Array & coords, const String& list, const bool allowUnits, const bool isX) const { @@ -904,13 +929,32 @@ private: } //============================================================================== - void parseCSSStyle (const XmlElement& xml) + void parseCSSStyle (const XmlPath& xml) { - cssStyleText = xml.getAllSubText() + "\n" + cssStyleText; + cssStyleText = xml->getAllSubText() + "\n" + cssStyleText; } - String getStyleAttribute (const XmlElement* xml, const String& attributeName, - const String& defaultValue = String::empty) const + static String::CharPointerType findStyleItem (String::CharPointerType source, String::CharPointerType name) + { + const int nameLength = (int) name.length(); + + while (! source.isEmpty()) + { + if (source.getAndAdvance() == '.' + && CharacterFunctions::compareIgnoreCaseUpTo (source, name, nameLength) == 0) + { + String::CharPointerType endOfName ((source + nameLength).findEndOfWhitespace()); + + if (*endOfName == '{') + return endOfName; + } + } + + return source; + } + + String getStyleAttribute (const XmlPath& xml, const String& attributeName, + const String& defaultValue = String()) const { if (xml->hasAttribute (attributeName)) return xml->getStringAttribute (attributeName, defaultValue); @@ -919,58 +963,45 @@ private: if (styleAtt.isNotEmpty()) { - const String value (getAttributeFromStyleList (styleAtt, attributeName, String::empty)); + const String value (getAttributeFromStyleList (styleAtt, attributeName, String())); if (value.isNotEmpty()) return value; } else if (xml->hasAttribute ("class")) { - const String className ("." + xml->getStringAttribute ("class")); + String::CharPointerType openBrace = findStyleItem (cssStyleText.getCharPointer(), + xml->getStringAttribute ("class").getCharPointer()); - int index = cssStyleText.indexOfIgnoreCase (className + " "); - - if (index < 0) - index = cssStyleText.indexOfIgnoreCase (className + "{"); - - if (index >= 0) + if (! openBrace.isEmpty()) { - const int openBracket = cssStyleText.indexOfChar (index, '{'); + String::CharPointerType closeBrace = CharacterFunctions::find (openBrace, (juce_wchar) '}'); - if (openBracket > index) + if (closeBrace != openBrace) { - const int closeBracket = cssStyleText.indexOfChar (openBracket, '}'); - - if (closeBracket > openBracket) - { - const String value (getAttributeFromStyleList (cssStyleText.substring (openBracket + 1, closeBracket), attributeName, defaultValue)); - - if (value.isNotEmpty()) - return value; - } + const String value (getAttributeFromStyleList (String (openBrace + 1, closeBrace), + attributeName, defaultValue)); + if (value.isNotEmpty()) + return value; } } } - xml = const_cast (topLevelXml)->findParentElementOf (xml); - - if (xml != nullptr) - return getStyleAttribute (xml, attributeName, defaultValue); + if (xml.parent != nullptr) + return getStyleAttribute (*xml.parent, attributeName, defaultValue); return defaultValue; } - String getInheritedAttribute (const XmlElement* xml, const String& attributeName) const + String getInheritedAttribute (const XmlPath& xml, const String& attributeName) const { if (xml->hasAttribute (attributeName)) return xml->getStringAttribute (attributeName); - xml = const_cast (topLevelXml)->findParentElementOf (xml); + if (xml.parent != nullptr) + return getInheritedAttribute (*xml.parent, attributeName); - if (xml != nullptr) - return getInheritedAttribute (xml, attributeName); - - return String::empty; + return String(); } //============================================================================== @@ -1013,61 +1044,51 @@ private: } //============================================================================== - static bool parseNextNumber (String::CharPointerType& s, String& value, const bool allowUnits) + static bool parseNextNumber (String::CharPointerType& text, String& value, const bool allowUnits) { + String::CharPointerType s (text); + while (s.isWhitespace() || *s == ',') ++s; String::CharPointerType start (s); - int numChars = 0; if (s.isDigit() || *s == '.' || *s == '-') - { - ++numChars; ++s; - } while (s.isDigit() || *s == '.') - { - ++numChars; ++s; - } if ((*s == 'e' || *s == 'E') && ((s + 1).isDigit() || s[1] == '-' || s[1] == '+')) { s += 2; - numChars += 2; while (s.isDigit()) - { - ++numChars; ++s; - } } if (allowUnits) - { while (s.isLetter()) - { - ++numChars; ++s; - } + + if (s == start) + { + text = s; + return false; } - if (numChars == 0) - return false; - - value = String (start, (size_t) numChars); + value = String (start, s); while (s.isWhitespace() || *s == ',') ++s; + text = s; return true; } //============================================================================== - static Colour parseColour (const String& s, int& index, const Colour& defaultColour) + static Colour parseColour (const String& s, int& index, const Colour defaultColour) { if (s [index] == '#') { @@ -1088,14 +1109,15 @@ private: return Colour ((uint8) (hex [0] * 0x11), (uint8) (hex [1] * 0x11), (uint8) (hex [2] * 0x11)); - else - return Colour ((uint8) ((hex [0] << 4) + hex [1]), - (uint8) ((hex [2] << 4) + hex [3]), - (uint8) ((hex [4] << 4) + hex [5])); + + return Colour ((uint8) ((hex [0] << 4) + hex [1]), + (uint8) ((hex [2] << 4) + hex [3]), + (uint8) ((hex [4] << 4) + hex [5])); } - else if (s [index] == 'r' - && s [index + 1] == 'g' - && s [index + 2] == 'b') + + if (s [index] == 'r' + && s [index + 1] == 'g' + && s [index + 2] == 'b') { const int openBracket = s.indexOfChar (index, '('); const int closeBracket = s.indexOfChar (openBracket, ')'); @@ -1123,7 +1145,7 @@ private: return Colours::findColourForName (s, defaultColour); } - static const AffineTransform parseTransform (String t) + static AffineTransform parseTransform (String t) { AffineTransform result; @@ -1132,7 +1154,7 @@ private: StringArray tokens; tokens.addTokens (t.fromFirstOccurrenceOf ("(", false, false) .upToFirstOccurrenceOf (")", false, false), - ", ", String::empty); + ", ", ""); tokens.removeEmptyStrings (true); @@ -1156,7 +1178,7 @@ private: else if (t.startsWithIgnoreCase ("scale")) { if (tokens.size() == 1) - trans = AffineTransform::scale (numbers[0], numbers[0]); + trans = AffineTransform::scale (numbers[0]); else trans = AffineTransform::scale (numbers[0], numbers[1]); } @@ -1223,8 +1245,6 @@ private: const double s2 = std::sqrt (s); rx *= s2; ry *= s2; - rx2 = rx * rx; - ry2 = ry * ry; c = 0; } @@ -1268,23 +1288,25 @@ private: deltaAngle = fmod (deltaAngle, double_Pi * 2.0); } - static const XmlElement* findElementForId (const XmlElement* const parent, const String& id) + template + static bool findElementForId (const XmlPath& parent, const String& id, OperationType& op) { forEachXmlChildElement (*parent, e) { if (e->compareAttribute ("id", id)) - return e; + { + op (parent.getChild (e)); + return true; + } - const XmlElement* const found = findElementForId (e, id); - - if (found != nullptr) - return found; + if (findElementForId (parent.getChild (e), id, op)) + return true; } - return nullptr; + return false; } - SVGState& operator= (const SVGState&); + SVGState& operator= (const SVGState&) JUCE_DELETED_FUNCTION; }; @@ -1292,5 +1314,13 @@ private: Drawable* Drawable::createFromSVG (const XmlElement& svgDocument) { SVGState state (&svgDocument); - return state.parseSVGElement (svgDocument); + return state.parseSVGElement (SVGState::XmlPath (&svgDocument, nullptr)); +} + +Path Drawable::parseSVGPath (const String& svgPath) +{ + SVGState state (nullptr); + Path p; + state.parsePathString (p, svgPath); + return p; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp index addfcc3..265df83 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.h index 5d0cf4a..ebe1680 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsDisplayComponent.h @@ -1,34 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ -#define __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ - -#include "../components/juce_Component.h" -#include "juce_DirectoryContentsList.h" -#include "juce_FileBrowserListener.h" +#ifndef JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED +#define JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED //============================================================================== @@ -109,8 +104,8 @@ protected: ListenerList listeners; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsDisplayComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsDisplayComponent) }; -#endif // __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ +#endif // JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp index 8b0adc8..97e9482 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -DirectoryContentsList::DirectoryContentsList (const FileFilter* const fileFilter_, - TimeSliceThread& thread_) - : fileFilter (fileFilter_), - thread (thread_), +DirectoryContentsList::DirectoryContentsList (const FileFilter* f, TimeSliceThread& t) + : fileFilter (f), thread (t), fileTypeFlags (File::ignoreHiddenFiles | File::findFiles), shouldStop (true) { @@ -49,11 +46,6 @@ bool DirectoryContentsList::ignoresHiddenFiles() const } //============================================================================== -const File& DirectoryContentsList::getDirectory() const -{ - return root; -} - void DirectoryContentsList::setDirectory (const File& directory, const bool includeDirectories, const bool includeFiles) @@ -64,6 +56,7 @@ void DirectoryContentsList::setDirectory (const File& directory, { clear(); root = directory; + changed(); // (this forces a refresh when setTypeFlags() is called, rather than triggering two refreshes) fileTypeFlags &= ~(File::findDirectories | File::findFiles); @@ -115,19 +108,19 @@ void DirectoryContentsList::refresh() } } -//============================================================================== -int DirectoryContentsList::getNumFiles() const +void DirectoryContentsList::setFileFilter (const FileFilter* newFileFilter) { - return files.size(); + const ScopedLock sl (fileListLock); + fileFilter = newFileFilter; } +//============================================================================== bool DirectoryContentsList::getFileInfo (const int index, FileInfo& result) const { const ScopedLock sl (fileListLock); - const FileInfo* const info = files [index]; - if (info != nullptr) + if (const FileInfo* const info = files [index]) { result = *info; return true; @@ -139,12 +132,11 @@ bool DirectoryContentsList::getFileInfo (const int index, File DirectoryContentsList::getFile (const int index) const { const ScopedLock sl (fileListLock); - const FileInfo* const info = files [index]; - if (info != nullptr) + if (const FileInfo* const info = files [index]) return root.getChildFile (info->filename); - return File::nonexistent; + return File(); } bool DirectoryContentsList::contains (const File& targetFile) const @@ -213,38 +205,39 @@ bool DirectoryContentsList::checkNextFile (bool& hasChanged) return true; } - else - { - fileFindHandle = nullptr; - } + + fileFindHandle = nullptr; } return false; } -int DirectoryContentsList::compareElements (const DirectoryContentsList::FileInfo* const first, - const DirectoryContentsList::FileInfo* const second) +struct FileInfoComparator { - #if JUCE_WINDOWS - if (first->isDirectory != second->isDirectory) - return first->isDirectory ? -1 : 1; - #endif + static int compareElements (const DirectoryContentsList::FileInfo* const first, + const DirectoryContentsList::FileInfo* const second) + { + #if JUCE_WINDOWS + if (first->isDirectory != second->isDirectory) + return first->isDirectory ? -1 : 1; + #endif - return first->filename.compareIgnoreCase (second->filename); -} + return first->filename.compareIgnoreCase (second->filename); + } +}; -bool DirectoryContentsList::addFile (const File& file, - const bool isDir, +bool DirectoryContentsList::addFile (const File& file, const bool isDir, const int64 fileSize, - const Time& modTime, - const Time& creationTime, + Time modTime, Time creationTime, const bool isReadOnly) { + const ScopedLock sl (fileListLock); + if (fileFilter == nullptr || ((! isDir) && fileFilter->isFileSuitable (file)) || (isDir && fileFilter->isDirectorySuitable (file))) { - ScopedPointer info (new FileInfo()); + ScopedPointer info (new FileInfo()); info->filename = file.getFileName(); info->fileSize = fileSize; @@ -253,13 +246,12 @@ bool DirectoryContentsList::addFile (const File& file, info->isDirectory = isDir; info->isReadOnly = isReadOnly; - const ScopedLock sl (fileListLock); - for (int i = files.size(); --i >= 0;) if (files.getUnchecked(i)->filename == info->filename) return false; - files.addSorted (*this, info.release()); + FileInfoComparator comp; + files.addSorted (comp, info.release()); return true; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h index 48367be..b2e6a21 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ -#define __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ - -#include "juce_FileFilter.h" +#ifndef JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED +#define JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED //============================================================================== @@ -40,7 +37,7 @@ @see FileListComponent, FileBrowserComponent */ class JUCE_API DirectoryContentsList : public ChangeBroadcaster, - public TimeSliceClient + private TimeSliceClient { public: //============================================================================== @@ -54,10 +51,9 @@ public: 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 + included in the list. If this is nullptr, 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 @@ -71,6 +67,9 @@ public: //============================================================================== + /** Returns the directory that's currently being used. */ + const File& getDirectory() const noexcept { return root; } + /** Sets the directory to look in for files. If the directory that's passed in is different to the current one, this will @@ -80,8 +79,15 @@ public: bool includeDirectories, bool includeFiles); - /** Returns the directory that's currently being used. */ - const File& getDirectory() const; + /** Returns true if this list contains directories. + @see setDirectory + */ + bool isFindingDirectories() const noexcept { return (fileTypeFlags & File::findDirectories) != 0; } + + /** Returns true if this list contains files. + @see setDirectory + */ + bool isFindingFiles() const noexcept { return (fileTypeFlags & File::findFiles) != 0; } /** Clears the list, and stops the thread scanning for files. */ void clear(); @@ -93,7 +99,6 @@ public: bool isStillLoading() const; /** Tells the list whether or not to ignore hidden files. - By default these are ignored. */ void setIgnoresHiddenFiles (bool shouldIgnoreHiddenFiles); @@ -103,6 +108,15 @@ public: */ bool ignoresHiddenFiles() const; + /** Replaces the current FileFilter. + This can be nullptr to have no filter. The DirectoryContentList does not take + ownership of this object - it just keeps a pointer to it, so you must manage its + lifetime. + Note that this only replaces the filter, it doesn't refresh the list - you'll + probably want to call refresh() after calling this. + */ + void setFileFilter (const FileFilter* newFileFilter); + //============================================================================== /** Contains cached information about one of the files in a DirectoryContentsList. */ @@ -122,13 +136,11 @@ public: int64 fileSize; /** File modification time. - As supplied by File::getLastModificationTime(). */ Time modificationTime; /** File creation time. - As supplied by File::getCreationTime(). */ Time creationTime; @@ -143,15 +155,14 @@ public: //============================================================================== /** Returns the number of files currently available in the list. - The info about one of these files can be retrieved with getFileInfo() or - getFile(). + 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; + int getNumFiles() const noexcept { return files.size(); } /** Returns the cached information about one of the files in the list. @@ -174,22 +185,16 @@ public: File getFile (int index) const; /** Returns the file filter being used. - The filter is specified in the constructor. */ - const FileFilter* getFilter() const { return fileFilter; } + const FileFilter* getFilter() const noexcept { return fileFilter; } /** Returns true if the list contains the specified file. */ bool contains (const File&) const; //============================================================================== /** @internal */ - int useTimeSlice(); - /** @internal */ - TimeSliceThread& getTimeSliceThread() { return thread; } - /** @internal */ - static int compareElements (const DirectoryContentsList::FileInfo* first, - const DirectoryContentsList::FileInfo* second); + TimeSliceThread& getTimeSliceThread() const noexcept { return thread; } private: File root; @@ -198,21 +203,21 @@ private: int fileTypeFlags; CriticalSection fileListLock; - OwnedArray files; + OwnedArray files; - ScopedPointer fileFindHandle; + ScopedPointer fileFindHandle; bool volatile shouldStop; + int useTimeSlice() override; void stopSearching(); void changed(); bool checkNextFile (bool& hasChanged); - bool addFile (const File& file, bool isDir, - const int64 fileSize, const Time& modTime, - const Time& creationTime, bool isReadOnly); - void setTypeFlags (int newFlags); + bool addFile (const File&, bool isDir, int64 fileSize, Time modTime, + Time creationTime, bool isReadOnly); + void setTypeFlags (int); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsList); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsList) }; -#endif // __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ +#endif // JUCE_DIRECTORYCONTENTSLIST_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp index c418f09..4a2f023 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -85,24 +84,24 @@ FileBrowserComponent::FileBrowserComponent (int flags_, fileListComponent->addListener (this); - addAndMakeVisible (¤tPathBox); + addAndMakeVisible (currentPathBox); currentPathBox.setEditableText (true); resetRecentPaths(); currentPathBox.addListener (this); - addAndMakeVisible (&filenameBox); + addAndMakeVisible (filenameBox); filenameBox.setMultiLine (false); filenameBox.setSelectAllWhenFocused (true); filenameBox.setText (filename, false); filenameBox.addListener (this); filenameBox.setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0); - addAndMakeVisible (&fileLabel); + addAndMakeVisible (fileLabel); fileLabel.attachToComponent (&filenameBox, true); addAndMakeVisible (goUpButton = getLookAndFeel().createFileBrowserGoUpButton()); goUpButton->addListener (this); - goUpButton->setTooltip (TRANS ("go up to parent directory")); + goUpButton->setTooltip (TRANS ("Go up to parent directory")); if (previewComp != nullptr) addAndMakeVisible (previewComp); @@ -157,10 +156,12 @@ File FileBrowserComponent::getSelectedFile (int index) const noexcept bool FileBrowserComponent::currentFileIsValid() const { + const File f (getSelectedFile (0)); + if (isSaveMode()) - return ! getSelectedFile (0).isDirectory(); - else - return getSelectedFile (0).exists(); + return (flags & canSelectDirectories) != 0 || ! f.isDirectory(); + + return f.exists(); } File FileBrowserComponent::getHighlightedFile() const noexcept @@ -176,7 +177,8 @@ void FileBrowserComponent::deselectAllFiles() //============================================================================== bool FileBrowserComponent::isFileSuitable (const File& file) const { - return (flags & canSelectFiles) != 0 && (fileFilter == nullptr || fileFilter->isFileSuitable (file)); + return (flags & canSelectFiles) != 0 + && (fileFilter == nullptr || fileFilter->isFileSuitable (file)); } bool FileBrowserComponent::isDirectorySuitable (const File&) const @@ -242,7 +244,7 @@ void FileBrowserComponent::setRoot (const File& newRootDirectory) if (currentRootName.isEmpty()) currentRootName = File::separatorString; - currentPathBox.setText (currentRootName, true); + currentPathBox.setText (currentRootName, dontSendNotification); goUpButton->setEnabled (currentRoot.getParentDirectory().isDirectory() && currentRoot.getParentDirectory() != currentRoot); @@ -300,12 +302,14 @@ void FileBrowserComponent::setFileFilter (const FileFilter* const newFileFilter) String FileBrowserComponent::getActionVerb() const { - return isSaveMode() ? TRANS("Save") : TRANS("Open"); + return isSaveMode() ? ((flags & canSelectDirectories) != 0 ? TRANS("Choose") + : TRANS("Save")) + : TRANS("Open"); } void FileBrowserComponent::setFilenameBoxLabel (const String& name) { - fileLabel.setText (name, false); + fileLabel.setText (name, dontSendNotification); } FilePreviewComponent* FileBrowserComponent::getPreviewComponent() const noexcept @@ -396,7 +400,7 @@ bool FileBrowserComponent::keyPressed (const KeyPress& key) { (void) key; -#if JUCE_LINUX || JUCE_WINDOWS + #if JUCE_LINUX || JUCE_WINDOWS if (key.getModifiers().isCommandDown() && (key.getKeyCode() == 'H' || key.getKeyCode() == 'h')) { @@ -404,7 +408,7 @@ bool FileBrowserComponent::keyPressed (const KeyPress& key) fileList->refresh(); return true; } -#endif + #endif return false; } @@ -493,7 +497,7 @@ void FileBrowserComponent::comboBoxChanged (ComboBox*) } } -void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPaths) +void FileBrowserComponent::getDefaultRoots (StringArray& rootNames, StringArray& rootPaths) { #if JUCE_WINDOWS Array roots; @@ -518,7 +522,7 @@ void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPa } else if (drive.isOnCDRomDrive()) { - name << TRANS(" [CD/DVD drive]"); + name << " [" << TRANS("CD/DVD drive") << ']'; } rootNames.add (name); @@ -528,17 +532,25 @@ void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPa rootNames.add (String::empty); rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName()); - rootNames.add ("Documents"); + rootNames.add (TRANS("Documents")); + rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName()); + rootNames.add (TRANS("Music")); + rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName()); + rootNames.add (TRANS("Pictures")); rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); - rootNames.add ("Desktop"); + rootNames.add (TRANS("Desktop")); #elif JUCE_MAC rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); - rootNames.add ("Home folder"); + rootNames.add (TRANS("Home folder")); rootPaths.add (File::getSpecialLocation (File::userDocumentsDirectory).getFullPathName()); - rootNames.add ("Documents"); + rootNames.add (TRANS("Documents")); + rootPaths.add (File::getSpecialLocation (File::userMusicDirectory).getFullPathName()); + rootNames.add (TRANS("Music")); + rootPaths.add (File::getSpecialLocation (File::userPicturesDirectory).getFullPathName()); + rootNames.add (TRANS("Pictures")); rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); - rootNames.add ("Desktop"); + rootNames.add (TRANS("Desktop")); rootPaths.add (String::empty); rootNames.add (String::empty); @@ -562,8 +574,13 @@ void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPa rootPaths.add ("/"); rootNames.add ("/"); rootPaths.add (File::getSpecialLocation (File::userHomeDirectory).getFullPathName()); - rootNames.add ("Home folder"); + rootNames.add (TRANS("Home folder")); rootPaths.add (File::getSpecialLocation (File::userDesktopDirectory).getFullPathName()); - rootNames.add ("Desktop"); + rootNames.add (TRANS("Desktop")); #endif } + +void FileBrowserComponent::getRoots (StringArray& rootNames, StringArray& rootPaths) +{ + getDefaultRoots (rootNames, rootPaths); +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h index 0478b37..a1151ef 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.h @@ -1,36 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ -#define __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ - -#include "juce_DirectoryContentsDisplayComponent.h" -#include "juce_FilePreviewComponent.h" -#include "../widgets/juce_TextEditor.h" -#include "../widgets/juce_ComboBox.h" -#include "../buttons/juce_DrawableButton.h" +#ifndef JUCE_FILEBROWSERCOMPONENT_H_INCLUDED +#define JUCE_FILEBROWSERCOMPONENT_H_INCLUDED //============================================================================== @@ -68,7 +61,8 @@ public: conjuction with canSelectFiles). */ canSelectMultipleItems = 16, /**< specifies that the user can select multiple items. */ useTreeView = 32, /**< specifies that a tree-view should be shown instead of a file list. */ - filenameBoxIsReadOnly = 64 /**< specifies that the user can't type directly into the filename box. */ + filenameBoxIsReadOnly = 64, /**< specifies that the user can't type directly into the filename box. */ + warnAboutOverwriting = 128 /**< specifies that the dialog should warn about overwriting existing files (if possible). */ }; //============================================================================== @@ -176,36 +170,78 @@ public: */ void removeListener (FileBrowserListener* listener); + /** Returns a platform-specific list of names and paths for some suggested places the user + might want to use as root folders. + The list returned contains empty strings to indicate section breaks. + @see getRoots() + */ + static void getDefaultRoots (StringArray& rootNames, StringArray& rootPaths); + + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes to provide + various file-browser layout and drawing methods. + */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + // These return a pointer to an internally cached drawable - make sure you don't keep + // a copy of this pointer anywhere, as it may become invalid in the future. + virtual const Drawable* getDefaultFolderImage() = 0; + virtual const Drawable* getDefaultDocumentFileImage() = 0; + + virtual AttributedString createFileChooserHeaderText (const String& title, + const String& instructions) = 0; + + virtual void drawFileBrowserRow (Graphics&, int width, int height, + const String& filename, + Image* optionalIcon, + const String& fileSizeDescription, + const String& fileTimeDescription, + bool isDirectory, + bool isItemSelected, + int itemIndex, + DirectoryContentsDisplayComponent&) = 0; + + virtual Button* createFileBrowserGoUpButton() = 0; + + virtual void layoutFileBrowserComponent (FileBrowserComponent& browserComp, + DirectoryContentsDisplayComponent* fileListComponent, + FilePreviewComponent* previewComp, + ComboBox* currentPathBox, + TextEditor* filenameBox, + Button* goUpButton) = 0; + }; //============================================================================== /** @internal */ - void resized(); + void resized() override; /** @internal */ - void buttonClicked (Button*); + void buttonClicked (Button*) override; /** @internal */ - void comboBoxChanged (ComboBox*); + void comboBoxChanged (ComboBox*) override; /** @internal */ - void textEditorTextChanged (TextEditor&); + void textEditorTextChanged (TextEditor&) override; /** @internal */ - void textEditorReturnKeyPressed (TextEditor&); + void textEditorReturnKeyPressed (TextEditor&) override; /** @internal */ - void textEditorEscapeKeyPressed (TextEditor&); + void textEditorEscapeKeyPressed (TextEditor&) override; /** @internal */ - void textEditorFocusLost (TextEditor&); + void textEditorFocusLost (TextEditor&) override; /** @internal */ - bool keyPressed (const KeyPress&); + bool keyPressed (const KeyPress&) override; /** @internal */ - void selectionChanged(); + void selectionChanged() override; /** @internal */ - void fileClicked (const File&, const MouseEvent&); + void fileClicked (const File&, const MouseEvent&) override; /** @internal */ - void fileDoubleClicked (const File&); + void fileDoubleClicked (const File&) override; /** @internal */ - void browserRootChanged (const File&); + void browserRootChanged (const File&) override; /** @internal */ - bool isFileSuitable (const File&) const; + bool isFileSuitable (const File&) const override; /** @internal */ - bool isDirectorySuitable (const File&) const; + bool isDirectorySuitable (const File&) const override; /** @internal */ FilePreviewComponent* getPreviewComponent() const noexcept; @@ -215,7 +251,9 @@ public: protected: /** Returns a list of names and paths for the default places the user might want to look. - Use an empty string to indicate a section break. + + By default this just calls getDefaultRoots(), but you may want to override it to + return a custom list. */ virtual void getRoots (StringArray& rootNames, StringArray& rootPaths); @@ -244,9 +282,9 @@ private: void sendListenerChangeMessage(); bool isFileOrDirSuitable (const File& f) const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBrowserComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileBrowserComponent) }; -#endif // __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ +#endif // JUCE_FILEBROWSERCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserListener.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserListener.h index ad26be4..bd206ae 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserListener.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserListener.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ -#define __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ - -#include "../mouse/juce_MouseEvent.h" +#ifndef JUCE_FILEBROWSERLISTENER_H_INCLUDED +#define JUCE_FILEBROWSERLISTENER_H_INCLUDED //============================================================================== @@ -57,4 +54,4 @@ public: }; -#endif // __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ +#endif // JUCE_FILEBROWSERLISTENER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp index 64e531f..71c148d 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,81 +25,88 @@ FileChooser::FileChooser (const String& chooserBoxTitle, const File& currentFileOrDirectory, const String& fileFilters, - const bool useNativeDialogBox_) + const bool useNativeBox) : title (chooserBoxTitle), filters (fileFilters), startingFile (currentFileOrDirectory), - useNativeDialogBox (useNativeDialogBox_) + useNativeDialogBox (useNativeBox && isPlatformDialogAvailable()) { - if (useNativeDialogBox) - { - static bool canUseNativeBox = isPlatformDialogAvailable(); - if (! canUseNativeBox) - useNativeDialogBox = false; - } - if (! fileFilters.containsNonWhitespaceChars()) filters = "*"; } -FileChooser::~FileChooser() -{ -} +FileChooser::~FileChooser() {} #if JUCE_MODAL_LOOPS_PERMITTED -bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComponent) +bool FileChooser::browseForFileToOpen (FilePreviewComponent* previewComp) { - return showDialog (false, true, false, false, false, previewComponent); + return showDialog (FileBrowserComponent::openMode + | FileBrowserComponent::canSelectFiles, + previewComp); } -bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent) +bool FileChooser::browseForMultipleFilesToOpen (FilePreviewComponent* previewComp) { - return showDialog (false, true, false, false, true, previewComponent); + return showDialog (FileBrowserComponent::openMode + | FileBrowserComponent::canSelectFiles + | FileBrowserComponent::canSelectMultipleItems, + previewComp); } -bool FileChooser::browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent) +bool FileChooser::browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComp) { - return showDialog (true, true, false, false, true, previewComponent); + return showDialog (FileBrowserComponent::openMode + | FileBrowserComponent::canSelectFiles + | FileBrowserComponent::canSelectDirectories + | FileBrowserComponent::canSelectMultipleItems, + previewComp); } -bool FileChooser::browseForFileToSave (const bool warnAboutOverwritingExistingFiles) +bool FileChooser::browseForFileToSave (const bool warnAboutOverwrite) { - return showDialog (false, true, true, warnAboutOverwritingExistingFiles, false, nullptr); + return showDialog (FileBrowserComponent::saveMode + | FileBrowserComponent::canSelectFiles + | (warnAboutOverwrite ? FileBrowserComponent::warnAboutOverwriting : 0), + nullptr); } bool FileChooser::browseForDirectory() { - return showDialog (true, false, false, false, false, nullptr); + return showDialog (FileBrowserComponent::openMode + | FileBrowserComponent::canSelectDirectories, + nullptr); } -bool FileChooser::showDialog (const bool selectsDirectories, - const bool selectsFiles, - const bool isSave, - const bool warnAboutOverwritingExistingFiles, - const bool selectMultipleFiles, - FilePreviewComponent* const previewComponent) +bool FileChooser::showDialog (const int flags, FilePreviewComponent* const previewComp) { - WeakReference previouslyFocused (Component::getCurrentlyFocusedComponent()); + FocusRestorer focusRestorer; results.clear(); // the preview component needs to be the right size before you pass it in here.. - jassert (previewComponent == nullptr || (previewComponent->getWidth() > 10 - && previewComponent->getHeight() > 10)); + jassert (previewComp == nullptr || (previewComp->getWidth() > 10 + && previewComp->getHeight() > 10)); + + const bool selectsDirectories = (flags & FileBrowserComponent::canSelectDirectories) != 0; + const bool selectsFiles = (flags & FileBrowserComponent::canSelectFiles) != 0; + const bool isSave = (flags & FileBrowserComponent::saveMode) != 0; + const bool warnAboutOverwrite = (flags & FileBrowserComponent::warnAboutOverwriting) != 0; + const bool selectMultiple = (flags & FileBrowserComponent::canSelectMultipleItems) != 0; + + // You've set the flags for both saveMode and openMode! + jassert (! (isSave && (flags & FileBrowserComponent::openMode) != 0)); #if JUCE_WINDOWS if (useNativeDialogBox && ! (selectsFiles && selectsDirectories)) #elif JUCE_MAC || JUCE_LINUX - if (useNativeDialogBox && (previewComponent == nullptr)) + if (useNativeDialogBox && (previewComp == nullptr)) #else if (false) #endif { showPlatformDialog (results, title, startingFile, filters, selectsDirectories, selectsFiles, isSave, - warnAboutOverwritingExistingFiles, - selectMultipleFiles, - previewComponent); + warnAboutOverwrite, selectMultiple, previewComp); } else { @@ -108,28 +114,10 @@ bool FileChooser::showDialog (const bool selectsDirectories, selectsDirectories ? "*" : String::empty, String::empty); - int flags = isSave ? FileBrowserComponent::saveMode - : FileBrowserComponent::openMode; - - if (selectsFiles) - flags |= FileBrowserComponent::canSelectFiles; - - if (selectsDirectories) - { - flags |= FileBrowserComponent::canSelectDirectories; - - if (! isSave) - flags |= FileBrowserComponent::filenameBoxIsReadOnly; - } - - if (selectMultipleFiles) - flags |= FileBrowserComponent::canSelectMultipleItems; - - FileBrowserComponent browserComponent (flags, startingFile, &wildcard, previewComponent); + FileBrowserComponent browserComponent (flags, startingFile, &wildcard, previewComp); FileChooserDialogBox box (title, String::empty, - browserComponent, - warnAboutOverwritingExistingFiles, + browserComponent, warnAboutOverwrite, browserComponent.findColour (AlertWindow::backgroundColourId)); if (box.show()) @@ -139,9 +127,6 @@ bool FileChooser::showDialog (const bool selectsDirectories, } } - if (previouslyFocused != nullptr) - previouslyFocused->grabKeyboardFocus(); - return results.size() > 0; } #endif @@ -155,16 +140,6 @@ File FileChooser::getResult() const return results.getFirst(); } -const Array& FileChooser::getResults() const -{ - return results; -} - //============================================================================== -FilePreviewComponent::FilePreviewComponent() -{ -} - -FilePreviewComponent::~FilePreviewComponent() -{ -} +FilePreviewComponent::FilePreviewComponent() {} +FilePreviewComponent::~FilePreviewComponent() {} diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.h index 32899e9..7c6b69f 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooser.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILECHOOSER_JUCEHEADER__ -#define __JUCE_FILECHOOSER_JUCEHEADER__ - -#include "juce_FilePreviewComponent.h" +#ifndef JUCE_FILECHOOSER_H_INCLUDED +#define JUCE_FILECHOOSER_H_INCLUDED //============================================================================== @@ -100,14 +97,14 @@ public: method to find out what it was. Returns false if they cancelled instead. @see browseForFileToSave, browseForDirectory */ - bool browseForFileToOpen (FilePreviewComponent* previewComponent = 0); + bool browseForFileToOpen (FilePreviewComponent* previewComponent = nullptr); /** 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); + bool browseForMultipleFilesToOpen (FilePreviewComponent* previewComponent = nullptr); /** Shows a dialog box to choose a file to save. @@ -141,7 +138,18 @@ public: 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); + bool browseForMultipleFilesOrDirectories (FilePreviewComponent* previewComponent = nullptr); + + //============================================================================== + /** Runs a dialog box for the given set of option flags. + The flag values used are those in FileBrowserComponent::FileChooserFlags. + + @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 FileBrowserComponent::FileChooserFlags + */ + bool showDialog (int flags, FilePreviewComponent* previewComponent); //============================================================================== /** Returns the last file that was chosen by one of the browseFor methods. @@ -167,18 +175,14 @@ public: @see getResult */ - const Array& getResults() const; + const Array& getResults() const noexcept { return results; } private: //============================================================================== String title, filters; - File startingFile; + const File startingFile; Array results; - bool useNativeDialogBox; - - bool showDialog (bool selectsDirectories, bool selectsFiles, bool isSave, - bool warnAboutOverwritingExistingFiles, bool selectMultipleFiles, - FilePreviewComponent* previewComponent); + const bool useNativeDialogBox; static void showPlatformDialog (Array& results, const String& title, const File& file, const String& filters, bool selectsDirectories, bool selectsFiles, @@ -186,8 +190,8 @@ private: FilePreviewComponent* previewComponent); static bool isPlatformDialogAvailable(); - JUCE_LEAK_DETECTOR (FileChooser); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooser) }; -#endif // __JUCE_FILECHOOSER_JUCEHEADER__ +#endif // JUCE_FILECHOOSER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp index 0d6a75e..29618a3 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,43 +25,43 @@ class FileChooserDialogBox::ContentComponent : public Component { public: - //============================================================================== - ContentComponent (const String& name, const String& instructions_, FileBrowserComponent& chooserComponent_) + ContentComponent (const String& name, const String& desc, FileBrowserComponent& chooser) : Component (name), - chooserComponent (chooserComponent_), - okButton (chooserComponent_.getActionVerb()), + chooserComponent (chooser), + okButton (chooser.getActionVerb()), cancelButton (TRANS ("Cancel")), newFolderButton (TRANS ("New Folder")), - instructions (instructions_) + instructions (desc) { - addAndMakeVisible (&chooserComponent); + addAndMakeVisible (chooserComponent); - addAndMakeVisible (&okButton); - okButton.addShortcut (KeyPress::returnKey); + addAndMakeVisible (okButton); + okButton.addShortcut (KeyPress (KeyPress::returnKey)); - addAndMakeVisible (&cancelButton); - cancelButton.addShortcut (KeyPress::escapeKey); + addAndMakeVisible (cancelButton); + cancelButton.addShortcut (KeyPress (KeyPress::escapeKey)); - addChildComponent (&newFolderButton); + addChildComponent (newFolderButton); setInterceptsMouseClicks (false, true); } - void paint (Graphics& g) + void paint (Graphics& g) override { - g.setColour (getLookAndFeel().findColour (FileChooserDialogBox::titleTextColourId)); - text.draw (g); + text.draw (g, getLocalBounds().reduced (6) + .removeFromTop ((int) text.getHeight()).toFloat()); } - void resized() + void resized() override { const int buttonHeight = 26; Rectangle area (getLocalBounds()); - getLookAndFeel().createFileChooserHeaderText (getName(), instructions, text, getWidth()); - const Rectangle bb (text.getBoundingBox (0, text.getNumGlyphs(), false)); - area.removeFromTop (roundToInt (bb.getBottom()) + 10); + text.createLayout (getLookAndFeel().createFileChooserHeaderText (getName(), instructions), + getWidth() - 12.0f); + + area.removeFromTop (roundToInt (text.getHeight()) + 10); chooserComponent.setBounds (area.removeFromTop (area.getHeight() - buttonHeight - 20)); Rectangle buttonArea (area.reduced (16, 10)); @@ -84,7 +83,7 @@ public: private: String instructions; - GlyphArrangement text; + TextLayout text; }; //============================================================================== @@ -92,7 +91,7 @@ FileChooserDialogBox::FileChooserDialogBox (const String& name, const String& instructions, FileBrowserComponent& chooserComponent, const bool warnAboutOverwritingExistingFiles_, - const Colour& backgroundColour) + Colour backgroundColour) : ResizableWindow (name, backgroundColour, true), warnAboutOverwritingExistingFiles (warnAboutOverwritingExistingFiles_) { @@ -124,18 +123,8 @@ bool FileChooserDialogBox::show (int w, int h) bool FileChooserDialogBox::showAt (int x, int y, int w, int h) { - if (w <= 0) - { - Component* const previewComp = content->chooserComponent.getPreviewComponent(); - - if (previewComp != nullptr) - w = 400 + previewComp->getWidth(); - else - w = 600; - } - - if (h <= 0) - h = 500; + if (w <= 0) w = getDefaultWidth(); + if (h <= 0) h = 500; if (x < 0 || y < 0) centreWithSize (w, h); @@ -150,11 +139,15 @@ bool FileChooserDialogBox::showAt (int x, int y, int w, int h) void FileChooserDialogBox::centreWithDefaultSize (Component* componentToCentreAround) { - Component* const previewComp = content->chooserComponent.getPreviewComponent(); + centreAroundComponent (componentToCentreAround, getDefaultWidth(), 500); +} - centreAroundComponent (componentToCentreAround, - previewComp != nullptr ? 400 + previewComp->getWidth() : 600, - 500); +int FileChooserDialogBox::getDefaultWidth() const +{ + if (Component* const previewComp = content->chooserComponent.getPreviewComponent()) + return 400 + previewComp->getWidth(); + + return 600; } //============================================================================== @@ -209,12 +202,13 @@ void FileChooserDialogBox::okButtonPressed() && content->chooserComponent.getSelectedFile(0).exists()) { AlertWindow::showOkCancelBox (AlertWindow::WarningIcon, - TRANS("File already exists"), - TRANS("There's already a file called:") - + "\n\n" + content->chooserComponent.getSelectedFile(0).getFullPathName() - + "\n\n" + TRANS("Are you sure you want to overwrite it?"), - TRANS("overwrite"), - TRANS("cancel"), + TRANS("File already exists"), + TRANS("There's already a file called: FLNM") + .replace ("FLNM", content->chooserComponent.getSelectedFile(0).getFullPathName()) + + "\n\n" + + TRANS("Are you sure you want to overwrite it?"), + TRANS("Overwrite"), + TRANS("Cancel"), this, ModalCallbackFunction::forComponent (okToOverwriteFileCallback, this)); } @@ -230,7 +224,7 @@ void FileChooserDialogBox::createNewFolderCallback (int result, FileChooserDialo if (result != 0 && alert != nullptr && box != nullptr) { alert->setVisible (false); - box->createNewFolderConfirmed (alert->getTextEditorContents ("name")); + box->createNewFolderConfirmed (alert->getTextEditorContents ("Folder Name")); } } @@ -244,9 +238,9 @@ void FileChooserDialogBox::createNewFolder() TRANS("Please enter the name for the folder"), AlertWindow::NoIcon, this); - aw->addTextEditor ("name", String::empty, String::empty, false); - aw->addButton (TRANS("ok"), 1, KeyPress::returnKey); - aw->addButton (TRANS("cancel"), KeyPress::escapeKey); + aw->addTextEditor ("Folder Name", String::empty, String::empty, false); + aw->addButton (TRANS("Create Folder"), 1, KeyPress (KeyPress::returnKey)); + aw->addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); aw->enterModalState (true, ModalCallbackFunction::forComponent (createNewFolderCallback, this, diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h index 4a3bba7..f63632e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.h @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ -#define __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ - -#include "juce_FileBrowserComponent.h" -#include "../windows/juce_ResizableWindow.h" -#include "../buttons/juce_TextButton.h" -#include "../windows/juce_AlertWindow.h" +#ifndef JUCE_FILECHOOSERDIALOGBOX_H_INCLUDED +#define JUCE_FILECHOOSERDIALOGBOX_H_INCLUDED //============================================================================== @@ -68,8 +62,8 @@ @see FileChooser */ class JUCE_API FileChooserDialogBox : public ResizableWindow, - public ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug) - public FileBrowserListener + private ButtonListener, // (can't use Button::Listener due to idiotic VC2005 bug) + private FileBrowserListener { public: //============================================================================== @@ -92,7 +86,7 @@ public: const String& instructions, FileBrowserComponent& browserComponent, bool warnAboutOverwritingExistingFiles, - const Colour& backgroundColour); + Colour backgroundColour); /** Destructor. */ ~FileChooserDialogBox(); @@ -121,7 +115,7 @@ public: /** Sets the size of this dialog box to its default and positions it either in the centre of the screen, or centred around a component that is provided. */ - void centreWithDefaultSize (Component* componentToCentreAround = 0); + void centreWithDefaultSize (Component* componentToCentreAround = nullptr); //============================================================================== /** A set of colour IDs to use to change the colour of various aspects of the box. @@ -136,25 +130,19 @@ public: titleTextColourId = 0x1000850, /**< The colour to use to draw the box's title. */ }; - //============================================================================== - /** @internal */ - void buttonClicked (Button*); - /** @internal */ - void closeButtonPressed(); - /** @internal */ - void selectionChanged(); - /** @internal */ - void fileClicked (const File&, const MouseEvent&); - /** @internal */ - void fileDoubleClicked (const File&); - /** @internal */ - void browserRootChanged (const File&); - private: class ContentComponent; ContentComponent* content; const bool warnAboutOverwritingExistingFiles; + void buttonClicked (Button*) override; + void closeButtonPressed(); + void selectionChanged() override; + void fileClicked (const File&, const MouseEvent&) override; + void fileDoubleClicked (const File&) override; + void browserRootChanged (const File&) override; + int getDefaultWidth() const; + void okButtonPressed(); void createNewFolder(); void createNewFolderConfirmed (const String& name); @@ -162,8 +150,8 @@ private: static void okToOverwriteFileCallback (int result, FileChooserDialogBox*); static void createNewFolderCallback (int result, FileChooserDialogBox*, Component::SafePointer); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileChooserDialogBox) }; -#endif // __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ +#endif // JUCE_FILECHOOSERDIALOGBOX_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileFilter.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileFilter.cpp deleted file mode 100644 index 25a5486..0000000 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileFilter.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. - - JUCE is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - ------------------------------------------------------------------------------ - - To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. - - ============================================================================== -*/ - -FileFilter::FileFilter (const String& filterDescription) - : description (filterDescription) -{ -} - -FileFilter::~FileFilter() -{ -} - -const String& FileFilter::getDescription() const noexcept -{ - return description; -} diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp index 70caa16..3125267 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -87,23 +86,23 @@ void FileListComponent::changeListenerCallback (ChangeBroadcaster*) } //============================================================================== -class FileListItemComponent : public Component, - public TimeSliceClient, - public AsyncUpdater +class FileListComponent::ItemComponent : public Component, + private TimeSliceClient, + private AsyncUpdater { public: - FileListItemComponent (FileListComponent& owner_, TimeSliceThread& thread_) - : owner (owner_), thread (thread_), index (0), highlighted (false) + ItemComponent (FileListComponent& fc, TimeSliceThread& t) + : owner (fc), thread (t), index (0), highlighted (false) { } - ~FileListItemComponent() + ~ItemComponent() { thread.removeTimeSliceClient (this); } //============================================================================== - void paint (Graphics& g) + void paint (Graphics& g) override { getLookAndFeel().drawFileBrowserRow (g, getWidth(), getHeight(), file.getFileName(), @@ -112,13 +111,13 @@ public: index, owner); } - void mouseDown (const MouseEvent& e) + void mouseDown (const MouseEvent& e) override { - owner.selectRowsBasedOnModifierKeys (index, e.mods, false); + owner.selectRowsBasedOnModifierKeys (index, e.mods, true); owner.sendMouseClickMessage (file, e); } - void mouseDoubleClick (const MouseEvent&) + void mouseDoubleClick (const MouseEvent&) override { owner.sendDoubleClickMessage (file); } @@ -169,13 +168,13 @@ public: } } - int useTimeSlice() + int useTimeSlice() override { updateIcon (false); return -1; } - void handleAsyncUpdate() + void handleAsyncUpdate() override { repaint(); } @@ -213,7 +212,7 @@ private: } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListItemComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ItemComponent) }; //============================================================================== @@ -228,12 +227,12 @@ void FileListComponent::paintListBoxItem (int, Graphics&, int, int, bool) Component* FileListComponent::refreshComponentForRow (int row, bool isSelected, Component* existingComponentToUpdate) { - jassert (existingComponentToUpdate == nullptr || dynamic_cast (existingComponentToUpdate) != nullptr); + jassert (existingComponentToUpdate == nullptr || dynamic_cast (existingComponentToUpdate) != nullptr); - FileListItemComponent* comp = static_cast (existingComponentToUpdate); + ItemComponent* comp = static_cast (existingComponentToUpdate); if (comp == nullptr) - comp = new FileListItemComponent (*this, fileList.getTimeSliceThread()); + comp = new ItemComponent (*this, fileList.getTimeSliceThread()); DirectoryContentsList::FileInfo fileInfo; comp->update (fileList.getDirectory(), diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h index 9d58b7c..7f074d2 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileListComponent.h @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__ -#define __JUCE_FILELISTCOMPONENT_JUCEHEADER__ - -#include "juce_DirectoryContentsDisplayComponent.h" -#include "juce_FileBrowserListener.h" -#include "../widgets/juce_ListBox.h" -#include "../widgets/juce_TreeView.h" +#ifndef JUCE_FILELISTCOMPONENT_H_INCLUDED +#define JUCE_FILELISTCOMPONENT_H_INCLUDED //============================================================================== @@ -80,28 +74,23 @@ public: (and if the file isn't in the list, all other items will be deselected). */ void setSelectedFile (const File&); - //============================================================================== - /** @internal */ - void changeListenerCallback (ChangeBroadcaster*); - /** @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); - private: //============================================================================== File lastDirectory; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListComponent); + class ItemComponent; + + void changeListenerCallback (ChangeBroadcaster*) override; + + int getNumRows() override; + void paintListBoxItem (int, Graphics&, int, int, bool) override; + Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate) override; + void selectedRowsChanged (int lastRowSelected) override; + void deleteKeyPressed (int currentSelectedRow) override; + void returnKeyPressed (int currentSelectedRow) override; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListComponent) }; -#endif // __JUCE_FILELISTCOMPONENT_JUCEHEADER__ +#endif // JUCE_FILELISTCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilePreviewComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilePreviewComponent.h index 1747f84..727f420 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilePreviewComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilePreviewComponent.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ -#define __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ - -#include "../components/juce_Component.h" +#ifndef JUCE_FILEPREVIEWCOMPONENT_H_INCLUDED +#define JUCE_FILEPREVIEWCOMPONENT_H_INCLUDED //============================================================================== @@ -61,8 +58,8 @@ public: private: //============================================================================== - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePreviewComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePreviewComponent) }; -#endif // __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ +#endif // JUCE_FILEPREVIEWCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp index 2232c3f..a2cad46 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -31,23 +30,23 @@ FileSearchPathListComponent::FileSearchPathListComponent() downButton (String::empty, DrawableButton::ImageOnButtonBackground) { listBox.setModel (this); - addAndMakeVisible (&listBox); + addAndMakeVisible (listBox); listBox.setColour (ListBox::backgroundColourId, Colours::black.withAlpha (0.02f)); listBox.setColour (ListBox::outlineColourId, Colours::black.withAlpha (0.1f)); listBox.setOutlineThickness (1); - addAndMakeVisible (&addButton); + addAndMakeVisible (addButton); addButton.addListener (this); addButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop); - addAndMakeVisible (&removeButton); + addAndMakeVisible (removeButton); removeButton.addListener (this); removeButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop); - addAndMakeVisible (&changeButton); + addAndMakeVisible (changeButton); changeButton.addListener (this); - addAndMakeVisible (&upButton); + addAndMakeVisible (upButton); upButton.addListener (this); { @@ -60,7 +59,7 @@ FileSearchPathListComponent::FileSearchPathListComponent() upButton.setImages (&arrowImage); } - addAndMakeVisible (&downButton); + addAndMakeVisible (downButton); downButton.addListener (this); { diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h index efdf6ad..25ffef7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.h @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ -#define __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ - -#include "../widgets/juce_ListBox.h" -#include "../buttons/juce_DrawableButton.h" -#include "../buttons/juce_TextButton.h" -#include "../mouse/juce_FileDragAndDropTarget.h" +#ifndef JUCE_FILESEARCHPATHLISTCOMPONENT_H_INCLUDED +#define JUCE_FILESEARCHPATHLISTCOMPONENT_H_INCLUDED //============================================================================== @@ -81,28 +75,27 @@ public: //============================================================================== /** @internal */ - int getNumRows(); + int getNumRows() override; /** @internal */ - void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected); + void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) override; /** @internal */ - void deleteKeyPressed (int lastRowSelected); + void deleteKeyPressed (int lastRowSelected) override; /** @internal */ - void returnKeyPressed (int lastRowSelected); + void returnKeyPressed (int lastRowSelected) override; /** @internal */ - void listBoxItemDoubleClicked (int row, const MouseEvent&); + void listBoxItemDoubleClicked (int row, const MouseEvent&) override; /** @internal */ - void selectedRowsChanged (int lastRowSelected); + void selectedRowsChanged (int lastRowSelected) override; /** @internal */ - void resized(); + void resized() override; /** @internal */ - void paint (Graphics& g); + void paint (Graphics&) override; /** @internal */ - bool isInterestedInFileDrag (const StringArray& files); + bool isInterestedInFileDrag (const StringArray&) override; /** @internal */ - void filesDropped (const StringArray& files, int, int); + void filesDropped (const StringArray& files, int, int) override; /** @internal */ - void buttonClicked (Button* button); - + void buttonClicked (Button*) override; private: //============================================================================== @@ -116,8 +109,8 @@ private: void changed(); void updateButtons(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileSearchPathListComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileSearchPathListComponent) }; -#endif // __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ +#endif // JUCE_FILESEARCHPATHLISTCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp index 9323e90..45963e8 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.cpp @@ -1,53 +1,52 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -Image juce_createIconForFile (const File& file); +Image juce_createIconForFile (const File&); //============================================================================== class FileListTreeItem : public TreeViewItem, - public TimeSliceClient, - public AsyncUpdater, - public ChangeListener + private TimeSliceClient, + private AsyncUpdater, + private ChangeListener { public: - FileListTreeItem (FileTreeComponent& owner_, - DirectoryContentsList* const parentContentsList_, - const int indexInContentsList_, - const File& file_, - TimeSliceThread& thread_) - : file (file_), - owner (owner_), - parentContentsList (parentContentsList_), - indexInContentsList (indexInContentsList_), + FileListTreeItem (FileTreeComponent& treeComp, + DirectoryContentsList* const parentContents, + const int indexInContents, + const File& f, + TimeSliceThread& t) + : file (f), + owner (treeComp), + parentContentsList (parentContents), + indexInContentsList (indexInContents), subContentsList (nullptr, false), - thread (thread_) + thread (t) { DirectoryContentsList::FileInfo fileInfo; - if (parentContentsList_ != nullptr - && parentContentsList_->getFileInfo (indexInContentsList_, fileInfo)) + if (parentContents != nullptr + && parentContents->getFileInfo (indexInContents, fileInfo)) { fileSize = File::descriptionOfSizeInBytes (fileInfo.fileSize); modTime = fileInfo.modificationTime.formatted ("%d %b '%y %H:%M"); @@ -63,16 +62,17 @@ public: { thread.removeTimeSliceClient (this); clearSubItems(); + removeSubContentsList(); } //============================================================================== - bool mightContainSubItems() { return isDirectory; } - String getUniqueName() const { return file.getFullPathName(); } - int getItemHeight() const { return 22; } + bool mightContainSubItems() override { return isDirectory; } + String getUniqueName() const override { return file.getFullPathName(); } + int getItemHeight() const override { return owner.getItemHeight(); } - var getDragSourceDescription() { return owner.getDragAndDropDescription(); } + var getDragSourceDescription() override { return owner.getDragAndDropDescription(); } - void itemOpennessChanged (bool isNowOpen) + void itemOpennessChanged (bool isNowOpen) override { if (isNowOpen) { @@ -87,7 +87,10 @@ public: jassert (parentContentsList != nullptr); DirectoryContentsList* const l = new DirectoryContentsList (parentContentsList->getFilter(), thread); - l->setDirectory (file, true, true); + + l->setDirectory (file, + parentContentsList->isFindingDirectories(), + parentContentsList->isFindingFiles()); setSubContentsList (l, true); } @@ -97,30 +100,77 @@ public: } } + void removeSubContentsList() + { + if (subContentsList != nullptr) + { + subContentsList->removeChangeListener (this); + subContentsList.clear(); + } + } + void setSubContentsList (DirectoryContentsList* newList, const bool canDeleteList) { + removeSubContentsList(); + OptionalScopedPointer newPointer (newList, canDeleteList); subContentsList = newPointer; newList->addChangeListener (this); } - void changeListenerCallback (ChangeBroadcaster*) + bool selectFile (const File& target) + { + if (file == target) + { + setSelected (true, true); + return true; + } + + if (target.isAChildOf (file)) + { + setOpen (true); + + for (int maxRetries = 500; --maxRetries > 0;) + { + for (int i = 0; i < getNumSubItems(); ++i) + if (FileListTreeItem* f = dynamic_cast (getSubItem (i))) + if (f->selectFile (target)) + return true; + + // if we've just opened and the contents are still loading, wait for it.. + if (subContentsList != nullptr && subContentsList->isStillLoading()) + { + Thread::sleep (10); + rebuildItemsFromContentList(); + } + else + { + break; + } + } + } + + return false; + } + + void changeListenerCallback (ChangeBroadcaster*) override + { + rebuildItemsFromContentList(); + } + + void rebuildItemsFromContentList() { clearSubItems(); if (isOpen() && subContentsList != nullptr) { for (int i = 0; i < subContentsList->getNumFiles(); ++i) - { - FileListTreeItem* const item - = new FileListTreeItem (owner, subContentsList, i, subContentsList->getFile(i), thread); - - addSubItem (item); - } + addSubItem (new FileListTreeItem (owner, subContentsList, i, + subContentsList->getFile(i), thread)); } } - void paintItem (Graphics& g, int width, int height) + void paintItem (Graphics& g, int width, int height) override { if (file != File::nonexistent) { @@ -137,30 +187,30 @@ public: indexInContentsList, owner); } - void itemClicked (const MouseEvent& e) + void itemClicked (const MouseEvent& e) override { owner.sendMouseClickMessage (file, e); } - void itemDoubleClicked (const MouseEvent& e) + void itemDoubleClicked (const MouseEvent& e) override { TreeViewItem::itemDoubleClicked (e); owner.sendDoubleClickMessage (file); } - void itemSelectionChanged (bool) + void itemSelectionChanged (bool) override { owner.sendSelectionChangeMessage(); } - int useTimeSlice() + int useTimeSlice() override { updateIcon (false); return -1; } - void handleAsyncUpdate() + void handleAsyncUpdate() override { owner.repaint(); } @@ -200,12 +250,13 @@ private: } } - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileListTreeItem) }; //============================================================================== FileTreeComponent::FileTreeComponent (DirectoryContentsList& listToShow) - : DirectoryContentsDisplayComponent (listToShow) + : DirectoryContentsDisplayComponent (listToShow), + itemHeight (22) { setRootItemVisible (false); refresh(); @@ -231,10 +282,10 @@ void FileTreeComponent::refresh() //============================================================================== File FileTreeComponent::getSelectedFile (const int index) const { - const FileListTreeItem* const item = dynamic_cast (getSelectedItem (index)); + if (const FileListTreeItem* const item = dynamic_cast (getSelectedItem (index))) + return item->file; - return item != nullptr ? item->file - : File::nonexistent; + return File::nonexistent; } void FileTreeComponent::deselectAllFiles() @@ -254,16 +305,18 @@ void FileTreeComponent::setDragAndDropDescription (const String& description) void FileTreeComponent::setSelectedFile (const File& target) { - for (int i = getNumSelectedItems(); --i >= 0;) - { - FileListTreeItem* t = dynamic_cast (getSelectedItem (i)); - - if (t != nullptr && t->file == target) - { - t->setSelected (true, true); - return; - } - } - - clearSelectedItems(); + if (FileListTreeItem* t = dynamic_cast (getRootItem())) + if (! t->selectFile (target)) + clearSelectedItems(); +} + +void FileTreeComponent::setItemHeight (int newHeight) +{ + if (itemHeight != newHeight) + { + itemHeight = newHeight; + + if (TreeViewItem* root = getRootItem()) + root->treeHasChanged(); + } } diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h index 3161884..cf59151 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileTreeComponent.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ -#define __JUCE_FILETREECOMPONENT_JUCEHEADER__ - -#include "juce_DirectoryContentsDisplayComponent.h" -#include "../widgets/juce_TreeView.h" +#ifndef JUCE_FILETREECOMPONENT_H_INCLUDED +#define JUCE_FILETREECOMPONENT_H_INCLUDED //============================================================================== @@ -88,14 +84,21 @@ public: /** Returns the last value that was set by setDragAndDropDescription(). */ - const String& getDragAndDropDescription() const noexcept { return dragAndDropDescription; } + const String& getDragAndDropDescription() const noexcept { return dragAndDropDescription; } + + /** Changes the height of the treeview items. */ + void setItemHeight (int newHeight); + + /** Returns the height of the treeview items. */ + int getItemHeight() const noexcept { return itemHeight; } private: //============================================================================== String dragAndDropDescription; + int itemHeight; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileTreeComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileTreeComponent) }; -#endif // __JUCE_FILETREECOMPONENT_JUCEHEADER__ +#endif // JUCE_FILETREECOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp index b72f498..4e5036e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -29,7 +28,7 @@ FilenameComponent::FilenameComponent (const String& name, const bool isDirectory, const bool isForSaving, const String& fileBrowserWildcard, - const String& enforcedSuffix_, + const String& suffix, const String& textWhenNothingSelected) : Component (name), maxRecentFiles (30), @@ -37,9 +36,9 @@ FilenameComponent::FilenameComponent (const String& name, isSaving (isForSaving), isFileDragOver (false), wildcard (fileBrowserWildcard), - enforcedSuffix (enforcedSuffix_) + enforcedSuffix (suffix) { - addAndMakeVisible (&filenameBox); + addAndMakeVisible (filenameBox); filenameBox.setEditableText (canEditFilename); filenameBox.addListener (this); filenameBox.setTextWhenNothingSelected (textWhenNothingSelected); @@ -47,7 +46,7 @@ FilenameComponent::FilenameComponent (const String& name, setBrowseButtonText ("..."); - setCurrentFile (currentFile, true); + setCurrentFile (currentFile, true, dontSendNotification); } FilenameComponent::~FilenameComponent() @@ -60,7 +59,7 @@ void FilenameComponent::paintOverChildren (Graphics& g) if (isFileDragOver) { g.setColour (Colours::red.withAlpha (0.2f)); - g.drawRect (0, 0, getWidth(), getHeight(), 3); + g.drawRect (getLocalBounds(), 3); } } @@ -69,6 +68,13 @@ void FilenameComponent::resized() getLookAndFeel().layoutFilenameComponent (*this, &filenameBox, browseButton); } +KeyboardFocusTraverser* FilenameComponent::createFocusTraverser() +{ + // This prevents the sub-components from grabbing focus if the + // FilenameComponent has been set to refuse focus. + return getWantsKeyboardFocus() ? Component::createFocusTraverser() : nullptr; +} + void FilenameComponent::setBrowseButtonText (const String& newBrowseButtonText) { browseButtonText = newBrowseButtonText; @@ -97,12 +103,18 @@ void FilenameComponent::setDefaultBrowseTarget (const File& newDefaultDirectory) defaultBrowseFile = newDefaultDirectory; } +File FilenameComponent::getLocationToBrowse() +{ + return getCurrentFile() == File::nonexistent ? defaultBrowseFile + : getCurrentFile(); +} + void FilenameComponent::buttonClicked (Button*) { #if JUCE_MODAL_LOOPS_PERMITTED - FileChooser fc (TRANS("Choose a new file"), - getCurrentFile() == File::nonexistent ? defaultBrowseFile - : getCurrentFile(), + FileChooser fc (isDir ? TRANS ("Choose a new directory") + : TRANS ("Choose a new file"), + getLocationToBrowse(), wildcard); if (isDir ? fc.browseForDirectory() @@ -152,7 +164,7 @@ void FilenameComponent::fileDragExit (const StringArray&) //============================================================================== File FilenameComponent::getCurrentFile() const { - File f (filenameBox.getText()); + File f (File::getCurrentWorkingDirectory().getChildFile (filenameBox.getText())); if (enforcedSuffix.isNotEmpty()) f = f.withFileExtension (enforcedSuffix); @@ -162,7 +174,7 @@ File FilenameComponent::getCurrentFile() const void FilenameComponent::setCurrentFile (File newFile, const bool addToRecentlyUsedList, - const bool sendChangeNotification) + NotificationType notification) { if (enforcedSuffix.isNotEmpty()) newFile = newFile.withFileExtension (enforcedSuffix); @@ -174,10 +186,15 @@ void FilenameComponent::setCurrentFile (File newFile, if (addToRecentlyUsedList) addRecentlyUsedFile (newFile); - filenameBox.setText (lastFilename, true); + filenameBox.setText (lastFilename, dontSendNotification); - if (sendChangeNotification) + if (notification != dontSendNotification) + { triggerAsyncUpdate(); + + if (notification == sendNotificationSync) + handleUpdateNowIfNeeded(); + } } } diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h index 7032ae4..bbae76e 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h @@ -1,35 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__ -#define __JUCE_FILENAMECOMPONENT_JUCEHEADER__ - -#include "../widgets/juce_ComboBox.h" -#include "../buttons/juce_TextButton.h" -#include "../mouse/juce_FileDragAndDropTarget.h" -class FilenameComponent; +#ifndef JUCE_FILENAMECOMPONENT_H_INCLUDED +#define JUCE_FILENAMECOMPONENT_H_INCLUDED //============================================================================== @@ -111,15 +105,14 @@ public: /** 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. + @param newFile the new filename to use + @param addToRecentlyUsedList if true, the filename will also be added to the + drop-down list of recent files. + @param notification whether to send a notification of the change to listeners */ void setCurrentFile (File newFile, bool addToRecentlyUsedList, - bool sendChangeNotification = true); + NotificationType notification = sendNotificationAsync); /** Changes whether the use can type into the filename box. */ @@ -131,6 +124,13 @@ public: */ void setDefaultBrowseTarget (const File& newDefaultDirectory); + /** This can be overridden to return a custom location that you want the dialog box + to show when the browse button is pushed. + The default implementation of this method will return either the current file + (if one has been chosen) or the location that was set by setDefaultBrowseTarget(). + */ + virtual File getLocationToBrowse(); + /** Returns all the entries on the recent files list. This can be used in conjunction with setRecentlyUsedFilenames() for saving the @@ -178,23 +178,35 @@ public: void removeListener (FilenameComponentListener* listener); /** Gives the component a tooltip. */ - void setTooltip (const String& newTooltip); + void setTooltip (const String& newTooltip) override; + + //============================================================================== + /** This abstract base class is implemented by LookAndFeel classes. */ + struct JUCE_API LookAndFeelMethods + { + virtual ~LookAndFeelMethods() {} + + virtual Button* createFilenameComponentBrowseButton (const String& text) = 0; + virtual void layoutFilenameComponent (FilenameComponent&, ComboBox* filenameBox, Button* browseButton) = 0; + }; //============================================================================== /** @internal */ - void paintOverChildren (Graphics& g); + void paintOverChildren (Graphics&) override; /** @internal */ - void resized(); + void resized() override; /** @internal */ - void lookAndFeelChanged(); + void lookAndFeelChanged() override; /** @internal */ - bool isInterestedInFileDrag (const StringArray& files); + bool isInterestedInFileDrag (const StringArray&) override; /** @internal */ - void filesDropped (const StringArray& files, int, int); + void filesDropped (const StringArray&, int, int) override; /** @internal */ - void fileDragEnter (const StringArray& files, int, int); + void fileDragEnter (const StringArray&, int, int) override; /** @internal */ - void fileDragExit (const StringArray& files); + void fileDragExit (const StringArray&) override; + /** @internal */ + KeyboardFocusTraverser* createFocusTraverser() override; private: //============================================================================== @@ -207,13 +219,13 @@ private: ListenerList listeners; File defaultBrowseFile; - void comboBoxChanged (ComboBox*); - void buttonClicked (Button* button); - void handleAsyncUpdate(); + void comboBoxChanged (ComboBox*) override; + void buttonClicked (Button*) override; + void handleAsyncUpdate() override; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilenameComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilenameComponent) }; -#endif // __JUCE_FILENAMECOMPONENT_JUCEHEADER__ +#endif // JUCE_FILENAMECOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp index 4a664d6..537aae3 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -59,16 +58,14 @@ void ImagePreviewComponent::timerCallback() stopTimer(); currentThumbnail = Image::null; - currentDetails = String::empty; + currentDetails.clear(); repaint(); - ScopedPointer in (fileToLoad.createInputStream()); + ScopedPointer in (fileToLoad.createInputStream()); if (in != nullptr) { - ImageFileFormat* const format = ImageFileFormat::findImageFormatForStream (*in); - - if (format != nullptr) + if (ImageFileFormat* const format = ImageFileFormat::findImageFormatForStream (*in)) { currentThumbnail = format->decodeImage (*in); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h index dbeb9af..6887a78 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ -#define __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ - -#include "juce_FilePreviewComponent.h" +#ifndef JUCE_IMAGEPREVIEWCOMPONENT_H_INCLUDED +#define JUCE_IMAGEPREVIEWCOMPONENT_H_INCLUDED //============================================================================== @@ -49,11 +46,11 @@ public: //============================================================================== /** @internal */ - void selectedFileChanged (const File& newSelectedFile); + void selectedFileChanged (const File& newSelectedFile) override; /** @internal */ - void paint (Graphics& g); + void paint (Graphics&) override; /** @internal */ - void timerCallback(); + void timerCallback() override; private: File fileToLoad; @@ -62,8 +59,8 @@ private: void getThumbSize (int& w, int& h) const; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePreviewComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ImagePreviewComponent) }; -#endif // __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ +#endif // JUCE_IMAGEPREVIEWCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp index 4945104..71f0554 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.cpp @@ -1,29 +1,28 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#if defined (__JUCE_GUI_BASICS_JUCEHEADER__) && ! JUCE_AMALGAMATED_INCLUDE +#if defined (JUCE_GUI_BASICS_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE /* When you add this cpp file to your project, you mustn't include it in a file where you've already included any other headers - just put it inside a file on its own, possibly with your config flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix @@ -36,6 +35,8 @@ // and your header search path must make it accessible to the module's files. #include "AppConfig.h" +#define NS_FORMAT_FUNCTION(F,A) // To avoid spurious warnings from GCC + #include "../juce_core/native/juce_BasicNativeHeaders.h" #include "juce_gui_basics.h" @@ -46,11 +47,15 @@ //============================================================================== #if JUCE_MAC #import - #define Point CarbonDummyPointName - #define Component CarbonDummyCompName - #import // still needed for SetSystemUIMode() - #undef Point - #undef Component + #import + + #if JUCE_SUPPORT_CARBON + #define Point CarbonDummyPointName + #define Component CarbonDummyCompName + #import // still needed for SetSystemUIMode() + #undef Point + #undef Component + #endif //============================================================================== #elif JUCE_WINDOWS @@ -59,7 +64,7 @@ #include #if JUCE_WEB_BROWSER - #include + #include #include #endif @@ -85,7 +90,7 @@ #endif #if JUCE_MINGW - #include + #include #endif //============================================================================== @@ -96,6 +101,7 @@ #include #include #include + #include #include #include @@ -129,19 +135,18 @@ namespace juce { -// START_AUTOINCLUDE components/*.cpp, mouse/*.cpp, keyboard/*.cpp, buttons/*.cpp, -// drawables/*.cpp, filebrowser/*.cpp, layout/*.cpp, lookandfeel/*.cpp, -// menus/*.cpp, positioning/*.cpp, properties/*.cpp, widgets/*.cpp, -// windows/*.cpp, commands/*.cpp, application/*.cpp, misc/*.cpp +extern bool juce_areThereAnyAlwaysOnTopWindows(); + #include "components/juce_Component.cpp" #include "components/juce_ComponentListener.cpp" +#include "mouse/juce_MouseInputSource.cpp" #include "components/juce_Desktop.cpp" #include "components/juce_ModalComponentManager.cpp" #include "mouse/juce_ComponentDragger.cpp" #include "mouse/juce_DragAndDropContainer.cpp" #include "mouse/juce_MouseCursor.cpp" #include "mouse/juce_MouseEvent.cpp" -#include "mouse/juce_MouseInputSource.cpp" +#include "mouse/juce_MouseInactivityDetector.cpp" #include "mouse/juce_MouseListener.cpp" #include "keyboard/juce_CaretComponent.cpp" #include "keyboard/juce_KeyboardFocusTraverser.cpp" @@ -170,17 +175,16 @@ namespace juce #include "filebrowser/juce_FileBrowserComponent.cpp" #include "filebrowser/juce_FileChooser.cpp" #include "filebrowser/juce_FileChooserDialogBox.cpp" -#include "filebrowser/juce_FileFilter.cpp" #include "filebrowser/juce_FileListComponent.cpp" #include "filebrowser/juce_FilenameComponent.cpp" #include "filebrowser/juce_FileSearchPathListComponent.cpp" #include "filebrowser/juce_FileTreeComponent.cpp" #include "filebrowser/juce_ImagePreviewComponent.cpp" -#include "filebrowser/juce_WildcardFileFilter.cpp" #include "layout/juce_ComponentAnimator.cpp" #include "layout/juce_ComponentBoundsConstrainer.cpp" #include "layout/juce_ComponentBuilder.cpp" #include "layout/juce_ComponentMovementWatcher.cpp" +#include "layout/juce_ConcertinaPanel.cpp" #include "layout/juce_GroupComponent.cpp" #include "layout/juce_MultiDocumentPanel.cpp" #include "layout/juce_ResizableBorderComponent.cpp" @@ -194,6 +198,9 @@ namespace juce #include "layout/juce_TabbedComponent.cpp" #include "layout/juce_Viewport.cpp" #include "lookandfeel/juce_LookAndFeel.cpp" +#include "lookandfeel/juce_LookAndFeel_V2.cpp" +#include "lookandfeel/juce_LookAndFeel_V1.cpp" +#include "lookandfeel/juce_LookAndFeel_V3.cpp" #include "menus/juce_MenuBarComponent.cpp" #include "menus/juce_MenuBarModel.cpp" #include "menus/juce_PopupMenu.cpp" @@ -220,8 +227,8 @@ namespace juce #include "widgets/juce_TableHeaderComponent.cpp" #include "widgets/juce_TableListBox.cpp" #include "widgets/juce_TextEditor.cpp" -#include "widgets/juce_Toolbar.cpp" #include "widgets/juce_ToolbarItemComponent.cpp" +#include "widgets/juce_Toolbar.cpp" #include "widgets/juce_ToolbarItemPalette.cpp" #include "widgets/juce_TreeView.cpp" #include "windows/juce_AlertWindow.cpp" @@ -240,22 +247,13 @@ namespace juce #include "application/juce_Application.cpp" #include "misc/juce_BubbleComponent.cpp" #include "misc/juce_DropShadower.cpp" -// END_AUTOINCLUDE -} - -using namespace juce; - -//============================================================================== -namespace juce -{ #if JUCE_IOS || JUCE_WINDOWS #include "native/juce_MultiTouchMapper.h" #endif #if JUCE_MAC || JUCE_IOS #include "../juce_core/native/juce_osx_ObjCHelpers.h" - #include "../juce_core/native/juce_mac_ObjCSuffix.h" #include "../juce_graphics/native/juce_mac_CoreGraphicsHelpers.h" #include "../juce_graphics/native/juce_mac_CoreGraphicsContext.h" diff --git a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h index 6b2bfd7..dedd3a0 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h +++ b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_GUI_BASICS_JUCEHEADER__ -#define __JUCE_GUI_BASICS_JUCEHEADER__ +#ifndef JUCE_GUI_BASICS_H_INCLUDED +#define JUCE_GUI_BASICS_H_INCLUDED #include "../juce_graphics/juce_graphics.h" #include "../juce_data_structures/juce_data_structures.h" @@ -73,374 +72,176 @@ namespace juce { -// START_AUTOINCLUDE components, mouse, keyboard, buttons, drawables, -// filebrowser, layout, lookandfeel, menus, positioning, properties, -// widgets, windows, commands, application, misc -#ifndef __JUCE_CACHEDCOMPONENTIMAGE_JUCEHEADER__ - #include "components/juce_CachedComponentImage.h" -#endif -#ifndef __JUCE_COMPONENT_JUCEHEADER__ - #include "components/juce_Component.h" -#endif -#ifndef __JUCE_COMPONENTLISTENER_JUCEHEADER__ - #include "components/juce_ComponentListener.h" -#endif -#ifndef __JUCE_DESKTOP_JUCEHEADER__ - #include "components/juce_Desktop.h" -#endif -#ifndef __JUCE_MODALCOMPONENTMANAGER_JUCEHEADER__ - #include "components/juce_ModalComponentManager.h" -#endif -#ifndef __JUCE_COMPONENTDRAGGER_JUCEHEADER__ - #include "mouse/juce_ComponentDragger.h" -#endif -#ifndef __JUCE_DRAGANDDROPCONTAINER_JUCEHEADER__ - #include "mouse/juce_DragAndDropContainer.h" -#endif -#ifndef __JUCE_DRAGANDDROPTARGET_JUCEHEADER__ - #include "mouse/juce_DragAndDropTarget.h" -#endif -#ifndef __JUCE_FILEDRAGANDDROPTARGET_JUCEHEADER__ - #include "mouse/juce_FileDragAndDropTarget.h" -#endif -#ifndef __JUCE_LASSOCOMPONENT_JUCEHEADER__ - #include "mouse/juce_LassoComponent.h" -#endif -#ifndef __JUCE_MOUSECURSOR_JUCEHEADER__ - #include "mouse/juce_MouseCursor.h" -#endif -#ifndef __JUCE_MOUSEEVENT_JUCEHEADER__ - #include "mouse/juce_MouseEvent.h" -#endif -#ifndef __JUCE_MOUSEINPUTSOURCE_JUCEHEADER__ - #include "mouse/juce_MouseInputSource.h" -#endif -#ifndef __JUCE_MOUSELISTENER_JUCEHEADER__ - #include "mouse/juce_MouseListener.h" -#endif -#ifndef __JUCE_SELECTEDITEMSET_JUCEHEADER__ - #include "mouse/juce_SelectedItemSet.h" -#endif -#ifndef __JUCE_TOOLTIPCLIENT_JUCEHEADER__ - #include "mouse/juce_TooltipClient.h" -#endif -#ifndef __JUCE_CARETCOMPONENT_JUCEHEADER__ - #include "keyboard/juce_CaretComponent.h" -#endif -#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ - #include "keyboard/juce_KeyboardFocusTraverser.h" -#endif -#ifndef __JUCE_KEYLISTENER_JUCEHEADER__ - #include "keyboard/juce_KeyListener.h" -#endif -#ifndef __JUCE_KEYPRESS_JUCEHEADER__ - #include "keyboard/juce_KeyPress.h" -#endif -#ifndef __JUCE_MODIFIERKEYS_JUCEHEADER__ - #include "keyboard/juce_ModifierKeys.h" -#endif -#ifndef __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ - #include "keyboard/juce_SystemClipboard.h" -#endif -#ifndef __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__ - #include "keyboard/juce_TextEditorKeyMapper.h" -#endif -#ifndef __JUCE_TEXTINPUTTARGET_JUCEHEADER__ - #include "keyboard/juce_TextInputTarget.h" -#endif -#ifndef __JUCE_ARROWBUTTON_JUCEHEADER__ - #include "buttons/juce_ArrowButton.h" -#endif -#ifndef __JUCE_BUTTON_JUCEHEADER__ - #include "buttons/juce_Button.h" -#endif -#ifndef __JUCE_DRAWABLEBUTTON_JUCEHEADER__ - #include "buttons/juce_DrawableButton.h" -#endif -#ifndef __JUCE_HYPERLINKBUTTON_JUCEHEADER__ - #include "buttons/juce_HyperlinkButton.h" -#endif -#ifndef __JUCE_IMAGEBUTTON_JUCEHEADER__ - #include "buttons/juce_ImageButton.h" -#endif -#ifndef __JUCE_SHAPEBUTTON_JUCEHEADER__ - #include "buttons/juce_ShapeButton.h" -#endif -#ifndef __JUCE_TEXTBUTTON_JUCEHEADER__ - #include "buttons/juce_TextButton.h" -#endif -#ifndef __JUCE_TOGGLEBUTTON_JUCEHEADER__ - #include "buttons/juce_ToggleButton.h" -#endif -#ifndef __JUCE_TOOLBARBUTTON_JUCEHEADER__ - #include "buttons/juce_ToolbarButton.h" -#endif -#ifndef __JUCE_DRAWABLE_JUCEHEADER__ - #include "drawables/juce_Drawable.h" -#endif -#ifndef __JUCE_DRAWABLECOMPOSITE_JUCEHEADER__ - #include "drawables/juce_DrawableComposite.h" -#endif -#ifndef __JUCE_DRAWABLEIMAGE_JUCEHEADER__ - #include "drawables/juce_DrawableImage.h" -#endif -#ifndef __JUCE_DRAWABLEPATH_JUCEHEADER__ - #include "drawables/juce_DrawablePath.h" -#endif -#ifndef __JUCE_DRAWABLERECTANGLE_JUCEHEADER__ - #include "drawables/juce_DrawableRectangle.h" -#endif -#ifndef __JUCE_DRAWABLESHAPE_JUCEHEADER__ - #include "drawables/juce_DrawableShape.h" -#endif -#ifndef __JUCE_DRAWABLETEXT_JUCEHEADER__ - #include "drawables/juce_DrawableText.h" -#endif -#ifndef __JUCE_DIRECTORYCONTENTSDISPLAYCOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_DirectoryContentsDisplayComponent.h" -#endif -#ifndef __JUCE_DIRECTORYCONTENTSLIST_JUCEHEADER__ - #include "filebrowser/juce_DirectoryContentsList.h" -#endif -#ifndef __JUCE_FILEBROWSERCOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_FileBrowserComponent.h" -#endif -#ifndef __JUCE_FILEBROWSERLISTENER_JUCEHEADER__ - #include "filebrowser/juce_FileBrowserListener.h" -#endif -#ifndef __JUCE_FILECHOOSER_JUCEHEADER__ - #include "filebrowser/juce_FileChooser.h" -#endif -#ifndef __JUCE_FILECHOOSERDIALOGBOX_JUCEHEADER__ - #include "filebrowser/juce_FileChooserDialogBox.h" -#endif -#ifndef __JUCE_FILEFILTER_JUCEHEADER__ - #include "filebrowser/juce_FileFilter.h" -#endif -#ifndef __JUCE_FILELISTCOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_FileListComponent.h" -#endif -#ifndef __JUCE_FILENAMECOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_FilenameComponent.h" -#endif -#ifndef __JUCE_FILEPREVIEWCOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_FilePreviewComponent.h" -#endif -#ifndef __JUCE_FILESEARCHPATHLISTCOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_FileSearchPathListComponent.h" -#endif -#ifndef __JUCE_FILETREECOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_FileTreeComponent.h" -#endif -#ifndef __JUCE_IMAGEPREVIEWCOMPONENT_JUCEHEADER__ - #include "filebrowser/juce_ImagePreviewComponent.h" -#endif -#ifndef __JUCE_WILDCARDFILEFILTER_JUCEHEADER__ - #include "filebrowser/juce_WildcardFileFilter.h" -#endif -#ifndef __JUCE_COMPONENTANIMATOR_JUCEHEADER__ - #include "layout/juce_ComponentAnimator.h" -#endif -#ifndef __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ - #include "layout/juce_ComponentBoundsConstrainer.h" -#endif -#ifndef __JUCE_COMPONENTBUILDER_JUCEHEADER__ - #include "layout/juce_ComponentBuilder.h" -#endif -#ifndef __JUCE_COMPONENTMOVEMENTWATCHER_JUCEHEADER__ - #include "layout/juce_ComponentMovementWatcher.h" -#endif -#ifndef __JUCE_GROUPCOMPONENT_JUCEHEADER__ - #include "layout/juce_GroupComponent.h" -#endif -#ifndef __JUCE_MULTIDOCUMENTPANEL_JUCEHEADER__ - #include "layout/juce_MultiDocumentPanel.h" -#endif -#ifndef __JUCE_RESIZABLEBORDERCOMPONENT_JUCEHEADER__ - #include "layout/juce_ResizableBorderComponent.h" -#endif -#ifndef __JUCE_RESIZABLECORNERCOMPONENT_JUCEHEADER__ - #include "layout/juce_ResizableCornerComponent.h" -#endif -#ifndef __JUCE_RESIZABLEEDGECOMPONENT_JUCEHEADER__ - #include "layout/juce_ResizableEdgeComponent.h" -#endif -#ifndef __JUCE_SCROLLBAR_JUCEHEADER__ - #include "layout/juce_ScrollBar.h" -#endif -#ifndef __JUCE_STRETCHABLELAYOUTMANAGER_JUCEHEADER__ - #include "layout/juce_StretchableLayoutManager.h" -#endif -#ifndef __JUCE_STRETCHABLELAYOUTRESIZERBAR_JUCEHEADER__ - #include "layout/juce_StretchableLayoutResizerBar.h" -#endif -#ifndef __JUCE_STRETCHABLEOBJECTRESIZER_JUCEHEADER__ - #include "layout/juce_StretchableObjectResizer.h" -#endif -#ifndef __JUCE_TABBEDBUTTONBAR_JUCEHEADER__ - #include "layout/juce_TabbedButtonBar.h" -#endif -#ifndef __JUCE_TABBEDCOMPONENT_JUCEHEADER__ - #include "layout/juce_TabbedComponent.h" -#endif -#ifndef __JUCE_VIEWPORT_JUCEHEADER__ - #include "layout/juce_Viewport.h" -#endif -#ifndef __JUCE_LOOKANDFEEL_JUCEHEADER__ - #include "lookandfeel/juce_LookAndFeel.h" -#endif -#ifndef __JUCE_MENUBARCOMPONENT_JUCEHEADER__ - #include "menus/juce_MenuBarComponent.h" -#endif -#ifndef __JUCE_MENUBARMODEL_JUCEHEADER__ - #include "menus/juce_MenuBarModel.h" -#endif -#ifndef __JUCE_POPUPMENU_JUCEHEADER__ - #include "menus/juce_PopupMenu.h" -#endif -#ifndef __JUCE_MARKERLIST_JUCEHEADER__ - #include "positioning/juce_MarkerList.h" -#endif -#ifndef __JUCE_RELATIVECOORDINATE_JUCEHEADER__ - #include "positioning/juce_RelativeCoordinate.h" -#endif -#ifndef __JUCE_RELATIVECOORDINATEPOSITIONER_JUCEHEADER__ - #include "positioning/juce_RelativeCoordinatePositioner.h" -#endif -#ifndef __JUCE_RELATIVEPARALLELOGRAM_JUCEHEADER__ - #include "positioning/juce_RelativeParallelogram.h" -#endif -#ifndef __JUCE_RELATIVEPOINT_JUCEHEADER__ - #include "positioning/juce_RelativePoint.h" -#endif -#ifndef __JUCE_RELATIVEPOINTPATH_JUCEHEADER__ - #include "positioning/juce_RelativePointPath.h" -#endif -#ifndef __JUCE_RELATIVERECTANGLE_JUCEHEADER__ - #include "positioning/juce_RelativeRectangle.h" -#endif -#ifndef __JUCE_BOOLEANPROPERTYCOMPONENT_JUCEHEADER__ - #include "properties/juce_BooleanPropertyComponent.h" -#endif -#ifndef __JUCE_BUTTONPROPERTYCOMPONENT_JUCEHEADER__ - #include "properties/juce_ButtonPropertyComponent.h" -#endif -#ifndef __JUCE_CHOICEPROPERTYCOMPONENT_JUCEHEADER__ - #include "properties/juce_ChoicePropertyComponent.h" -#endif -#ifndef __JUCE_PROPERTYCOMPONENT_JUCEHEADER__ - #include "properties/juce_PropertyComponent.h" -#endif -#ifndef __JUCE_PROPERTYPANEL_JUCEHEADER__ - #include "properties/juce_PropertyPanel.h" -#endif -#ifndef __JUCE_SLIDERPROPERTYCOMPONENT_JUCEHEADER__ - #include "properties/juce_SliderPropertyComponent.h" -#endif -#ifndef __JUCE_TEXTPROPERTYCOMPONENT_JUCEHEADER__ - #include "properties/juce_TextPropertyComponent.h" -#endif -#ifndef __JUCE_COMBOBOX_JUCEHEADER__ - #include "widgets/juce_ComboBox.h" -#endif -#ifndef __JUCE_IMAGECOMPONENT_JUCEHEADER__ - #include "widgets/juce_ImageComponent.h" -#endif -#ifndef __JUCE_LABEL_JUCEHEADER__ - #include "widgets/juce_Label.h" -#endif -#ifndef __JUCE_LISTBOX_JUCEHEADER__ - #include "widgets/juce_ListBox.h" -#endif -#ifndef __JUCE_PROGRESSBAR_JUCEHEADER__ - #include "widgets/juce_ProgressBar.h" -#endif -#ifndef __JUCE_SLIDER_JUCEHEADER__ - #include "widgets/juce_Slider.h" -#endif -#ifndef __JUCE_TABLEHEADERCOMPONENT_JUCEHEADER__ - #include "widgets/juce_TableHeaderComponent.h" -#endif -#ifndef __JUCE_TABLELISTBOX_JUCEHEADER__ - #include "widgets/juce_TableListBox.h" -#endif -#ifndef __JUCE_TEXTEDITOR_JUCEHEADER__ - #include "widgets/juce_TextEditor.h" -#endif -#ifndef __JUCE_TOOLBAR_JUCEHEADER__ - #include "widgets/juce_Toolbar.h" -#endif -#ifndef __JUCE_TOOLBARITEMCOMPONENT_JUCEHEADER__ - #include "widgets/juce_ToolbarItemComponent.h" -#endif -#ifndef __JUCE_TOOLBARITEMFACTORY_JUCEHEADER__ - #include "widgets/juce_ToolbarItemFactory.h" -#endif -#ifndef __JUCE_TOOLBARITEMPALETTE_JUCEHEADER__ - #include "widgets/juce_ToolbarItemPalette.h" -#endif -#ifndef __JUCE_TREEVIEW_JUCEHEADER__ - #include "widgets/juce_TreeView.h" -#endif -#ifndef __JUCE_ALERTWINDOW_JUCEHEADER__ - #include "windows/juce_AlertWindow.h" -#endif -#ifndef __JUCE_CALLOUTBOX_JUCEHEADER__ - #include "windows/juce_CallOutBox.h" -#endif -#ifndef __JUCE_COMPONENTPEER_JUCEHEADER__ - #include "windows/juce_ComponentPeer.h" -#endif -#ifndef __JUCE_DIALOGWINDOW_JUCEHEADER__ - #include "windows/juce_DialogWindow.h" -#endif -#ifndef __JUCE_DOCUMENTWINDOW_JUCEHEADER__ - #include "windows/juce_DocumentWindow.h" -#endif -#ifndef __JUCE_NATIVEMESSAGEBOX_JUCEHEADER__ - #include "windows/juce_NativeMessageBox.h" -#endif -#ifndef __JUCE_RESIZABLEWINDOW_JUCEHEADER__ - #include "windows/juce_ResizableWindow.h" -#endif -#ifndef __JUCE_THREADWITHPROGRESSWINDOW_JUCEHEADER__ - #include "windows/juce_ThreadWithProgressWindow.h" -#endif -#ifndef __JUCE_TOOLTIPWINDOW_JUCEHEADER__ - #include "windows/juce_TooltipWindow.h" -#endif -#ifndef __JUCE_TOPLEVELWINDOW_JUCEHEADER__ - #include "windows/juce_TopLevelWindow.h" -#endif -#ifndef __JUCE_APPLICATIONCOMMANDID_JUCEHEADER__ - #include "commands/juce_ApplicationCommandID.h" -#endif -#ifndef __JUCE_APPLICATIONCOMMANDINFO_JUCEHEADER__ - #include "commands/juce_ApplicationCommandInfo.h" -#endif -#ifndef __JUCE_APPLICATIONCOMMANDMANAGER_JUCEHEADER__ - #include "commands/juce_ApplicationCommandManager.h" -#endif -#ifndef __JUCE_APPLICATIONCOMMANDTARGET_JUCEHEADER__ - #include "commands/juce_ApplicationCommandTarget.h" -#endif -#ifndef __JUCE_KEYPRESSMAPPINGSET_JUCEHEADER__ - #include "commands/juce_KeyPressMappingSet.h" -#endif -#ifndef __JUCE_APPLICATION_JUCEHEADER__ - #include "application/juce_Application.h" -#endif -#ifndef __JUCE_INITIALISATION_JUCEHEADER__ - #include "application/juce_Initialisation.h" -#endif -#ifndef __JUCE_BUBBLECOMPONENT_JUCEHEADER__ - #include "misc/juce_BubbleComponent.h" -#endif -#ifndef __JUCE_DROPSHADOWER_JUCEHEADER__ - #include "misc/juce_DropShadower.h" -#endif -// END_AUTOINCLUDE +class Component; +class LookAndFeel; +class MouseInputSource; +class MouseInputSourceInternal; +class ComponentPeer; +class MarkerList; +class RelativeRectangle; +class MouseEvent; +struct MouseWheelDetails; +class ToggleButton; +class TextButton; +class AlertWindow; +class TextLayout; +class ScrollBar; +class ComboBox; +class Button; +class FilenameComponent; +class DocumentWindow; +class ResizableWindow; +class GroupComponent; +class MenuBarComponent; +class DropShadower; +class GlyphArrangement; +class PropertyComponent; +class TableHeaderComponent; +class Toolbar; +class ToolbarItemComponent; +class PopupMenu; +class ProgressBar; +class FileBrowserComponent; +class DirectoryContentsDisplayComponent; +class FilePreviewComponent; +class ImageButton; +class CallOutBox; +class Drawable; +class DrawablePath; +class DrawableComposite; +class CaretComponent; +class BubbleComponent; +class KeyPressMappingSet; +class ApplicationCommandManagerListener; +class DrawableButton; + +#include "mouse/juce_MouseCursor.h" +#include "mouse/juce_MouseListener.h" +#include "keyboard/juce_ModifierKeys.h" +#include "mouse/juce_MouseInputSource.h" +#include "mouse/juce_MouseEvent.h" +#include "keyboard/juce_KeyPress.h" +#include "keyboard/juce_KeyListener.h" +#include "keyboard/juce_KeyboardFocusTraverser.h" +#include "components/juce_ModalComponentManager.h" +#include "components/juce_ComponentListener.h" +#include "components/juce_CachedComponentImage.h" +#include "components/juce_Component.h" +#include "layout/juce_ComponentAnimator.h" +#include "components/juce_Desktop.h" +#include "layout/juce_ComponentBoundsConstrainer.h" +#include "mouse/juce_ComponentDragger.h" +#include "mouse/juce_DragAndDropTarget.h" +#include "mouse/juce_DragAndDropContainer.h" +#include "mouse/juce_FileDragAndDropTarget.h" +#include "mouse/juce_SelectedItemSet.h" +#include "mouse/juce_LassoComponent.h" +#include "mouse/juce_MouseInactivityDetector.h" +#include "mouse/juce_TextDragAndDropTarget.h" +#include "mouse/juce_TooltipClient.h" +#include "keyboard/juce_CaretComponent.h" +#include "keyboard/juce_SystemClipboard.h" +#include "keyboard/juce_TextEditorKeyMapper.h" +#include "keyboard/juce_TextInputTarget.h" +#include "commands/juce_ApplicationCommandID.h" +#include "commands/juce_ApplicationCommandInfo.h" +#include "commands/juce_ApplicationCommandTarget.h" +#include "commands/juce_ApplicationCommandManager.h" +#include "commands/juce_KeyPressMappingSet.h" +#include "buttons/juce_Button.h" +#include "buttons/juce_ArrowButton.h" +#include "buttons/juce_DrawableButton.h" +#include "buttons/juce_HyperlinkButton.h" +#include "buttons/juce_ImageButton.h" +#include "buttons/juce_ShapeButton.h" +#include "buttons/juce_TextButton.h" +#include "buttons/juce_ToggleButton.h" +#include "layout/juce_AnimatedPosition.h" +#include "layout/juce_AnimatedPositionBehaviours.h" +#include "layout/juce_ComponentBuilder.h" +#include "layout/juce_ComponentMovementWatcher.h" +#include "layout/juce_ConcertinaPanel.h" +#include "layout/juce_GroupComponent.h" +#include "layout/juce_ResizableBorderComponent.h" +#include "layout/juce_ResizableCornerComponent.h" +#include "layout/juce_ResizableEdgeComponent.h" +#include "layout/juce_ScrollBar.h" +#include "layout/juce_StretchableLayoutManager.h" +#include "layout/juce_StretchableLayoutResizerBar.h" +#include "layout/juce_StretchableObjectResizer.h" +#include "layout/juce_TabbedButtonBar.h" +#include "layout/juce_TabbedComponent.h" +#include "layout/juce_Viewport.h" +#include "menus/juce_PopupMenu.h" +#include "menus/juce_MenuBarModel.h" +#include "menus/juce_MenuBarComponent.h" +#include "positioning/juce_RelativeCoordinate.h" +#include "positioning/juce_MarkerList.h" +#include "positioning/juce_RelativePoint.h" +#include "positioning/juce_RelativeRectangle.h" +#include "positioning/juce_RelativeCoordinatePositioner.h" +#include "positioning/juce_RelativeParallelogram.h" +#include "positioning/juce_RelativePointPath.h" +#include "drawables/juce_Drawable.h" +#include "drawables/juce_DrawableShape.h" +#include "drawables/juce_DrawableComposite.h" +#include "drawables/juce_DrawableImage.h" +#include "drawables/juce_DrawablePath.h" +#include "drawables/juce_DrawableRectangle.h" +#include "drawables/juce_DrawableText.h" +#include "widgets/juce_TextEditor.h" +#include "widgets/juce_Label.h" +#include "widgets/juce_ComboBox.h" +#include "widgets/juce_ImageComponent.h" +#include "widgets/juce_ListBox.h" +#include "widgets/juce_ProgressBar.h" +#include "widgets/juce_Slider.h" +#include "widgets/juce_TableHeaderComponent.h" +#include "widgets/juce_TableListBox.h" +#include "widgets/juce_Toolbar.h" +#include "widgets/juce_ToolbarItemComponent.h" +#include "widgets/juce_ToolbarItemFactory.h" +#include "widgets/juce_ToolbarItemPalette.h" +#include "buttons/juce_ToolbarButton.h" +#include "misc/juce_DropShadower.h" +#include "widgets/juce_TreeView.h" +#include "windows/juce_TopLevelWindow.h" +#include "windows/juce_AlertWindow.h" +#include "windows/juce_CallOutBox.h" +#include "windows/juce_ComponentPeer.h" +#include "windows/juce_ResizableWindow.h" +#include "windows/juce_DocumentWindow.h" +#include "windows/juce_DialogWindow.h" +#include "windows/juce_NativeMessageBox.h" +#include "windows/juce_ThreadWithProgressWindow.h" +#include "windows/juce_TooltipWindow.h" +#include "layout/juce_MultiDocumentPanel.h" +#include "filebrowser/juce_FileBrowserListener.h" +#include "filebrowser/juce_DirectoryContentsList.h" +#include "filebrowser/juce_DirectoryContentsDisplayComponent.h" +#include "filebrowser/juce_FileBrowserComponent.h" +#include "filebrowser/juce_FileChooser.h" +#include "filebrowser/juce_FileChooserDialogBox.h" +#include "filebrowser/juce_FileListComponent.h" +#include "filebrowser/juce_FilenameComponent.h" +#include "filebrowser/juce_FilePreviewComponent.h" +#include "filebrowser/juce_FileSearchPathListComponent.h" +#include "filebrowser/juce_FileTreeComponent.h" +#include "filebrowser/juce_ImagePreviewComponent.h" +#include "properties/juce_PropertyComponent.h" +#include "properties/juce_BooleanPropertyComponent.h" +#include "properties/juce_ButtonPropertyComponent.h" +#include "properties/juce_ChoicePropertyComponent.h" +#include "properties/juce_PropertyPanel.h" +#include "properties/juce_SliderPropertyComponent.h" +#include "properties/juce_TextPropertyComponent.h" +#include "application/juce_Application.h" +#include "misc/juce_BubbleComponent.h" +#include "lookandfeel/juce_LookAndFeel.h" +#include "lookandfeel/juce_LookAndFeel_V2.h" +#include "lookandfeel/juce_LookAndFeel_V1.h" +#include "lookandfeel/juce_LookAndFeel_V3.h" } -#endif // __JUCE_GUI_BASICS_JUCEHEADER__ +#endif // JUCE_GUI_BASICS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.mm b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.mm index 4c918c7..fd6808c 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.mm +++ b/JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.mm @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_gui_basics/juce_module_info b/JuceLibraryCode/modules/juce_gui_basics/juce_module_info index 2c45b2d..9ec7c1a 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/juce_module_info +++ b/JuceLibraryCode/modules/juce_gui_basics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_gui_basics", "name": "JUCE GUI core classes", - "version": "2.0.21", + "version": "3.0.6", "description": "Basic user-interface components and related classes.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", @@ -35,5 +35,6 @@ "native/*" ], "OSXFrameworks": "Cocoa Carbon QuartzCore", - "iOSFrameworks": "UIKit" + "iOSFrameworks": "UIKit", + "LinuxLibs": "X11 Xinerama Xext" } diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp index 9e25803..67e8155 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -26,7 +25,7 @@ CaretComponent::CaretComponent (Component* const keyFocusOwner) : owner (keyFocusOwner) { - setAlwaysOnTop (true); + setPaintingIsUnclipped (true); setInterceptsMouseClicks (false, false); } @@ -36,7 +35,8 @@ CaretComponent::~CaretComponent() void CaretComponent::paint (Graphics& g) { - g.fillAll (findColour (caretColourId, true)); + g.setColour (findColour (caretColourId, true)); + g.fillRect (getLocalBounds()); } void CaretComponent::timerCallback() @@ -53,6 +53,6 @@ void CaretComponent::setCaretPosition (const Rectangle& characterArea) bool CaretComponent::shouldBeShown() const { - return owner == nullptr || (owner->hasKeyboardFocus (true) + return owner == nullptr || (owner->hasKeyboardFocus (false) && ! owner->isCurrentlyBlockedByAnotherModalComponent()); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.h index 953ad3e..2ae0ec1 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_CaretComponent.h @@ -1,39 +1,36 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_CARETCOMPONENT_JUCEHEADER__ -#define __JUCE_CARETCOMPONENT_JUCEHEADER__ - -#include "../components/juce_Component.h" +#ifndef JUCE_CARETCOMPONENT_H_INCLUDED +#define JUCE_CARETCOMPONENT_H_INCLUDED //============================================================================== /** */ class JUCE_API CaretComponent : public Component, - public Timer + private Timer { public: //============================================================================== @@ -68,16 +65,16 @@ public: //============================================================================== /** @internal */ - void paint (Graphics& g); - /** @internal */ - void timerCallback(); + void paint (Graphics&) override; private: Component* owner; - bool shouldBeShown() const; - JUCE_DECLARE_NON_COPYABLE (CaretComponent); + bool shouldBeShown() const; + void timerCallback() override; + + JUCE_DECLARE_NON_COPYABLE (CaretComponent) }; -#endif // __JUCE_CARETCOMPONENT_JUCEHEADER__ +#endif // JUCE_CARETCOMPONENT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.cpp b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.cpp index 08cd4b1..76ea686 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.h index 6d3379f..fb30138 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyListener.h @@ -1,33 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_KEYLISTENER_JUCEHEADER__ -#define __JUCE_KEYLISTENER_JUCEHEADER__ - -#include "juce_KeyPress.h" -class Component; +#ifndef JUCE_KEYLISTENER_H_INCLUDED +#define JUCE_KEYLISTENER_H_INCLUDED //============================================================================== @@ -37,7 +33,7 @@ class Component; 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 + @see KeyPress, Component::addKeyListener, KeyPressMappingSet */ class JUCE_API KeyListener { @@ -77,4 +73,4 @@ public: }; -#endif // __JUCE_KEYLISTENER_JUCEHEADER__ +#endif // JUCE_KEYLISTENER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.cpp b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.cpp index fdeb4c7..f833f11 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.cpp @@ -1,52 +1,44 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ KeyPress::KeyPress() noexcept - : keyCode (0), - textCharacter (0) + : keyCode (0), textCharacter (0) { } -KeyPress::KeyPress (const int keyCode_, - const ModifierKeys& mods_, - const juce_wchar textCharacter_) noexcept - : keyCode (keyCode_), - mods (mods_), - textCharacter (textCharacter_) +KeyPress::KeyPress (int code, ModifierKeys m, juce_wchar textChar) noexcept + : keyCode (code), mods (m), textCharacter (textChar) { } -KeyPress::KeyPress (const int keyCode_) noexcept - : keyCode (keyCode_), - textCharacter (0) +KeyPress::KeyPress (const int code) noexcept + : keyCode (code), textCharacter (0) { } KeyPress::KeyPress (const KeyPress& other) noexcept - : keyCode (other.keyCode), - mods (other.mods), + : keyCode (other.keyCode), mods (other.mods), textCharacter (other.textCharacter) { } @@ -60,6 +52,11 @@ KeyPress& KeyPress::operator= (const KeyPress& other) noexcept return *this; } +bool KeyPress::operator== (int otherKeyCode) const noexcept +{ + return keyCode == otherKeyCode && ! mods.isAnyModifierKeyDown(); +} + bool KeyPress::operator== (const KeyPress& other) const noexcept { return mods.getRawFlags() == other.mods.getRawFlags() @@ -78,6 +75,11 @@ bool KeyPress::operator!= (const KeyPress& other) const noexcept return ! operator== (other); } +bool KeyPress::operator!= (int otherKeyCode) const noexcept +{ + return ! operator== (otherKeyCode); +} + bool KeyPress::isCurrentlyDown() const { return isKeyCurrentlyDown (keyCode) @@ -180,13 +182,14 @@ namespace KeyPressHelpers { "command + ", 0x2318 }, { "option + ", 0x2325 }, { "ctrl + ", 0x2303 }, - { "return", 0x23ce }, + { "return", 0x21b5 }, { "cursor left", 0x2190 }, { "cursor right", 0x2192 }, { "cursor up", 0x2191 }, { "cursor down", 0x2193 }, { "backspace", 0x232b }, - { "delete", 0x2326 } + { "delete", 0x2326 }, + { "spacebar", 0x2423 } }; #endif } @@ -250,23 +253,14 @@ String KeyPress::getTextDescription() const if (textCharacter == '/') return "/"; - if (mods.isCtrlDown()) - desc << "ctrl + "; - - if (mods.isShiftDown()) - desc << "shift + "; + if (mods.isCtrlDown()) desc << "ctrl + "; + if (mods.isShiftDown()) desc << "shift + "; #if JUCE_MAC - if (mods.isAltDown()) - desc << "option + "; - - // only do this on the mac, because on Windows ctrl and command are the same, - // and this would get confusing - if (mods.isCommandDown()) - desc << "command + "; + if (mods.isAltDown()) desc << "option + "; + if (mods.isCommandDown()) desc << "command + "; #else - if (mods.isAltDown()) - desc << "alt + "; + if (mods.isAltDown()) desc << "alt + "; #endif for (int i = 0; i < numElementsInArray (KeyPressHelpers::translations); ++i) diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.h index 7599f37..1269b38 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyPress.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_KEYPRESS_JUCEHEADER__ -#define __JUCE_KEYPRESS_JUCEHEADER__ - -#include "juce_ModifierKeys.h" +#ifndef JUCE_KEYPRESS_H_INCLUDED +#define JUCE_KEYPRESS_H_INCLUDED //============================================================================== @@ -35,7 +32,7 @@ E.g. a KeyPress might represent CTRL+C, SHIFT+ALT+H, Spacebar, Escape, etc. - @see Component, KeyListener, Button::addShortcut, KeyPressMappingManager + @see Component, KeyListener, KeyPressMappingSet, Button::addShortcut */ class JUCE_API KeyPress { @@ -67,12 +64,11 @@ public: @see getKeyCode, isKeyCode, getModifiers */ KeyPress (int keyCode, - const ModifierKeys& modifiers, + ModifierKeys modifiers, juce_wchar textCharacter) noexcept; - /** Creates a keypress with a keyCode but no modifiers or text character. - */ - KeyPress (int keyCode) noexcept; + /** Creates a keypress with a keyCode but no modifiers or text character. */ + explicit KeyPress (int keyCode) noexcept; /** Creates a copy of another KeyPress. */ KeyPress (const KeyPress& other) noexcept; @@ -86,6 +82,12 @@ public: /** Compares two KeyPress objects. */ bool operator!= (const KeyPress& other) const noexcept; + /** Returns true if this keypress is for the given keycode without any modifiers. */ + bool operator== (int keyCode) const noexcept; + + /** Returns true if this keypress is not for the given keycode without any modifiers. */ + bool operator!= (int keyCode) const noexcept; + //============================================================================== /** Returns true if this is a valid KeyPress. @@ -245,8 +247,8 @@ private: ModifierKeys mods; juce_wchar textCharacter; - JUCE_LEAK_DETECTOR (KeyPress); + JUCE_LEAK_DETECTOR (KeyPress) }; -#endif // __JUCE_KEYPRESS_JUCEHEADER__ +#endif // JUCE_KEYPRESS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp index c4eaf93..b84e903 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -57,8 +56,7 @@ namespace KeyboardFocusHelpers Array localComps; ScreenPositionComparator comparator; - int i; - for (i = parent->getNumChildComponents(); --i >= 0;) + for (int i = parent->getNumChildComponents(); --i >= 0;) { Component* const c = parent->getChildComponent (i); @@ -66,7 +64,7 @@ namespace KeyboardFocusHelpers localComps.addSorted (comparator, c); } - for (i = 0; i < localComps.size(); ++i) + for (int i = 0; i < localComps.size(); ++i) { Component* const c = localComps.getUnchecked (i); diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h index 32446c5..94ef5f7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ -#define __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ - -class Component; +#ifndef JUCE_KEYBOARDFOCUSTRAVERSER_H_INCLUDED +#define JUCE_KEYBOARDFOCUSTRAVERSER_H_INCLUDED //============================================================================== @@ -63,7 +60,7 @@ public: 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. + This may return nullptr if there's no suitable candidate. */ virtual Component* getNextComponent (Component* current); @@ -73,7 +70,7 @@ public: 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. + This may return nullptr if there's no suitable candidate. */ virtual Component* getPreviousComponent (Component* current); @@ -83,10 +80,10 @@ public: The default implementation will just return the foremost child component that wants focus. - This may return 0 if there's no suitable candidate. + This may return nullptr if there's no suitable candidate. */ virtual Component* getDefaultComponent (Component* parentComponent); }; -#endif // __JUCE_KEYBOARDFOCUSTRAVERSER_JUCEHEADER__ +#endif // JUCE_KEYBOARDFOCUSTRAVERSER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.cpp b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.cpp index 476a6fd..f3bab25 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.cpp @@ -1,44 +1,32 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -ModifierKeys::ModifierKeys() noexcept - : flags (0) -{ -} +ModifierKeys::ModifierKeys() noexcept : flags (0) {} +ModifierKeys::ModifierKeys (int rawFlags) noexcept : flags (rawFlags) {} +ModifierKeys::ModifierKeys (const ModifierKeys& other) noexcept : flags (other.flags) {} -ModifierKeys::ModifierKeys (const int flags_) noexcept - : flags (flags_) -{ -} - -ModifierKeys::ModifierKeys (const ModifierKeys& other) noexcept - : flags (other.flags) -{ -} - -ModifierKeys& ModifierKeys::operator= (const ModifierKeys& other) noexcept +ModifierKeys& ModifierKeys::operator= (const ModifierKeys other) noexcept { flags = other.flags; return *this; diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h index 85d384e..123ffed 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_ModifierKeys.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_MODIFIERKEYS_JUCEHEADER__ -#define __JUCE_MODIFIERKEYS_JUCEHEADER__ +#ifndef JUCE_MODIFIERKEYS_H_INCLUDED +#define JUCE_MODIFIERKEYS_H_INCLUDED //============================================================================== @@ -55,7 +54,7 @@ public: ModifierKeys (const ModifierKeys& other) noexcept; /** Copies this object from another one. */ - ModifierKeys& operator= (const ModifierKeys& other) noexcept; + ModifierKeys& operator= (const ModifierKeys other) noexcept; //============================================================================== /** Checks whether the 'command' key flag is set (or 'ctrl' on Windows/Linux). @@ -106,7 +105,7 @@ public: */ inline bool isCtrlDown() const noexcept { return testFlags (ctrlModifier); } - /** Checks whether the shift key's flag is set. */ + /** Checks whether the ALT key's flag is set. */ inline bool isAltDown() const noexcept { return testFlags (altModifier); } //============================================================================== @@ -155,6 +154,9 @@ public: /** Represents a combination of all the mouse buttons at once. */ allMouseButtonModifiers = leftButtonModifier | rightButtonModifier | middleButtonModifier, + + /** Represents a combination of all the alt, ctrl and command key modifiers. */ + ctrlAltCommandModifiers = ctrlModifier | altModifier | commandModifier }; //============================================================================== @@ -164,18 +166,18 @@ public: /** Returns a copy of only the non-mouse flags */ ModifierKeys withoutMouseButtons() const noexcept { return ModifierKeys (flags & ~allMouseButtonModifiers); } - bool operator== (const ModifierKeys& other) const noexcept { return flags == other.flags; } - bool operator!= (const ModifierKeys& other) const noexcept { return flags != other.flags; } + bool operator== (const ModifierKeys other) const noexcept { return flags == other.flags; } + bool operator!= (const ModifierKeys other) const noexcept { return flags != other.flags; } //============================================================================== /** Returns the raw flags for direct testing. */ inline int getRawFlags() const noexcept { return flags; } - inline const ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept { return ModifierKeys (flags & ~rawFlagsToClear); } - inline const ModifierKeys withFlags (int rawFlagsToSet) const noexcept { return ModifierKeys (flags | rawFlagsToSet); } + ModifierKeys withoutFlags (int rawFlagsToClear) const noexcept { return ModifierKeys (flags & ~rawFlagsToClear); } + ModifierKeys withFlags (int rawFlagsToSet) const noexcept { 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 noexcept { return (flags & flagsToTest) != 0; } + bool testFlags (int flagsToTest) const noexcept { return (flags & flagsToTest) != 0; } /** Returns the total number of mouse buttons that are down. */ int getNumMouseButtonsDown() const noexcept; @@ -218,4 +220,4 @@ private: }; -#endif // __JUCE_MODIFIERKEYS_JUCEHEADER__ +#endif // JUCE_MODIFIERKEYS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_SystemClipboard.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_SystemClipboard.h index ae7192b..29e323b 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_SystemClipboard.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_SystemClipboard.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ -#define __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ +#ifndef JUCE_SYSTEMCLIPBOARD_H_INCLUDED +#define JUCE_SYSTEMCLIPBOARD_H_INCLUDED //============================================================================== @@ -45,4 +44,4 @@ public: static String getTextFromClipboard(); }; -#endif // __JUCE_SYSTEMCLIPBOARD_JUCEHEADER__ +#endif // JUCE_SYSTEMCLIPBOARD_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h index 88ddbab..f09dc9b 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextEditorKeyMapper.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__ -#define __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__ - -#include "juce_KeyPress.h" +#ifndef JUCE_TEXTEDITORKEYMAPPER_H_INCLUDED +#define JUCE_TEXTEDITORKEYMAPPER_H_INCLUDED //============================================================================== @@ -43,33 +40,56 @@ struct TextEditorKeyMapper */ static bool invokeKeyFunction (CallbackClass& target, const KeyPress& key) { - const bool isShiftDown = key.getModifiers().isShiftDown(); - const bool ctrlOrAltDown = key.getModifiers().isCtrlDown() || key.getModifiers().isAltDown(); + const ModifierKeys& mods = key.getModifiers(); + + const bool isShiftDown = mods.isShiftDown(); + const bool ctrlOrAltDown = mods.isCtrlDown() || mods.isAltDown(); + + int numCtrlAltCommandKeys = 0; + if (mods.isCtrlDown()) ++numCtrlAltCommandKeys; + if (mods.isAltDown()) ++numCtrlAltCommandKeys; if (key == KeyPress (KeyPress::downKey, ModifierKeys::ctrlModifier, 0) && target.scrollUp()) return true; if (key == KeyPress (KeyPress::upKey, ModifierKeys::ctrlModifier, 0) && target.scrollDown()) return true; #if JUCE_MAC - if (key.getModifiers().isCommandDown()) + if (mods.isCommandDown() && ! ctrlOrAltDown) { if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretToTop (isShiftDown); if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretToEnd (isShiftDown); if (key.isKeyCode (KeyPress::leftKey)) return target.moveCaretToStartOfLine (isShiftDown); - if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretToEndOfLine (isShiftDown); + if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretToEndOfLine (isShiftDown); } + + if (mods.isCommandDown()) + ++numCtrlAltCommandKeys; #endif - if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretUp (isShiftDown); - if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretDown (isShiftDown); - if (key.isKeyCode (KeyPress::leftKey)) return target.moveCaretLeft (ctrlOrAltDown, isShiftDown); - if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretRight (ctrlOrAltDown, isShiftDown); - if (key.isKeyCode (KeyPress::pageUpKey)) return target.pageUp (isShiftDown); - if (key.isKeyCode (KeyPress::pageDownKey)) return target.pageDown (isShiftDown); + if (numCtrlAltCommandKeys < 2) + { + if (key.isKeyCode (KeyPress::leftKey)) return target.moveCaretLeft (ctrlOrAltDown, isShiftDown); + if (key.isKeyCode (KeyPress::rightKey)) return target.moveCaretRight (ctrlOrAltDown, isShiftDown); - if (key.isKeyCode (KeyPress::homeKey)) return ctrlOrAltDown ? target.moveCaretToTop (isShiftDown) - : target.moveCaretToStartOfLine (isShiftDown); - if (key.isKeyCode (KeyPress::endKey)) return ctrlOrAltDown ? target.moveCaretToEnd (isShiftDown) - : target.moveCaretToEndOfLine (isShiftDown); + if (key.isKeyCode (KeyPress::homeKey)) return ctrlOrAltDown ? target.moveCaretToTop (isShiftDown) + : target.moveCaretToStartOfLine (isShiftDown); + if (key.isKeyCode (KeyPress::endKey)) return ctrlOrAltDown ? target.moveCaretToEnd (isShiftDown) + : target.moveCaretToEndOfLine (isShiftDown); + } + + if (numCtrlAltCommandKeys == 0) + { + if (key.isKeyCode (KeyPress::upKey)) return target.moveCaretUp (isShiftDown); + if (key.isKeyCode (KeyPress::downKey)) return target.moveCaretDown (isShiftDown); + + if (key.isKeyCode (KeyPress::pageUpKey)) return target.pageUp (isShiftDown); + if (key.isKeyCode (KeyPress::pageDownKey)) return target.pageDown (isShiftDown); + } + + if (numCtrlAltCommandKeys < 2) + { + if (key.isKeyCode (KeyPress::backspaceKey)) return target.deleteBackwards (ctrlOrAltDown); + if (key.isKeyCode (KeyPress::deleteKey)) return target.deleteForwards (ctrlOrAltDown); + } if (key == KeyPress ('c', ModifierKeys::commandModifier, 0) || key == KeyPress (KeyPress::insertKey, ModifierKeys::ctrlModifier, 0)) @@ -83,9 +103,6 @@ struct TextEditorKeyMapper || key == KeyPress (KeyPress::insertKey, ModifierKeys::shiftModifier, 0)) return target.pasteFromClipboard(); - if (key.isKeyCode (KeyPress::backspaceKey)) return target.deleteBackwards (ctrlOrAltDown); - if (key.isKeyCode (KeyPress::deleteKey)) return target.deleteForwards (ctrlOrAltDown); - if (key == KeyPress ('a', ModifierKeys::commandModifier, 0)) return target.selectAll(); @@ -101,4 +118,4 @@ struct TextEditorKeyMapper }; -#endif // __JUCE_TEXTEDITORKEYMAPPER_JUCEHEADER__ +#endif // JUCE_TEXTEDITORKEYMAPPER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h index 192368c..9770f5d 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_TextInputTarget.h @@ -1,30 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_TEXTINPUTTARGET_JUCEHEADER__ -#define __JUCE_TEXTINPUTTARGET_JUCEHEADER__ +#ifndef JUCE_TEXTINPUTTARGET_H_INCLUDED +#define JUCE_TEXTINPUTTARGET_H_INCLUDED //============================================================================== @@ -72,7 +71,24 @@ public: /** Returns the position of the caret, relative to the component's origin. */ virtual Rectangle getCaretRectangle() = 0; + + /** A set of possible on-screen keyboard types, for use in the + getKeyboardType() method. + */ + enum VirtualKeyboardType + { + textKeyboard = 0, + numericKeyboard, + urlKeyboard, + emailAddressKeyboard, + phoneNumberKeyboard + }; + + /** Returns the target's preference for the type of keyboard that would be most appropriate. + This may be ignored, depending on the capabilities of the OS. + */ + virtual VirtualKeyboardType getKeyboardType() { return textKeyboard; } }; -#endif // __JUCE_TEXTINPUTTARGET_JUCEHEADER__ +#endif // JUCE_TEXTINPUTTARGET_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h new file mode 100644 index 0000000..dac6ce1 --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPosition.h @@ -0,0 +1,209 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_ANIMATEDPOSITION_H_INCLUDED +#define JUCE_ANIMATEDPOSITION_H_INCLUDED + + +//============================================================================== +/** + Models a 1-dimensional position that can be dragged around by the user, and which + will then continue moving with a customisable physics behaviour when released. + + This is useful for things like scrollable views or objects that can be dragged and + thrown around with the mouse/touch, and by writing your own behaviour class, you can + customise the trajectory that it follows when released. + + The class uses its own Timer to continuously change its value when a drag ends, and + Listener objects can be registered to receive callbacks whenever the value changes. + + The value is stored as a double, and can be used to represent whatever units you need. + + The template parameter Behaviour must be a class that implements various methods to + return the physics of the value's movement - you can use the classes provided for this + in the AnimatedPositionBehaviours namespace, or write your own custom behaviour. + + @see AnimatedPositionBehaviours::ContinuousWithMomentum, + AnimatedPositionBehaviours::SnapToPageBoundaries +*/ +template +class AnimatedPosition : private Timer +{ +public: + AnimatedPosition() + : position(), grabbedPos(), releaseVelocity(), + range (-std::numeric_limits::max(), + std::numeric_limits::max()) + { + } + + /** Sets a range within which the value will be constrained. */ + void setLimits (Range newRange) + { + range = newRange; + } + + //============================================================================== + /** Called to indicate that the object is now being controlled by a + mouse-drag or similar operation. + + After calling this method, you should make calls to the drag() method + each time the mouse drags the position around, and always be sure to + finish with a call to endDrag() when the mouse is released, which allows + the position to continue moving freely according to the specified behaviour. + */ + void beginDrag() + { + grabbedPos = position; + releaseVelocity = 0; + stopTimer(); + } + + /** Called during a mouse-drag operation, to indicate that the mouse has moved. + The delta is the difference between the position when beginDrag() was called + and the new position that's required. + */ + void drag (double deltaFromStartOfDrag) + { + moveTo (grabbedPos + deltaFromStartOfDrag); + } + + /** Called after beginDrag() and drag() to indicate that the drag operation has + now finished. + */ + void endDrag() + { + startTimer (1000 / 60); + } + + /** Called outside of a drag operation to cause a nudge in the specified direction. + This is intended for use by e.g. mouse-wheel events. + */ + void nudge (double deltaFromCurrentPosition) + { + startTimer (100); + moveTo (position + deltaFromCurrentPosition); + } + + //============================================================================== + /** Returns the current position. */ + double getPosition() const noexcept + { + return position; + } + + /** Explicitly sets the position and stops any further movement. + This will cause a synchronous call to any listeners if the position actually + changes. + */ + void setPosition (double newPosition) + { + stopTimer(); + setPositionAndSendChange (newPosition); + } + + //============================================================================== + /** Implement this class if you need to receive callbacks when the value of + an AnimatedPosition changes. + @see AnimatedPosition::addListener, AnimatedPosition::removeListener + */ + class Listener + { + public: + virtual ~Listener() {} + + /** Called synchronously when an AnimatedPosition changes. */ + virtual void positionChanged (AnimatedPosition&, double newPosition) = 0; + }; + + /** Adds a listener to be called when the value changes. */ + void addListener (Listener* listener) { listeners.add (listener); } + + /** Removes a previously-registered listener. */ + void removeListener (Listener* listener) { listeners.remove (listener); } + + //============================================================================== + /** The behaviour object. + This is public to let you tweak any parameters that it provides. + */ + Behaviour behaviour; + +private: + //============================================================================== + double position, grabbedPos, releaseVelocity; + Range range; + Time lastUpdate, lastDrag; + ListenerList listeners; + + static double getSpeed (const Time last, double lastPos, + const Time now, double newPos) + { + const double elapsedSecs = jmax (0.005, (now - last).inSeconds()); + const double v = (newPos - lastPos) / elapsedSecs; + return std::abs (v) > 0.2 ? v : 0.0; + } + + void moveTo (double newPos) + { + const Time now (Time::getCurrentTime()); + releaseVelocity = getSpeed (lastDrag, position, now, newPos); + behaviour.releasedWithVelocity (newPos, releaseVelocity); + lastDrag = now; + + setPositionAndSendChange (newPos); + } + + void setPositionAndSendChange (double newPosition) + { + newPosition = range.clipValue (newPosition); + + if (position != newPosition) + { + position = newPosition; + listeners.call (&Listener::positionChanged, *this, newPosition); + } + } + + void timerCallback() override + { + const Time now = Time::getCurrentTime(); + + const double elapsed = jlimit (0.001, 0.020, (now - lastUpdate).inSeconds()); + lastUpdate = now; + + const double newPos = behaviour.getNextPosition (position, elapsed); + + if (behaviour.isStopped (newPos)) + stopTimer(); + else + startTimer (1000 / 60); + + setPositionAndSendChange (newPos); + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AnimatedPosition) +}; + + +#endif // JUCE_ANIMATEDPOSITION_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h new file mode 100644 index 0000000..026f36e --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_AnimatedPositionBehaviours.h @@ -0,0 +1,151 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_ANIMATEDPOSITIONBEHAVIOURS_H_INCLUDED +#define JUCE_ANIMATEDPOSITIONBEHAVIOURS_H_INCLUDED + + +//============================================================================== +/** Contains classes for different types of physics behaviours - these classes + are used as template parameters for the AnimatedPosition class. +*/ +namespace AnimatedPositionBehaviours +{ + /** A non-snapping behaviour that allows the content to be freely flicked in + either direction, with momentum based on the velocity at which it was + released, and variable friction to make it come to a halt. + + This class is intended to be used as a template parameter to the + AnimatedPosition class. + + @see AnimatedPosition + */ + struct ContinuousWithMomentum + { + ContinuousWithMomentum() noexcept + : velocity (0), damping (0.92) + { + } + + /** Sets the friction that damps the movement of the value. + A typical value is 0.08; higher values indicate more friction. + */ + void setFriction (double newFriction) noexcept + { + damping = 1.0 - newFriction; + } + + /** Called by the AnimatedPosition class. This tells us the position and + velocity at which the user is about to release the object. + The velocity is measured in units/second. + */ + void releasedWithVelocity (double /*position*/, double releaseVelocity) noexcept + { + velocity = releaseVelocity; + } + + /** Called by the AnimatedPosition class to get the new position, after + the given time has elapsed. + */ + double getNextPosition (double oldPos, double elapsedSeconds) noexcept + { + velocity *= damping; + + if (std::abs (velocity) < 0.05) + velocity = 0; + + return oldPos + velocity * elapsedSeconds; + } + + /** Called by the AnimatedPosition class to check whether the object + is now stationary. + */ + bool isStopped (double /*position*/) const noexcept + { + return velocity == 0; + } + + private: + double velocity, damping; + }; + + //============================================================================== + /** A behaviour that gravitates an AnimatedPosition object towards the nearest + integer position when released. + + This class is intended to be used as a template parameter to the + AnimatedPosition class. It's handy when using an AnimatedPosition to show a + series of pages, because it allows the pages can be scrolled smoothly, but when + released, snaps back to show a whole page. + + @see AnimatedPosition + */ + struct SnapToPageBoundaries + { + SnapToPageBoundaries() noexcept : targetSnapPosition() + { + } + + /** Called by the AnimatedPosition class. This tells us the position and + velocity at which the user is about to release the object. + The velocity is measured in units/second. + */ + void releasedWithVelocity (double position, double releaseVelocity) noexcept + { + targetSnapPosition = std::floor (position + 0.5); + + if (releaseVelocity > 1.0 && targetSnapPosition < position) ++targetSnapPosition; + if (releaseVelocity < -1.0 && targetSnapPosition > position) --targetSnapPosition; + } + + /** Called by the AnimatedPosition class to get the new position, after + the given time has elapsed. + */ + double getNextPosition (double oldPos, double elapsedSeconds) const noexcept + { + if (isStopped (oldPos)) + return targetSnapPosition; + + const double snapSpeed = 10.0; + const double velocity = (targetSnapPosition - oldPos) * snapSpeed; + const double newPos = oldPos + velocity * elapsedSeconds; + + return isStopped (newPos) ? targetSnapPosition : newPos; + } + + /** Called by the AnimatedPosition class to check whether the object + is now stationary. + */ + bool isStopped (double position) const noexcept + { + return std::abs (targetSnapPosition - position) < 0.001; + } + + private: + double targetSnapPosition; + }; +}; + + +#endif // JUCE_ANIMATEDPOSITIONBEHAVIOURS_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp index f3a95bb..b7d052c 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -46,11 +45,11 @@ public: isMoving = (finalBounds != component->getBounds()); isChangingAlpha = (finalAlpha != component->getAlpha()); - left = component->getX(); - top = component->getY(); - right = component->getRight(); - bottom = component->getBottom(); - alpha = component->getAlpha(); + left = component->getX(); + top = component->getY(); + right = component->getRight(); + bottom = component->getBottom(); + alpha = component->getAlpha(); const double invTotalDistance = 4.0 / (startSpeed_ + endSpeed_ + 2.0); startSpeed = jmax (0.0, startSpeed_ * invTotalDistance); @@ -67,10 +66,8 @@ public: bool useTimeslice (const int elapsed) { - Component* const c = proxy != nullptr ? static_cast (proxy) - : static_cast (component); - - if (c != nullptr) + if (Component* const c = proxy != nullptr ? static_cast (proxy) + : static_cast (component)) { msElapsed += elapsed; double newProgress = msElapsed / (double) msTotal; @@ -138,38 +135,38 @@ public: class ProxyComponent : public Component { public: - ProxyComponent (Component& component) - : image (component.createComponentSnapshot (component.getLocalBounds())) + ProxyComponent (Component& c) { - setBounds (component.getBounds()); - setTransform (component.getTransform()); - setAlpha (component.getAlpha()); + setWantsKeyboardFocus (false); + setBounds (c.getBounds()); + setTransform (c.getTransform()); + setAlpha (c.getAlpha()); setInterceptsMouseClicks (false, false); - Component* const parent = component.getParentComponent(); - - if (parent != nullptr) + if (Component* const parent = c.getParentComponent()) parent->addAndMakeVisible (this); - else if (component.isOnDesktop() && component.getPeer() != nullptr) - addToDesktop (component.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses); + else if (c.isOnDesktop() && c.getPeer() != nullptr) + addToDesktop (c.getPeer()->getStyleFlags() | ComponentPeer::windowIgnoresKeyPresses); else jassertfalse; // seem to be trying to animate a component that's not visible.. + image = c.createComponentSnapshot (c.getLocalBounds(), false, getDesktopScaleFactor()); + setVisible (true); - toBehind (&component); + toBehind (&c); } - void paint (Graphics& g) + void paint (Graphics& g) override { g.setOpacity (1.0f); - g.drawImage (image, 0, 0, getWidth(), getHeight(), - 0, 0, image.getWidth(), image.getHeight()); + g.drawImageTransformed (image, AffineTransform::scale (getWidth() / (float) image.getWidth(), + getHeight() / (float) image.getHeight()), false); } private: Image image; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProxyComponent) }; WeakReference component; @@ -282,9 +279,7 @@ void ComponentAnimator::cancelAllAnimations (const bool moveComponentsToTheirFin void ComponentAnimator::cancelAnimation (Component* const component, const bool moveComponentToItsFinalPosition) { - AnimationTask* const at = findTaskFor (component); - - if (at != nullptr) + if (AnimationTask* const at = findTaskFor (component)) { if (moveComponentToItsFinalPosition) at->moveToFinalDestination(); @@ -297,9 +292,8 @@ void ComponentAnimator::cancelAnimation (Component* const component, Rectangle ComponentAnimator::getComponentDestination (Component* const component) { jassert (component != nullptr); - AnimationTask* const at = findTaskFor (component); - if (at != nullptr) + if (AnimationTask* const at = findTaskFor (component)) return at->destination; return component->getBounds(); diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.h index ae979e5..f8ef6c6 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_COMPONENTANIMATOR_JUCEHEADER__ -#define __JUCE_COMPONENTANIMATOR_JUCEHEADER__ - -#include "../components/juce_Component.h" +#ifndef JUCE_COMPONENTANIMATOR_H_INCLUDED +#define JUCE_COMPONENTANIMATOR_H_INCLUDED //============================================================================== @@ -157,8 +154,8 @@ private: AnimationTask* findTaskFor (Component* component) const noexcept; void timerCallback(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentAnimator); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentAnimator) }; -#endif // __JUCE_COMPONENTANIMATOR_JUCEHEADER__ +#endif // JUCE_COMPONENTANIMATOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp index 2ac1af8..4f1fd4a 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.cpp @@ -1,33 +1,30 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ ComponentBoundsConstrainer::ComponentBoundsConstrainer() noexcept - : minW (0), - maxW (0x3fffffff), - minH (0), - maxH (0x3fffffff), + : minW (0), maxW (0x3fffffff), + minH (0), maxH (0x3fffffff), minOffTop (0), minOffLeft (0), minOffBottom (0), @@ -41,25 +38,10 @@ ComponentBoundsConstrainer::~ComponentBoundsConstrainer() } //============================================================================== -void ComponentBoundsConstrainer::setMinimumWidth (const int minimumWidth) noexcept -{ - minW = minimumWidth; -} - -void ComponentBoundsConstrainer::setMaximumWidth (const int maximumWidth) noexcept -{ - maxW = maximumWidth; -} - -void ComponentBoundsConstrainer::setMinimumHeight (const int minimumHeight) noexcept -{ - minH = minimumHeight; -} - -void ComponentBoundsConstrainer::setMaximumHeight (const int maximumHeight) noexcept -{ - maxH = maximumHeight; -} +void ComponentBoundsConstrainer::setMinimumWidth (const int minimumWidth) noexcept { minW = minimumWidth; } +void ComponentBoundsConstrainer::setMaximumWidth (const int maximumWidth) noexcept { maxW = maximumWidth; } +void ComponentBoundsConstrainer::setMinimumHeight (const int minimumHeight) noexcept { minH = minimumHeight; } +void ComponentBoundsConstrainer::setMaximumHeight (const int maximumHeight) noexcept { maxH = maximumHeight; } void ComponentBoundsConstrainer::setMinimumSize (const int minimumWidth, const int minimumHeight) noexcept { @@ -70,11 +52,8 @@ void ComponentBoundsConstrainer::setMinimumSize (const int minimumWidth, const i minW = minimumWidth; minH = minimumHeight; - if (minW > maxW) - maxW = minW; - - if (minH > maxH) - maxH = minH; + if (minW > maxW) maxW = minW; + if (minH > maxH) maxH = minH; } void ComponentBoundsConstrainer::setMaximumSize (const int maximumWidth, const int maximumHeight) noexcept @@ -108,10 +87,10 @@ void ComponentBoundsConstrainer::setMinimumOnscreenAmounts (const int minimumWhe const int minimumWhenOffTheBottom, const int minimumWhenOffTheRight) noexcept { - minOffTop = minimumWhenOffTheTop; - minOffLeft = minimumWhenOffTheLeft; + minOffTop = minimumWhenOffTheTop; + minOffLeft = minimumWhenOffTheLeft; minOffBottom = minimumWhenOffTheBottom; - minOffRight = minimumWhenOffTheRight; + minOffRight = minimumWhenOffTheRight; } void ComponentBoundsConstrainer::setFixedAspectRatio (const double widthOverHeight) noexcept @@ -136,19 +115,16 @@ void ComponentBoundsConstrainer::setBoundsForComponent (Component* const compone Rectangle limits, bounds (targetBounds); BorderSize border; - Component* const parent = component->getParentComponent(); - - if (parent == nullptr) + if (Component* const parent = component->getParentComponent()) { - ComponentPeer* peer = component->getPeer(); - if (peer != nullptr) - border = peer->getFrameSize(); - - limits = Desktop::getInstance().getMonitorAreaContaining (bounds.getCentre()); + limits.setSize (parent->getWidth(), parent->getHeight()); } else { - limits.setSize (parent->getWidth(), parent->getHeight()); + if (ComponentPeer* const peer = component->getPeer()) + border = peer->getFrameSize(); + + limits = Desktop::getInstance().getDisplays().getDisplayContaining (bounds.getCentre()).userArea; } border.addTo (bounds); @@ -172,9 +148,7 @@ void ComponentBoundsConstrainer::checkComponentBounds (Component* component) void ComponentBoundsConstrainer::applyBoundsToComponent (Component* component, const Rectangle& bounds) { - Component::Positioner* const positioner = component->getPositioner(); - - if (positioner != nullptr) + if (Component::Positioner* const positioner = component->getPositioner()) positioner->applyNewBounds (bounds); else component->setBounds (bounds); diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h index 1208697..65f5ee4 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBoundsConstrainer.h @@ -1,32 +1,29 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ -#ifndef __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ -#define __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ - -#include "../components/juce_Component.h" +#ifndef JUCE_COMPONENTBOUNDSCONSTRAINER_H_INCLUDED +#define JUCE_COMPONENTBOUNDSCONSTRAINER_H_INCLUDED //============================================================================== @@ -140,7 +137,7 @@ public: //============================================================================== - /** This callback changes the given co-ordinates to impose whatever the current + /** This callback changes the given coordinates to impose whatever the current constraints are set to be. @param bounds the target position that should be examined and adjusted @@ -193,8 +190,8 @@ private: int minOffTop, minOffLeft, minOffBottom, minOffRight; double aspectRatio; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBoundsConstrainer); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentBoundsConstrainer) }; -#endif // __JUCE_COMPONENTBOUNDSCONSTRAINER_JUCEHEADER__ +#endif // JUCE_COMPONENTBOUNDSCONSTRAINER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp index c1926f6..82519ea 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentBuilder.cpp @@ -1,24 +1,23 @@ /* ============================================================================== - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-11 by Raw Material Software Ltd. + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. - ------------------------------------------------------------------------------ + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 - JUCE can be redistributed and/or modified under the terms of the GNU General - Public License (Version 2), as published by the Free Software Foundation. - A copy of the license is included in the JUCE distribution, or can be found - online at www.gnu.org/licenses. + Details of these licenses can be found at: www.gnu.org/licenses JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - ------------------------------------------------------------------------------ + ------------------------------------------------------------------------------ To release a closed-source product which uses JUCE, commercial licenses are - available: visit www.rawmaterialsoftware.com/juce for more information. + available: visit www.juce.com for more information. ============================================================================== */ @@ -52,12 +51,8 @@ namespace ComponentBuilderHelpers return &c; for (int i = c.getNumChildComponents(); --i >= 0;) - { - Component* const child = findComponentWithID (*c.getChildComponent (i), compId); - - if (child != nullptr) + if (Component* const child = findComponentWithID (*c.getChildComponent (i), compId)) return child; - } return nullptr; } @@ -73,9 +68,7 @@ namespace ComponentBuilderHelpers static void updateComponent (ComponentBuilder& builder, const ValueTree& state) { - Component* topLevelComp = builder.getManagedComponent(); - - if (topLevelComp != nullptr) + if (Component* topLevelComp = builder.getManagedComponent()) { ComponentBuilder::TypeHandler* const type = builder.getHandlerForState (state); const String uid (getStateId (state)); @@ -88,73 +81,15 @@ namespace ComponentBuilderHelpers } else { - Component* const changedComp = findComponentWithID (*topLevelComp, uid); - - if (changedComp != nullptr) + if (Component* const changedComp = findComponentWithID (*topLevelComp, uid)) type->updateComponentFromState (changedComp, state); } } } - - static void updateComponentColours (Component& component, const ValueTree& colourState) - { - NamedValueSet& properties = component.getProperties(); - - for (int i = properties.size(); --i >= 0;) - { - const Identifier name (properties.getName (i)); - - if (name.toString().startsWith ("jcclr_")) - { - const String colourName (name.toString().substring (6)); - - if (colourState [colourName].isVoid()) - component.removeColour (colourName.getHexValue32()); - } - } - - for (int i = 0; i < colourState.getNumProperties(); ++i) - { - const Identifier colourName (colourState.getPropertyName (i)); - const String colour (colourState [colourName].toString()); - - if (colour.isNotEmpty()) - component.setColour (colourName.toString().getHexValue32(), Colour::fromString (colour)); - } - } - - template - class StandardTypeHandler : public ComponentBuilder::TypeHandler - { - public: - StandardTypeHandler() : ComponentBuilder::TypeHandler (ComponentClass::Ids::tagType) - {} - - Component* addNewComponentFromState (const ValueTree& state, Component* parent) - { - ComponentClass* const c = new ComponentClass(); - - if (parent != nullptr) - parent->addAndMakeVisible (c); - - updateComponentFromState (c, state); - return c; - } - - void updateComponentFromState (Component* component, const ValueTree& state) - { - ComponentClass* const c = dynamic_cast (component); - jassert (c != nullptr); - - c->setComponentID (state [ComponentBuilder::idProperty]); - c->refreshFromValueTree (state, *this->getBuilder()); - } - }; } //============================================================================= const Identifier ComponentBuilder::idProperty ("id"); -const Identifier ComponentBuilder::positionID ("position"); ComponentBuilder::ComponentBuilder() : imageProvider (nullptr) @@ -196,10 +131,11 @@ Component* ComponentBuilder::createComponent() { jassert (types.size() > 0); // You need to register all the necessary types before you can load a component! - TypeHandler* const type = getHandlerForState (state); - jassert (type != nullptr); // trying to create a component from an unknown type of ValueTree + if (TypeHandler* const type = getHandlerForState (state)) + return ComponentBuilderHelpers::createNewComponent (*type, state, nullptr); - return type != nullptr ? ComponentBuilderHelpers::createNewComponent (*type, state, nullptr) : nullptr; + jassertfalse; // trying to create a component from an unknown type of ValueTree + return nullptr; } void ComponentBuilder::registerTypeHandler (ComponentBuilder::TypeHandler* const type) @@ -242,18 +178,6 @@ ComponentBuilder::TypeHandler* ComponentBuilder::getHandler (const int index) co void ComponentBuilder::registerStandardComponentTypes() { Drawable::registerDrawableTypeHandlers (*this); - - registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler ()); - registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler ()); - registerTypeHandler (new ComponentBuilderHelpers::StandardTypeHandler