Browse Source

Delete the rest of the juce modules

tags/1.9.8
falkTX 7 years ago
parent
commit
3f1c018bcc
100 changed files with 0 additions and 23457 deletions
  1. +0
    -119
      source/modules/juce_data_structures/Makefile
  2. +0
    -109
      source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp
  3. +0
    -132
      source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h
  4. +0
    -363
      source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp
  5. +0
    -252
      source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h
  6. +0
    -44
      source/modules/juce_data_structures/juce_data_structures.cpp
  7. +0
    -64
      source/modules/juce_data_structures/juce_data_structures.h
  8. +0
    -352
      source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp
  9. +0
    -246
      source/modules/juce_data_structures/undomanager/juce_UndoManager.h
  10. +0
    -99
      source/modules/juce_data_structures/undomanager/juce_UndoableAction.h
  11. +0
    -159
      source/modules/juce_data_structures/values/juce_CachedValue.cpp
  12. +0
    -312
      source/modules/juce_data_structures/values/juce_CachedValue.h
  13. +0
    -242
      source/modules/juce_data_structures/values/juce_Value.cpp
  14. +0
    -243
      source/modules/juce_data_structures/values/juce_Value.h
  15. +0
    -1151
      source/modules/juce_data_structures/values/juce_ValueTree.cpp
  16. +0
    -578
      source/modules/juce_data_structures/values/juce_ValueTree.h
  17. +0
    -242
      source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp
  18. +0
    -97
      source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h
  19. +0
    -123
      source/modules/juce_events/Makefile
  20. +0
    -92
      source/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp
  21. +0
    -77
      source/modules/juce_events/broadcasters/juce_ActionBroadcaster.h
  22. +0
    -46
      source/modules/juce_events/broadcasters/juce_ActionListener.h
  23. +0
    -93
      source/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp
  24. +0
    -108
      source/modules/juce_events/broadcasters/juce_AsyncUpdater.h
  25. +0
    -99
      source/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp
  26. +0
    -101
      source/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h
  27. +0
    -61
      source/modules/juce_events/broadcasters/juce_ChangeListener.h
  28. +0
    -267
      source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp
  29. +0
    -189
      source/modules/juce_events/interprocess/juce_ConnectedChildProcess.h
  30. +0
    -362
      source/modules/juce_events/interprocess/juce_InterprocessConnection.cpp
  31. +0
    -207
      source/modules/juce_events/interprocess/juce_InterprocessConnection.h
  32. +0
    -81
      source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp
  33. +0
    -104
      source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h
  34. +0
    -102
      source/modules/juce_events/juce_events.cpp
  35. +0
    -96
      source/modules/juce_events/juce_events.h
  36. +0
    -333
      source/modules/juce_events/messages/juce_ApplicationBase.cpp
  37. +0
    -312
      source/modules/juce_events/messages/juce_ApplicationBase.h
  38. +0
    -72
      source/modules/juce_events/messages/juce_CallbackMessage.h
  39. +0
    -94
      source/modules/juce_events/messages/juce_DeletedAtShutdown.cpp
  40. +0
    -63
      source/modules/juce_events/messages/juce_DeletedAtShutdown.h
  41. +0
    -200
      source/modules/juce_events/messages/juce_Initialisation.h
  42. +0
    -62
      source/modules/juce_events/messages/juce_Message.h
  43. +0
    -52
      source/modules/juce_events/messages/juce_MessageListener.cpp
  44. +0
    -68
      source/modules/juce_events/messages/juce_MessageListener.h
  45. +0
    -387
      source/modules/juce_events/messages/juce_MessageManager.cpp
  46. +0
    -381
      source/modules/juce_events/messages/juce_MessageManager.h
  47. +0
    -57
      source/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h
  48. +0
    -39
      source/modules/juce_events/messages/juce_NotificationType.h
  49. +0
    -147
      source/modules/juce_events/native/juce_android_Messaging.cpp
  50. +0
    -103
      source/modules/juce_events/native/juce_ios_MessageManager.mm
  51. +0
    -55
      source/modules/juce_events/native/juce_linux_EventLoop.h
  52. +0
    -265
      source/modules/juce_events/native/juce_linux_Messaging.cpp
  53. +0
    -430
      source/modules/juce_events/native/juce_mac_MessageManager.mm
  54. +0
    -105
      source/modules/juce_events/native/juce_osx_MessageQueue.h
  55. +0
    -135
      source/modules/juce_events/native/juce_win32_HiddenMessageWindow.h
  56. +0
    -232
      source/modules/juce_events/native/juce_win32_Messaging.cpp
  57. +0
    -26
      source/modules/juce_events/native/juce_win32_WinRTWrapper.cpp
  58. +0
    -131
      source/modules/juce_events/native/juce_win32_WinRTWrapper.h
  59. +0
    -108
      source/modules/juce_events/timers/juce_MultiTimer.cpp
  60. +0
    -123
      source/modules/juce_events/timers/juce_MultiTimer.h
  61. +0
    -371
      source/modules/juce_events/timers/juce_Timer.cpp
  62. +0
    -135
      source/modules/juce_events/timers/juce_Timer.h
  63. +0
    -123
      source/modules/juce_graphics/Makefile
  64. +0
    -468
      source/modules/juce_graphics/colour/juce_Colour.cpp
  65. +0
    -367
      source/modules/juce_graphics/colour/juce_Colour.h
  66. +0
    -244
      source/modules/juce_graphics/colour/juce_ColourGradient.cpp
  67. +0
    -202
      source/modules/juce_graphics/colour/juce_ColourGradient.h
  68. +0
    -335
      source/modules/juce_graphics/colour/juce_Colours.cpp
  69. +0
    -109
      source/modules/juce_graphics/colour/juce_Colours.h
  70. +0
    -153
      source/modules/juce_graphics/colour/juce_FillType.cpp
  71. +0
    -150
      source/modules/juce_graphics/colour/juce_FillType.h
  72. +0
    -757
      source/modules/juce_graphics/colour/juce_PixelFormats.h
  73. +0
    -699
      source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp
  74. +0
    -746
      source/modules/juce_graphics/contexts/juce_GraphicsContext.h
  75. +0
    -101
      source/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h
  76. +0
    -540
      source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp
  77. +0
    -120
      source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h
  78. +0
    -45
      source/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp
  79. +0
    -56
      source/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h
  80. +0
    -189
      source/modules/juce_graphics/effects/juce_DropShadowEffect.cpp
  81. +0
    -110
      source/modules/juce_graphics/effects/juce_DropShadowEffect.h
  82. +0
    -58
      source/modules/juce_graphics/effects/juce_GlowEffect.cpp
  83. +0
    -75
      source/modules/juce_graphics/effects/juce_GlowEffect.h
  84. +0
    -70
      source/modules/juce_graphics/effects/juce_ImageEffectFilter.h
  85. +0
    -356
      source/modules/juce_graphics/fonts/juce_AttributedString.cpp
  86. +0
    -207
      source/modules/juce_graphics/fonts/juce_AttributedString.h
  87. +0
    -413
      source/modules/juce_graphics/fonts/juce_CustomTypeface.cpp
  88. +0
    -165
      source/modules/juce_graphics/fonts/juce_CustomTypeface.h
  89. +0
    -722
      source/modules/juce_graphics/fonts/juce_Font.cpp
  90. +0
    -478
      source/modules/juce_graphics/fonts/juce_Font.h
  91. +0
    -820
      source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp
  92. +0
    -327
      source/modules/juce_graphics/fonts/juce_GlyphArrangement.h
  93. +0
    -592
      source/modules/juce_graphics/fonts/juce_TextLayout.cpp
  94. +0
    -195
      source/modules/juce_graphics/fonts/juce_TextLayout.h
  95. +0
    -267
      source/modules/juce_graphics/fonts/juce_Typeface.cpp
  96. +0
    -161
      source/modules/juce_graphics/fonts/juce_Typeface.h
  97. +0
    -269
      source/modules/juce_graphics/geometry/juce_AffineTransform.cpp
  98. +0
    -279
      source/modules/juce_graphics/geometry/juce_AffineTransform.h
  99. +0
    -153
      source/modules/juce_graphics/geometry/juce_BorderSize.h
  100. +0
    -838
      source/modules/juce_graphics/geometry/juce_EdgeTable.cpp

+ 0
- 119
source/modules/juce_data_structures/Makefile View File

@@ -1,119 +0,0 @@
#!/usr/bin/make -f
# Makefile for juce_data_structures #
# --------------------------------- #
# Created by falkTX
#

CWD=../..
MODULENAME=juce_data_structures
include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(JUCE_DATA_STRUCTURES_FLAGS) -I..

# ----------------------------------------------------------------------------------------------------------------------------

ifeq ($(MACOS),true)
OBJS = $(OBJDIR)/$(MODULENAME).mm.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).mm.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).mm.posix64.o
else
OBJS = $(OBJDIR)/$(MODULENAME).cpp.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).cpp.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).cpp.posix64.o
endif
OBJS_win32 = $(OBJDIR)/$(MODULENAME).cpp.win32.o
OBJS_win64 = $(OBJDIR)/$(MODULENAME).cpp.win64.o

# ----------------------------------------------------------------------------------------------------------------------------

all: $(MODULEDIR)/$(MODULENAME).a
posix32: $(MODULEDIR)/$(MODULENAME).posix32.a
posix64: $(MODULEDIR)/$(MODULENAME).posix64.a
win32: $(MODULEDIR)/$(MODULENAME).win32.a
win64: $(MODULEDIR)/$(MODULENAME).win64.a

# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a

debug:
$(MAKE) DEBUG=true

# ----------------------------------------------------------------------------------------------------------------------------

$(MODULEDIR)/$(MODULENAME).a: $(OBJS)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix64.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win64.a"
@rm -f $@
@$(AR) crs $@ $^

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).cpp.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).mm.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -ObjC++ -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

-include $(OBJS:%.o=%.d)
-include $(OBJS_posix32:%.o=%.d)
-include $(OBJS_posix64:%.o=%.d)
-include $(OBJS_win32:%.o=%.d)
-include $(OBJS_win64:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------------

+ 0
- 109
source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.cpp View File

@@ -1,109 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
ApplicationProperties::ApplicationProperties()
: commonSettingsAreReadOnly (0)
{
}
ApplicationProperties::~ApplicationProperties()
{
closeFiles();
}
//==============================================================================
void ApplicationProperties::setStorageParameters (const PropertiesFile::Options& newOptions)
{
options = newOptions;
}
//==============================================================================
void ApplicationProperties::openFiles()
{
// You need to call setStorageParameters() before trying to get hold of the properties!
jassert (options.applicationName.isNotEmpty());
if (options.applicationName.isNotEmpty())
{
PropertiesFile::Options o (options);
if (userProps == nullptr)
{
o.commonToAllUsers = false;
userProps = new PropertiesFile (o);
}
if (commonProps == nullptr)
{
o.commonToAllUsers = true;
commonProps = new PropertiesFile (o);
}
userProps->setFallbackPropertySet (commonProps);
}
}
PropertiesFile* ApplicationProperties::getUserSettings()
{
if (userProps == nullptr)
openFiles();
return userProps;
}
PropertiesFile* ApplicationProperties::getCommonSettings (const bool returnUserPropsIfReadOnly)
{
if (commonProps == nullptr)
openFiles();
if (returnUserPropsIfReadOnly)
{
if (commonSettingsAreReadOnly == 0)
commonSettingsAreReadOnly = commonProps->save() ? -1 : 1;
if (commonSettingsAreReadOnly > 0)
return userProps;
}
return commonProps;
}
bool ApplicationProperties::saveIfNeeded()
{
return (userProps == nullptr || userProps->saveIfNeeded())
&& (commonProps == nullptr || commonProps->saveIfNeeded());
}
void ApplicationProperties::closeFiles()
{
userProps = nullptr;
commonProps = nullptr;
}
} // namespace juce

+ 0
- 132
source/modules/juce_data_structures/app_properties/juce_ApplicationProperties.h View File

@@ -1,132 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Manages a collection of properties.
This is a slightly higher-level wrapper for managing PropertiesFile objects.
It holds two different PropertiesFile objects internally, one for user-specific
settings (stored in your user directory), and one for settings that are common to
all users (stored in a folder accessible to all users).
The class manages the creation of these files on-demand, allowing access via the
getUserSettings() and getCommonSettings() methods.
After creating an instance of an ApplicationProperties object, you should first
of all call setStorageParameters() to tell it the parameters to use to create
its files.
@see PropertiesFile
*/
class JUCE_API ApplicationProperties
{
public:
//==============================================================================
/**
Creates an ApplicationProperties object.
Before using it, you must call setStorageParameters() to give it the info
it needs to create the property files.
*/
ApplicationProperties();
/** Destructor. */
~ApplicationProperties();
//==============================================================================
/** Gives the object the information it needs to create the appropriate properties files.
See the PropertiesFile::Options class for details about what options you need to set.
*/
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.
The first time this is called, it will create and load the properties file.
Note that when you search the user PropertiesFile for a value that it doesn't contain,
the common settings are used as a second-chance place to look. This is done via the
PropertySet::setFallbackPropertySet() method - by default the common settings are set
to the fallback for the user settings.
@see getCommonSettings
*/
PropertiesFile* getUserSettings();
/** Returns the common settings file.
The first time this is called, it will create and load the properties file.
@param returnUserPropsIfReadOnly if this is true, and the common properties file is
read-only (e.g. because the user doesn't have permission to write
to shared files), then this will return the user settings instead,
(like getUserSettings() would do). This is handy if you'd like to
write a value to the common settings, but if that's no possible,
then you'd rather write to the user settings than none at all.
If returnUserPropsIfReadOnly is false, this method will always return
the common settings, even if any changes to them can't be saved.
@see getUserSettings
*/
PropertiesFile* getCommonSettings (bool returnUserPropsIfReadOnly);
//==============================================================================
/** Saves both files if they need to be saved.
@see PropertiesFile::saveIfNeeded
*/
bool saveIfNeeded();
/** Flushes and closes both files if they are open.
This flushes any pending changes to disk with PropertiesFile::saveIfNeeded()
and closes both files. They will then be re-opened the next time getUserSettings()
or getCommonSettings() is called.
*/
void closeFiles();
private:
//==============================================================================
PropertiesFile::Options options;
ScopedPointer<PropertiesFile> userProps, commonProps;
int commonSettingsAreReadOnly;
void openFiles();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationProperties)
};
} // namespace juce

+ 0
- 363
source/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp View File

@@ -1,363 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace PropertyFileConstants
{
JUCE_CONSTEXPR static const int magicNumber = (int) ByteOrder::littleEndianInt ('P', 'R', 'O', 'P');
JUCE_CONSTEXPR static const int magicNumberCompressed = (int) ByteOrder::littleEndianInt ('C', 'P', 'R', 'P');
JUCE_CONSTEXPR static const char* const fileTag = "PROPERTIES";
JUCE_CONSTEXPR static const char* const valueTag = "VALUE";
JUCE_CONSTEXPR static const char* const nameAttribute = "name";
JUCE_CONSTEXPR static const char* const valueAttribute = "val";
}
//==============================================================================
PropertiesFile::Options::Options()
: commonToAllUsers (false),
ignoreCaseOfKeyNames (false),
doNotSave (false),
millisecondsBeforeSaving (3000),
storageFormat (PropertiesFile::storeAsXML),
processLock (nullptr)
{
}
File PropertiesFile::Options::getDefaultFile() const
{
// mustn't have illegal characters in this name..
jassert (applicationName == File::createLegalFileName (applicationName));
#if JUCE_MAC || JUCE_IOS
File dir (commonToAllUsers ? "/Library/"
: "~/Library/");
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".
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"
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".
But.. for better Apple-compliance, the recommended approach would be to write some code that
finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
and then uses the new path.
*/
jassertfalse;
dir = dir.getChildFile ("Application Support");
}
else
{
dir = dir.getChildFile (osxLibrarySubFolder);
}
if (folderName.isNotEmpty())
dir = dir.getChildFile (folderName);
#elif JUCE_LINUX || JUCE_ANDROID
const File dir (File (commonToAllUsers ? "/var" : "~")
.getChildFile (folderName.isNotEmpty() ? folderName
: ("." + applicationName)));
#elif JUCE_WINDOWS
File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory
: File::userApplicationDataDirectory));
if (dir == File())
return {};
dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
: applicationName);
#endif
return (filenameSuffix.startsWithChar (L'.')
? dir.getChildFile (applicationName).withFileExtension (filenameSuffix)
: dir.getChildFile (applicationName + "." + filenameSuffix));
}
//==============================================================================
PropertiesFile::PropertiesFile (const File& f, const Options& o)
: PropertySet (o.ignoreCaseOfKeyNames),
file (f), options (o),
loadedOk (false), needsWriting (false)
{
reload();
}
PropertiesFile::PropertiesFile (const Options& o)
: PropertySet (o.ignoreCaseOfKeyNames),
file (o.getDefaultFile()), options (o),
loadedOk (false), needsWriting (false)
{
reload();
}
bool PropertiesFile::reload()
{
ProcessScopedLock pl (createProcessLock());
if (pl != nullptr && ! pl->isLocked())
return false; // locking failure..
loadedOk = (! file.exists()) || loadAsBinary() || loadAsXml();
return loadedOk;
}
PropertiesFile::~PropertiesFile()
{
saveIfNeeded();
}
InterProcessLock::ScopedLockType* PropertiesFile::createProcessLock() const
{
return options.processLock != nullptr ? new InterProcessLock::ScopedLockType (*options.processLock) : nullptr;
}
bool PropertiesFile::saveIfNeeded()
{
const ScopedLock sl (getLock());
return (! needsWriting) || save();
}
bool PropertiesFile::needsToBeSaved() const
{
const ScopedLock sl (getLock());
return needsWriting;
}
void PropertiesFile::setNeedsToBeSaved (const bool needsToBeSaved_)
{
const ScopedLock sl (getLock());
needsWriting = needsToBeSaved_;
}
bool PropertiesFile::save()
{
const ScopedLock sl (getLock());
stopTimer();
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<XmlElement> doc (parser.getDocumentElement (true));
if (doc != nullptr && doc->hasTagName (PropertyFileConstants::fileTag))
{
doc = parser.getDocumentElement();
if (doc != nullptr)
{
forEachXmlChildElementWithTagName (*doc, e, PropertyFileConstants::valueTag)
{
const String name (e->getStringAttribute (PropertyFileConstants::nameAttribute));
if (name.isNotEmpty())
{
getAllProperties().set (name,
e->getFirstChildElement() != nullptr
? e->getFirstChildElement()->createDocument ("", true)
: e->getStringAttribute (PropertyFileConstants::valueAttribute));
}
}
return true;
}
// 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.
}
return false;
}
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<OutputStream> 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;
}
}
return false;
}
void PropertiesFile::timerCallback()
{
saveIfNeeded();
}
void PropertiesFile::propertyChanged()
{
sendChangeMessage();
needsWriting = true;
if (options.millisecondsBeforeSaving > 0)
startTimer (options.millisecondsBeforeSaving);
else if (options.millisecondsBeforeSaving == 0)
saveIfNeeded();
}
} // namespace juce

+ 0
- 252
source/modules/juce_data_structures/app_properties/juce_PropertiesFile.h View File

@@ -1,252 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** Wrapper on a file that stores a list of key/value data pairs.
Useful for storing application settings, etc. See the PropertySet class for
the interfaces that read and write values.
Not designed for very large amounts of data, as it keeps all the values in
memory and writes them out to disk lazily when they are changed.
Because this class derives from ChangeBroadcaster, ChangeListeners can be registered
with it, and these will be signalled when a value changes.
@see PropertySet
*/
class JUCE_API PropertiesFile : public PropertySet,
public ChangeBroadcaster,
private Timer
{
public:
//==============================================================================
enum StorageFormat
{
storeAsBinary,
storeAsCompressedBinary,
storeAsXML
};
//==============================================================================
struct JUCE_API Options
{
/** Creates an empty Options structure.
You'll need to fill-in the data members appropriately before using this structure.
*/
Options();
/** The name of your application - this is used to help generate the path and filename
at which the properties file will be stored. */
String applicationName;
/** The suffix to use for your properties file.
It doesn't really matter what this is - you may want to use ".settings" or
".properties" or something. If the suffix includes the prefixing dot (for example
".settings") then the suffix of applicationName will be replaced with your suffix
("MyApp.exe" -> "MyApp.settings"). If your filenameSuffix does NOT include the dot,
then the suffix will be appended to the applicationName ("MyApp.exe" ->
"MyApp.exe.settings").
*/
String filenameSuffix;
/** The name of a subfolder in which you'd like your properties file to live.
See the getDefaultFile() method for more details about how this is used.
*/
String folderName;
/** If you're using properties files on a Mac, you must set this value - failure to
do so will cause a runtime assertion.
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".
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" 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".
But.. for better Apple-compliance, the recommended approach would be to write some code that
finds your old settings files in ~/Library/Preferences, moves them to ~/Library/Application Support,
and then uses the new path.
*/
String osxLibrarySubFolder;
/** If true, the file will be created in a location that's shared between users.
The default constructor initialises this value to false.
*/
bool commonToAllUsers;
/** If true, this means that property names are matched in a case-insensitive manner.
See the PropertySet constructor for more info.
The default constructor initialises this value to false.
*/
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
synchronously each time a value-change method is called). If it is less than zero,
the file won't be saved until save() or saveIfNeeded() are explicitly called.
The default constructor sets this to a reasonable value of a few seconds, so you
only need to change it if you need a special case.
*/
int millisecondsBeforeSaving;
/** Specifies whether the file should be written as XML, binary, etc.
The default constructor sets this to storeAsXML, so you only need to set it explicitly
if you want to use a different format.
*/
StorageFormat storageFormat;
/** An optional InterprocessLock object that will be used to prevent multiple threads or
processes from writing to the file at the same time. The PropertiesFile will keep a
pointer to this object but will not take ownership of it - the caller is responsible for
making sure that the lock doesn't get deleted before the PropertiesFile has been deleted.
The default constructor initialises this value to nullptr, so you don't need to touch it
unless you want to use a lock.
*/
InterProcessLock* processLock;
/** This can be called to suggest a file that should be used, based on the values
in this structure.
So on a Mac, this will return a file called:
~/Library/[osxLibrarySubFolder]/[folderName]/[applicationName].[filenameSuffix]
On Windows it'll return something like:
C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[filenameSuffix]
On Linux it'll return
~/[folderName]/[applicationName].[filenameSuffix]
If the folderName variable is empty, it'll use the app name for this (or omit the
folder name on the Mac).
The paths will also vary depending on whether commonToAllUsers is true.
*/
File getDefaultFile() const;
};
//==============================================================================
/** Creates a PropertiesFile object.
The file used will be chosen by calling PropertiesFile::Options::getDefaultFile()
for the options provided. To set the file explicitly, use the other constructor.
*/
explicit PropertiesFile (const Options& options);
/** Creates a PropertiesFile object.
Unlike the other constructor, this one allows you to explicitly set the file that you
want to be used, rather than using the default one.
*/
PropertiesFile (const File& file,
const Options& options);
/** Destructor.
When deleted, the file will first call saveIfNeeded() to flush any changes to disk.
*/
~PropertiesFile();
//==============================================================================
/** Returns true if this file was created from a valid (or non-existent) file.
If the file failed to load correctly because it was corrupt or had insufficient
access, this will be false.
*/
bool isValidFile() const noexcept { return loadedOk; }
//==============================================================================
/** This will flush all the values to disk if they've changed since the last
time they were saved.
Returns false if it fails to write to the file for some reason (maybe because
it's read-only or the directory doesn't exist or something).
@see save
*/
bool saveIfNeeded();
/** This will force a write-to-disk of the current values, regardless of whether
anything has changed since the last save.
Returns false if it fails to write to the file for some reason (maybe because
it's read-only or the directory doesn't exist or something).
@see saveIfNeeded
*/
bool save();
/** Returns true if the properties have been altered since the last time they were saved.
The file is flagged as needing to be saved when you change a value, but you can
explicitly set this flag with setNeedsToBeSaved().
*/
bool needsToBeSaved() const;
/** Explicitly sets the flag to indicate whether the file needs saving or not.
@see needsToBeSaved
*/
void setNeedsToBeSaved (bool needsToBeSaved);
/** Attempts to reload the settings from the file. */
bool reload();
//==============================================================================
/** Returns the file that's being used. */
const File& getFile() const noexcept { return file; }
protected:
/** @internal */
void propertyChanged() override;
private:
//==============================================================================
File file;
Options options;
bool loadedOk, needsWriting;
typedef const ScopedPointer<InterProcessLock::ScopedLockType> ProcessScopedLock;
InterProcessLock::ScopedLockType* createProcessLock() const;
void timerCallback() override;
bool saveAsXml();
bool saveAsBinary();
bool loadAsXml();
bool loadAsBinary();
bool loadAsBinary (InputStream&);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PropertiesFile)
};
} // namespace juce

+ 0
- 44
source/modules/juce_data_structures/juce_data_structures.cpp View File

@@ -1,44 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#ifdef JUCE_DATA_STRUCTURES_H_INCLUDED
/* 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
header files that the compiler may be using.
*/
#error "Incorrect use of JUCE cpp file"
#endif
#include "juce_data_structures.h"
#include "values/juce_Value.cpp"
#include "values/juce_ValueTree.cpp"
#include "values/juce_ValueTreeSynchroniser.cpp"
#include "values/juce_CachedValue.cpp"
#include "undomanager/juce_UndoManager.cpp"
#include "app_properties/juce_ApplicationProperties.cpp"
#include "app_properties/juce_PropertiesFile.cpp"

+ 0
- 64
source/modules/juce_data_structures/juce_data_structures.h View File

@@ -1,64 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.txt file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_data_structures
vendor: juce
version: 5.1.2
name: JUCE data model helper classes
description: Classes for undo/redo management, and smart data structures.
website: http://www.juce.com/juce
license: GPL/Commercial
dependencies: juce_events
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#pragma once
#define JUCE_DATA_STRUCTURES_H_INCLUDED
//==============================================================================
#include <juce_events/juce_events.h>
#include "undomanager/juce_UndoableAction.h"
#include "undomanager/juce_UndoManager.h"
#include "values/juce_Value.h"
#include "values/juce_ValueTree.h"
#include "values/juce_ValueTreeSynchroniser.h"
#include "values/juce_CachedValue.h"
#include "app_properties/juce_PropertiesFile.h"
#include "app_properties/juce_ApplicationProperties.h"

+ 0
- 352
source/modules/juce_data_structures/undomanager/juce_UndoManager.cpp View File

@@ -1,352 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct UndoManager::ActionSet
{
ActionSet (const String& transactionName)
: name (transactionName),
time (Time::getCurrentTime())
{}
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;
}
OwnedArray<UndoableAction> actions;
String name;
Time time;
};
//==============================================================================
UndoManager::UndoManager (const int maxNumberOfUnitsToKeep,
const int minimumTransactions)
: totalUnitsStored (0),
nextIndex (0),
newTransaction (true),
reentrancyCheck (false)
{
setMaxNumberOfStoredUnits (maxNumberOfUnitsToKeep,
minimumTransactions);
}
UndoManager::~UndoManager()
{
}
//==============================================================================
void UndoManager::clearUndoHistory()
{
transactions.clear();
totalUnitsStored = 0;
nextIndex = 0;
sendChangeMessage();
}
int UndoManager::getNumberOfUnitsTakenUpByStoredCommands() const
{
return totalUnitsStored;
}
void UndoManager::setMaxNumberOfStoredUnits (const int maxNumberOfUnitsToKeep,
const int minimumTransactions)
{
maxNumUnitsToKeep = jmax (1, maxNumberOfUnitsToKeep);
minimumTransactionsToKeep = jmax (1, minimumTransactions);
}
//==============================================================================
bool UndoManager::perform (UndoableAction* const newAction, const String& actionName)
{
if (perform (newAction))
{
if (actionName.isNotEmpty())
setCurrentTransactionName (actionName);
return true;
}
return false;
}
bool UndoManager::perform (UndoableAction* const newAction)
{
if (newAction != nullptr)
{
ScopedPointer<UndoableAction> 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 (action->perform())
{
ActionSet* actionSet = getCurrentSet();
if (actionSet != nullptr && ! newTransaction)
{
if (UndoableAction* const lastAction = actionSet->actions.getLast())
{
if (UndoableAction* const coalescedAction = lastAction->createCoalescedAction (action))
{
action = coalescedAction;
totalUnitsStored -= lastAction->getSizeInUnits();
actionSet->actions.removeLast();
}
}
}
else
{
actionSet = new ActionSet (newTransactionName);
transactions.insert (nextIndex, actionSet);
++nextIndex;
}
totalUnitsStored += action->getSizeInUnits();
actionSet->actions.add (action.release());
newTransaction = false;
moveFutureTransactionsToStash();
dropOldTransactionsIfTooLarge();
sendChangeMessage();
return true;
}
}
return false;
}
void UndoManager::moveFutureTransactionsToStash()
{
if (nextIndex < transactions.size())
{
stashedFutureTransactions.clear();
while (nextIndex < transactions.size())
{
ActionSet* removed = transactions.removeAndReturn (nextIndex);
stashedFutureTransactions.add (removed);
totalUnitsStored -= removed->getTotalSize();
}
}
}
void UndoManager::restoreStashedFutureTransactions()
{
while (nextIndex < transactions.size())
{
totalUnitsStored -= transactions.getUnchecked (nextIndex)->getTotalSize();
transactions.remove (nextIndex);
}
for (int i = 0; i < stashedFutureTransactions.size(); ++i)
{
ActionSet* action = stashedFutureTransactions.removeAndReturn (i);
totalUnitsStored += action->getTotalSize();
transactions.add (action);
}
stashedFutureTransactions.clearQuick (false);
}
void UndoManager::dropOldTransactionsIfTooLarge()
{
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() noexcept
{
beginNewTransaction (String());
}
void UndoManager::beginNewTransaction (const String& actionName) noexcept
{
newTransaction = true;
newTransactionName = actionName;
}
void UndoManager::setCurrentTransactionName (const String& newName) noexcept
{
if (newTransaction)
newTransactionName = newName;
else if (ActionSet* action = getCurrentSet())
action->name = newName;
}
String UndoManager::getCurrentTransactionName() const noexcept
{
if (ActionSet* action = getCurrentSet())
return action->name;
return newTransactionName;
}
//==============================================================================
UndoManager::ActionSet* UndoManager::getCurrentSet() const noexcept { return transactions [nextIndex - 1]; }
UndoManager::ActionSet* UndoManager::getNextSet() const noexcept { return transactions [nextIndex]; }
bool UndoManager::canUndo() const noexcept { return getCurrentSet() != nullptr; }
bool UndoManager::canRedo() const noexcept { return getNextSet() != nullptr; }
bool UndoManager::undo()
{
if (const ActionSet* const s = getCurrentSet())
{
const ScopedValueSetter<bool> setter (reentrancyCheck, true);
if (s->undo())
--nextIndex;
else
clearUndoHistory();
beginNewTransaction();
sendChangeMessage();
return true;
}
return false;
}
bool UndoManager::redo()
{
if (const ActionSet* const s = getNextSet())
{
const ScopedValueSetter<bool> setter (reentrancyCheck, true);
if (s->perform())
++nextIndex;
else
clearUndoHistory();
beginNewTransaction();
sendChangeMessage();
return true;
}
return false;
}
String UndoManager::getUndoDescription() const
{
if (auto* s = getCurrentSet())
return s->name;
return {};
}
String UndoManager::getRedoDescription() const
{
if (auto* s = getNextSet())
return s->name;
return {};
}
Time UndoManager::getTimeOfUndoTransaction() const
{
if (auto* s = getCurrentSet())
return s->time;
return {};
}
Time UndoManager::getTimeOfRedoTransaction() const
{
if (auto* s = getNextSet())
return s->time;
return Time::getCurrentTime();
}
bool UndoManager::undoCurrentTransactionOnly()
{
if ((! newTransaction) && undo())
{
restoreStashedFutureTransactions();
return true;
}
return false;
}
void UndoManager::getActionsInCurrentTransaction (Array<const UndoableAction*>& actionsFound) const
{
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
{
if (! newTransaction)
if (const ActionSet* const s = getCurrentSet())
return s->actions.size();
return 0;
}
} // namespace juce

+ 0
- 246
source/modules/juce_data_structures/undomanager/juce_UndoManager.h View File

@@ -1,246 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Manages a list of undo/redo commands.
An UndoManager object keeps a list of past actions and can use these actions
to move backwards and forwards through an undo history.
To use it, create subclasses of UndoableAction which perform all the
actions you need, then when you need to actually perform an action, create one
and pass it to the UndoManager's perform() method.
The manager also uses the concept of 'transactions' to group the actions
together - all actions performed between calls to beginNewTransaction() are
grouped together and are all undone/redone as a group.
The UndoManager is a ChangeBroadcaster, so listeners can register to be told
when actions are performed or undone.
@see UndoableAction
*/
class JUCE_API UndoManager : public ChangeBroadcaster
{
public:
//==============================================================================
/** Creates an UndoManager.
@param maxNumberOfUnitsToKeep each UndoableAction object returns a value
to indicate how much storage it takes up
(UndoableAction::getSizeInUnits()), so this
lets you specify the maximum total number of
units that the undomanager is allowed to
keep in memory before letting the older actions
drop off the end of the list.
@param minimumTransactionsToKeep this specifies the minimum number of transactions
that will be kept, even if this involves exceeding
the amount of space specified in maxNumberOfUnitsToKeep
*/
UndoManager (int maxNumberOfUnitsToKeep = 30000,
int minimumTransactionsToKeep = 30);
/** Destructor. */
~UndoManager();
//==============================================================================
/** Deletes all stored actions in the list. */
void clearUndoHistory();
/** Returns the current amount of space to use for storing UndoableAction objects.
@see setMaxNumberOfStoredUnits
*/
int getNumberOfUnitsTakenUpByStoredCommands() const;
/** Sets the amount of space that can be used for storing UndoableAction objects.
@param maxNumberOfUnitsToKeep each UndoableAction object returns a value
to indicate how much storage it takes up
(UndoableAction::getSizeInUnits()), so this
lets you specify the maximum total number of
units that the undomanager is allowed to
keep in memory before letting the older actions
drop off the end of the list.
@param minimumTransactionsToKeep this specifies the minimum number of transactions
that will be kept, even if this involves exceeding
the amount of space specified in maxNumberOfUnitsToKeep
@see getNumberOfUnitsTakenUpByStoredCommands
*/
void setMaxNumberOfStoredUnits (int maxNumberOfUnitsToKeep,
int minimumTransactionsToKeep);
//==============================================================================
/** Performs an action and adds it to the undo history list.
@param action the action to perform - this object will be deleted by
the UndoManager when no longer needed
@returns true if the command succeeds - see UndoableAction::perform
@see beginNewTransaction
*/
bool perform (UndoableAction* action);
/** Performs an action and also gives it a name.
@param action the action to perform - this object will be deleted by
the UndoManager when no longer needed
@param actionName if this string is non-empty, the current transaction will be
given this name; if it's empty, the current transaction name will
be left unchanged. See setCurrentTransactionName()
@returns true if the command succeeds - see UndoableAction::perform
@see beginNewTransaction
*/
bool perform (UndoableAction* action, const String& actionName);
/** Starts a new group of actions that together will be treated as a single transaction.
All actions that are passed to the perform() method between calls to this
method are grouped together and undone/redone together by a single call to
undo() or redo().
*/
void beginNewTransaction() noexcept;
/** Starts a new group of actions that together will be treated as a single transaction.
All actions that are passed to the perform() method between calls to this
method are grouped together and undone/redone together by a single call to
undo() or redo().
@param actionName a description of the transaction that is about to be
performed
*/
void beginNewTransaction (const String& actionName) noexcept;
/** Changes the name stored for the current transaction.
Each transaction is given a name when the beginNewTransaction() method is
called, but this can be used to change that name without starting a new
transaction.
*/
void setCurrentTransactionName (const String& newName) noexcept;
/** Returns the name of the current transaction.
@see setCurrentTransactionName
*/
String getCurrentTransactionName() const noexcept;
//==============================================================================
/** Returns true if there's at least one action in the list to undo.
@see getUndoDescription, undo, canRedo
*/
bool canUndo() const noexcept;
/** Returns the name of the transaction that will be rolled-back when undo() is called.
@see undo
*/
String getUndoDescription() const;
/** Tries to roll-back the last transaction.
@returns true if the transaction can be undone, and false if it fails, or
if there aren't any transactions to undo
*/
bool undo();
/** Tries to roll-back any actions that were added to the current transaction.
This will perform an undo() only if there are some actions in the undo list
that were added after the last call to beginNewTransaction().
This is useful because it lets you call beginNewTransaction(), then
perform an operation which may or may not actually perform some actions, and
then call this method to get rid of any actions that might have been done
without it rolling back the previous transaction if nothing was actually
done.
@returns true if any actions were undone.
*/
bool undoCurrentTransactionOnly();
/** Returns a list of the UndoableAction objects that have been performed during the
transaction that is currently open.
Effectively, this is the list of actions that would be undone if undoCurrentTransactionOnly()
were to be called now.
The first item in the list is the earliest action performed.
*/
void getActionsInCurrentTransaction (Array<const UndoableAction*>& actionsFound) const;
/** Returns the number of UndoableAction objects that have been performed during the
transaction that is currently open.
@see getActionsInCurrentTransaction
*/
int getNumActionsInCurrentTransaction() const;
/** Returns 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 noexcept;
/** Returns the name of the transaction that will be redone when redo() is called.
@see redo
*/
String getRedoDescription() const;
/** Tries to redo the last transaction that was undone.
@returns true if the transaction can be redone, and false if it fails, or
if there aren't any transactions to redo
*/
bool redo();
private:
//==============================================================================
struct ActionSet;
friend struct ContainerDeletePolicy<ActionSet>;
OwnedArray<ActionSet> transactions, stashedFutureTransactions;
String newTransactionName;
int totalUnitsStored, maxNumUnitsToKeep, minimumTransactionsToKeep, nextIndex;
bool newTransaction, reentrancyCheck;
ActionSet* getCurrentSet() const noexcept;
ActionSet* getNextSet() const noexcept;
void moveFutureTransactionsToStash();
void restoreStashedFutureTransactions();
void dropOldTransactionsIfTooLarge();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UndoManager)
};
} // namespace juce

+ 0
- 99
source/modules/juce_data_structures/undomanager/juce_UndoableAction.h View File

@@ -1,99 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Used by the UndoManager class to store an action which can be done
and undone.
@see UndoManager
*/
class JUCE_API UndoableAction
{
protected:
/** Creates an action. */
UndoableAction() noexcept {}
public:
/** Destructor. */
virtual ~UndoableAction() {}
//==============================================================================
/** Overridden by a subclass to perform the action.
This method is called by the UndoManager, and shouldn't be used directly by
applications.
Be careful not to make any calls in a perform() method that could call
recursively back into the UndoManager::perform() method
@returns true if the action could be performed.
@see UndoManager::perform
*/
virtual bool perform() = 0;
/** Overridden by a subclass to undo the action.
This method is called by the UndoManager, and shouldn't be used directly by
applications.
Be careful not to make any calls in an undo() method that could call
recursively back into the UndoManager::perform() method
@returns true if the action could be undone without any errors.
@see UndoManager::perform
*/
virtual bool undo() = 0;
//==============================================================================
/** Returns a value to indicate how much memory this object takes up.
Because the UndoManager keeps a list of UndoableActions, this is used
to work out how much space each one will take up, so that the UndoManager
can work out how many to keep.
The default value returned here is 10 - units are arbitrary and
don't have to be accurate.
@see UndoManager::getNumberOfUnitsTakenUpByStoredCommands,
UndoManager::setMaxNumberOfStoredUnits
*/
virtual int getSizeInUnits() { return 10; }
/** Allows multiple actions to be coalesced into a single action object, to reduce storage space.
If possible, this method should create and return a single action that does the same job as
this one followed by the supplied action.
If it's not possible to merge the two actions, the method should return a nullptr.
*/
virtual UndoableAction* createCoalescedAction (UndoableAction* nextAction) { ignoreUnused (nextAction); return nullptr; }
};
} // namespace juce

+ 0
- 159
source/modules/juce_data_structures/values/juce_CachedValue.cpp View File

@@ -1,159 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_UNIT_TESTS
class CachedValueTests : public UnitTest
{
public:
CachedValueTests() : UnitTest ("CachedValues", "Values") {}
void runTest() override
{
beginTest ("default constructor");
{
CachedValue<String> cv;
expect (cv.isUsingDefault());
expect (cv.get() == String());
}
beginTest ("without default value");
{
ValueTree t ("root");
t.setProperty ("testkey", "testvalue", nullptr);
CachedValue<String> cv (t, "testkey", nullptr);
expect (! cv.isUsingDefault());
expect (cv.get() == "testvalue");
cv.resetToDefault();
expect (cv.isUsingDefault());
expect (cv.get() == String());
}
beginTest ("with default value");
{
ValueTree t ("root");
t.setProperty ("testkey", "testvalue", nullptr);
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
expect (! cv.isUsingDefault());
expect (cv.get() == "testvalue");
cv.resetToDefault();
expect (cv.isUsingDefault());
expect (cv.get() == "defaultvalue");
}
beginTest ("with default value (int)");
{
ValueTree t ("root");
t.setProperty ("testkey", 23, nullptr);
CachedValue<int> cv (t, "testkey", nullptr, 34);
expect (! cv.isUsingDefault());
expect (cv == 23);
expectEquals (cv.get(), 23);
cv.resetToDefault();
expect (cv.isUsingDefault());
expect (cv == 34);
}
beginTest ("with void value");
{
ValueTree t ("root");
t.setProperty ("testkey", var(), nullptr);
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
expect (! cv.isUsingDefault());
expect (cv == "");
expectEquals (cv.get(), String());
}
beginTest ("with non-existent value");
{
ValueTree t ("root");
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
expect (cv.isUsingDefault());
expect (cv == "defaultvalue");
expect (cv.get() == "defaultvalue");
}
beginTest ("with value changing");
{
ValueTree t ("root");
t.setProperty ("testkey", "oldvalue", nullptr);
CachedValue<String> cv (t, "testkey", nullptr, "defaultvalue");
expect (cv == "oldvalue");
t.setProperty ("testkey", "newvalue", nullptr);
expect (cv != "oldvalue");
expect (cv == "newvalue");
}
beginTest ("set value");
{
ValueTree t ("root");
t.setProperty ("testkey", 23, nullptr);
CachedValue<int> cv (t, "testkey", nullptr, 45);
cv = 34;
expectEquals ((int) t["testkey"], 34);
cv.resetToDefault();
expect (cv == 45);
expectEquals (cv.get(), 45);
expect (t["testkey"] == var());
}
beginTest ("reset value");
{
}
}
};
static CachedValueTests cachedValueTests;
#endif
} // namespace juce

+ 0
- 312
source/modules/juce_data_structures/values/juce_CachedValue.h View File

@@ -1,312 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
This class acts as a typed wrapper around a property inside a ValueTree.
A CachedValue provides an easy way to read and write a ValueTree property with
a chosen type. So for example a CachedValue<int> allows you to read or write the
property as an int, and a CachedValue<String> lets you work with it as a String.
It also allows efficient access to the value, by caching a copy of it in the
type that is being used.
You can give the CachedValue an optional UndoManager which it will use when writing
to the underlying ValueTree.
If the property inside the ValueTree is missing, the CachedValue will automatically
return an optional default value, which can be specified when initialising the CachedValue.
To create one, you can either use the constructor to attach the CachedValue to a
ValueTree, or can create an uninitialised CachedValue with its default constructor and
then attach it later with the referTo() methods.
Common types like String, int, double which can be easily converted to a var should work
out-of-the-box, but if you want to use more complex custom types, you may need to implement
some template specialisations of VariantConverter which this class uses to convert between
the type and the ValueTree's internal var.
*/
template <typename Type>
class CachedValue : private ValueTree::Listener
{
public:
//==============================================================================
/** Default constructor.
Creates a default CachedValue not referring to any property. To initialise the
object, call one of the referTo() methods.
*/
CachedValue();
/** Constructor.
Creates a CachedValue referring to a Value property inside a ValueTree.
If you use this constructor, the fallback value will be a default-constructed
instance of Type.
@param tree The ValueTree containing the property
@param propertyID The identifier of the property
@param undoManager The UndoManager to use when writing to the property
*/
CachedValue (ValueTree& tree, const Identifier& propertyID,
UndoManager* undoManager);
/** Constructor.
Creates a default Cached Value referring to a Value property inside a ValueTree,
and specifies a fallback value to use if the property does not exist.
@param tree The ValueTree containing the property
@param propertyID The identifier of the property
@param undoManager The UndoManager to use when writing to the property
@param defaultToUse The fallback default value to use.
*/
CachedValue (ValueTree& tree, const Identifier& propertyID,
UndoManager* undoManager, const Type& defaultToUse);
//==============================================================================
/** Returns the current value of the property. If the property does not exist,
returns the fallback default value.
This is the same as calling get().
*/
operator Type() const noexcept { return cachedValue; }
/** Returns the current value of the property. If the property does not exist,
returns the fallback default value.
*/
Type get() const noexcept { return cachedValue; }
/** Dereference operator. Provides direct access to the property. */
Type& operator*() noexcept { return cachedValue; }
/** Dereference operator. Provides direct access to members of the property
if it is of object type.
*/
Type* operator->() noexcept { return &cachedValue; }
/** Returns true if the current value of the property (or the fallback value)
is equal to other.
*/
template <typename OtherType>
bool operator== (const OtherType& other) const { return cachedValue == other; }
/** Returns true if the current value of the property (or the fallback value)
is not equal to other.
*/
template <typename OtherType>
bool operator!= (const OtherType& other) const { return cachedValue != other; }
//==============================================================================
/** Returns the current property as a Value object. */
Value getPropertyAsValue();
/** Returns true if the current property does not exist and the CachedValue is using
the fallback default value instead.
*/
bool isUsingDefault() const;
/** Returns the current fallback default value. */
Type getDefault() const { return defaultValue; }
//==============================================================================
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
CachedValue& operator= (const Type& newValue);
/** Sets the property. This will actually modify the property in the referenced ValueTree. */
void setValue (const Type& newValue, UndoManager* undoManagerToUse);
/** Removes the property from the referenced ValueTree and makes the CachedValue
return the fallback default value instead.
*/
void resetToDefault();
/** Removes the property from the referenced ValueTree and makes the CachedValue
return the fallback default value instead.
*/
void resetToDefault (UndoManager* undoManagerToUse);
/** Resets the fallback default value. */
void setDefault (const Type& value) { defaultValue = value; }
//==============================================================================
/** Makes the CachedValue refer to the specified property inside the given ValueTree. */
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um);
/** Makes the CachedValue refer to the specified property inside the given ValueTree,
and specifies a fallback value to use if the property does not exist.
*/
void referTo (ValueTree& tree, const Identifier& property, UndoManager* um, const Type& defaultVal);
/** Force an update in case the referenced property has been changed from elsewhere.
Note: The CachedValue is a ValueTree::Listener and therefore will be informed of
changes of the referenced property anyway (and update itself). But this may happen
asynchronously. forceUpdateOfCachedValue() forces an update immediately.
*/
void forceUpdateOfCachedValue();
//==============================================================================
/** Returns a reference to the ValueTree containing the referenced property. */
ValueTree& getValueTree() noexcept { return targetTree; }
/** Returns the property ID of the referenced property. */
const Identifier& getPropertyID() const noexcept { return targetProperty; }
private:
//==============================================================================
ValueTree targetTree;
Identifier targetProperty;
UndoManager* undoManager;
Type defaultValue;
Type cachedValue;
//==============================================================================
void referToWithDefault (ValueTree&, const Identifier&, UndoManager*, const Type&);
Type getTypedValue() const;
void valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty) override;
void valueTreeChildAdded (ValueTree&, ValueTree&) override {}
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override {}
void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
void valueTreeParentChanged (ValueTree&) override {}
JUCE_DECLARE_NON_COPYABLE (CachedValue)
};
//==============================================================================
template <typename Type>
inline CachedValue<Type>::CachedValue() : undoManager (nullptr) {}
template <typename Type>
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um)
: targetTree (v), targetProperty (i), undoManager (um),
defaultValue(), cachedValue (getTypedValue())
{
targetTree.addListener (this);
}
template <typename Type>
inline CachedValue<Type>::CachedValue (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultToUse)
: targetTree (v), targetProperty (i), undoManager (um),
defaultValue (defaultToUse), cachedValue (getTypedValue())
{
targetTree.addListener (this);
}
template <typename Type>
inline Value CachedValue<Type>::getPropertyAsValue()
{
return targetTree.getPropertyAsValue (targetProperty, undoManager);
}
template <typename Type>
inline bool CachedValue<Type>::isUsingDefault() const
{
return ! targetTree.hasProperty (targetProperty);
}
template <typename Type>
inline CachedValue<Type>& CachedValue<Type>::operator= (const Type& newValue)
{
setValue (newValue, undoManager);
return *this;
}
template <typename Type>
inline void CachedValue<Type>::setValue (const Type& newValue, UndoManager* undoManagerToUse)
{
if (cachedValue != newValue || isUsingDefault())
{
cachedValue = newValue;
targetTree.setProperty (targetProperty, VariantConverter<Type>::toVar (newValue), undoManagerToUse);
}
}
template <typename Type>
inline void CachedValue<Type>::resetToDefault()
{
resetToDefault (undoManager);
}
template <typename Type>
inline void CachedValue<Type>::resetToDefault (UndoManager* undoManagerToUse)
{
targetTree.removeProperty (targetProperty, undoManagerToUse);
forceUpdateOfCachedValue();
}
template <typename Type>
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um)
{
referToWithDefault (v, i, um, Type());
}
template <typename Type>
inline void CachedValue<Type>::referTo (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
{
referToWithDefault (v, i, um, defaultVal);
}
template <typename Type>
inline void CachedValue<Type>::forceUpdateOfCachedValue()
{
cachedValue = getTypedValue();
}
template <typename Type>
inline void CachedValue<Type>::referToWithDefault (ValueTree& v, const Identifier& i, UndoManager* um, const Type& defaultVal)
{
targetTree.removeListener (this);
targetTree = v;
targetProperty = i;
undoManager = um;
defaultValue = defaultVal;
cachedValue = getTypedValue();
targetTree.addListener (this);
}
template <typename Type>
inline Type CachedValue<Type>::getTypedValue() const
{
if (const var* property = targetTree.getPropertyPointer (targetProperty))
return VariantConverter<Type>::fromVar (*property);
return defaultValue;
}
template <typename Type>
inline void CachedValue<Type>::valueTreePropertyChanged (ValueTree& changedTree, const Identifier& changedProperty)
{
if (changedProperty == targetProperty && targetTree == changedTree)
forceUpdateOfCachedValue();
}
} // namespace juce

+ 0
- 242
source/modules/juce_data_structures/values/juce_Value.cpp View File

@@ -1,242 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
Value::ValueSource::ValueSource()
{
}
Value::ValueSource::~ValueSource()
{
cancelPendingUpdate();
}
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<ValueSource> localRef (this);
cancelPendingUpdate();
for (int i = numListeners; --i >= 0;)
if (Value* const v = valuesWithListeners[i])
v->callListeners();
}
else
{
triggerAsyncUpdate();
}
}
}
//==============================================================================
class SimpleValueSource : public Value::ValueSource
{
public:
SimpleValueSource()
{
}
SimpleValueSource (const var& initialValue)
: value (initialValue)
{
}
var getValue() const override
{
return value;
}
void setValue (const var& newValue) override
{
if (! newValue.equalsWithSameType (value))
{
value = newValue;
sendChangeMessage (false);
}
}
private:
var value;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SimpleValueSource)
};
//==============================================================================
Value::Value() : value (new SimpleValueSource())
{
}
Value::Value (ValueSource* const v) : value (v)
{
jassert (v != nullptr);
}
Value::Value (const var& initialValue) : value (new SimpleValueSource (initialValue))
{
}
Value::Value (const Value& other) : value (other.value)
{
}
Value::Value (Value&& other) noexcept
{
// moving a Value with listeners will lose those listeners, which
// probably isn't what you wanted to happen!
jassert (other.listeners.size() == 0);
other.removeFromListenerList();
value = static_cast<ReferenceCountedObjectPtr<ValueSource>&&> (other.value);
}
Value& Value::operator= (Value&& other) noexcept
{
// moving a Value with listeners will lose those listeners, which
// probably isn't what you wanted to happen!
jassert (other.listeners.size() == 0);
other.removeFromListenerList();
value = static_cast<ReferenceCountedObjectPtr<ValueSource>&&> (other.value);
return *this;
}
Value::~Value()
{
removeFromListenerList();
}
void Value::removeFromListenerList()
{
if (listeners.size() > 0 && value != nullptr) // may be nullptr after a move operation
value->valuesWithListeners.removeValue (this);
}
//==============================================================================
var Value::getValue() const
{
return value->getValue();
}
Value::operator var() const
{
return value->getValue();
}
void Value::setValue (const var& newValue)
{
value->setValue (newValue);
}
String Value::toString() const
{
return value->getValue().toString();
}
Value& Value::operator= (const var& newValue)
{
value->setValue (newValue);
return *this;
}
void Value::referTo (const Value& valueToReferTo)
{
if (valueToReferTo.value != value)
{
if (listeners.size() > 0)
{
value->valuesWithListeners.removeValue (this);
valueToReferTo.value->valuesWithListeners.add (this);
}
value = valueToReferTo.value;
callListeners();
}
}
bool Value::refersToSameSourceAs (const Value& other) const
{
return value == other.value;
}
bool Value::operator== (const Value& other) const
{
return value == other.value || value->getValue() == other.getValue();
}
bool Value::operator!= (const Value& other) const
{
return value != other.value && value->getValue() != other.getValue();
}
//==============================================================================
void Value::addListener (ValueListener* const listener)
{
if (listener != nullptr)
{
if (listeners.size() == 0)
value->valuesWithListeners.add (this);
listeners.add (listener);
}
}
void Value::removeListener (ValueListener* const listener)
{
listeners.remove (listener);
if (listeners.size() == 0)
value->valuesWithListeners.removeValue (this);
}
void Value::callListeners()
{
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)
{
return stream << value.toString();
}
} // namespace juce

+ 0
- 243
source/modules/juce_data_structures/values/juce_Value.h View File

@@ -1,243 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a shared variant value.
A Value object contains a reference to a var object, and can get and set its value.
Listeners can be attached to be told when the value is changed.
The Value class is a wrapper around a shared, reference-counted underlying data
object - this means that multiple Value objects can all refer to the same piece of
data, allowing all of them to be notified when any of them changes it.
When you create a Value with its default constructor, it acts as a wrapper around a
simple var object, but by creating a Value that refers to a custom subclass of ValueSource,
you can map the Value onto any kind of underlying data.
Important note! The Value class is not thread-safe! If you're accessing one from
multiple threads, then you'll need to use your own synchronisation around any code
that accesses it.
*/
class JUCE_API Value
{
public:
//==============================================================================
/** Creates an empty Value, containing a void var. */
Value();
/** Creates a Value that refers to the same value as another one.
Note that this doesn't make a copy of the other value - both this and the other
Value will share the same underlying value, so that when either one alters it, both
will see it change.
*/
Value (const Value& other);
/** Creates a Value that is set to the specified value. */
explicit Value (const var& initialValue);
/** Move constructor */
Value (Value&&) noexcept;
/** Destructor. */
~Value();
//==============================================================================
/** Returns the current value. */
var getValue() const;
/** Returns the current value. */
operator var() const;
/** Returns the value as a string.
This is a shortcut for "myValue.getValue().toString()".
*/
String toString() const;
/** Sets the current value.
You can also use operator= to set the value.
If there are any listeners registered, they will be notified of the
change asynchronously.
*/
void setValue (const var& newValue);
/** Sets the current value.
This is the same as calling setValue().
If there are any listeners registered, they will be notified of the
change asynchronously.
*/
Value& operator= (const var& newValue);
/** Move assignment operator */
Value& operator= (Value&&) noexcept;
/** Makes this object refer to the same underlying ValueSource as another one.
Once this object has been connected to another one, changing either one
will update the other.
Existing listeners will still be registered after you call this method, and
they'll continue to receive messages when the new value changes.
*/
void referTo (const Value& valueToReferTo);
/** Returns true if this value and the other one are references to the same value.
*/
bool refersToSameSourceAs (const Value& other) const;
/** Compares two values.
This is a compare-by-value comparison, so is effectively the same as
saying (this->getValue() == other.getValue()).
*/
bool operator== (const Value& other) const;
/** Compares two values.
This is a compare-by-value comparison, so is effectively the same as
saying (this->getValue() != other.getValue()).
*/
bool operator!= (const Value& other) const;
//==============================================================================
/** Receives callbacks when a Value object changes.
@see Value::addListener
*/
class JUCE_API Listener
{
public:
Listener() {}
virtual ~Listener() {}
/** Called when a Value object is changed.
Note that the Value object passed as a parameter may not be exactly the same
object that you registered the listener with - it might be a copy that refers
to the same underlying ValueSource. To find out, you can call Value::refersToSameSourceAs().
*/
virtual void valueChanged (Value& value) = 0;
};
/** Adds a listener to receive callbacks when the value changes.
The listener is added to this specific Value object, and not to the shared
object that it refers to. When this object is deleted, all the listeners will
be lost, even if other references to the same Value still exist. So when you're
adding a listener, make sure that you add it to a Value instance that will last
for as long as you need the listener. In general, you'd never want to add a listener
to a local stack-based Value, but more likely to one that's a member variable.
@see removeListener
*/
void addListener (Listener* listener);
/** Removes a listener that was previously added with addListener(). */
void removeListener (Listener* listener);
//==============================================================================
/**
Used internally by the Value class as the base class for its shared value objects.
The Value class is essentially a reference-counted pointer to a shared instance
of a ValueSource object. If you're feeling adventurous, you can create your own custom
ValueSource classes to allow Value objects to represent your own custom data items.
*/
class JUCE_API ValueSource : public ReferenceCountedObject,
private AsyncUpdater
{
public:
ValueSource();
virtual ~ValueSource();
/** Returns the current value of this object. */
virtual var getValue() const = 0;
/** Changes the current value.
This must also trigger a change message if the value actually changes.
*/
virtual void setValue (const var& newValue) = 0;
/** Delivers a change message to all the listeners that are registered with
this value.
If dispatchSynchronously is true, the method will call all the listeners
before returning; otherwise it'll dispatch a message and make the call later.
*/
void sendChangeMessage (bool dispatchSynchronously);
protected:
//==============================================================================
friend class Value;
SortedSet<Value*> valuesWithListeners;
private:
void handleAsyncUpdate() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource)
};
//==============================================================================
/** Creates a Value object that uses this valueSource object as its underlying data. */
explicit Value (ValueSource* valueSource);
/** Returns the ValueSource that this value is referring to. */
ValueSource& getValueSource() noexcept { return *value; }
private:
//==============================================================================
friend class ValueSource;
ReferenceCountedObjectPtr<ValueSource> value;
ListenerList<Listener> listeners;
void callListeners();
void removeFromListenerList();
// This is disallowed to avoid confusion about whether it should
// do a by-value or by-reference copy.
Value& operator= (const Value&) JUCE_DELETED_FUNCTION;
// This declaration prevents accidental construction from an integer of 0,
// which is possible in some compilers via an implicit cast to a pointer.
explicit Value (void*) JUCE_DELETED_FUNCTION;
};
/** Writes a Value to an OutputStream as a UTF8 string. */
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;
} // namespace juce

+ 0
- 1151
source/modules/juce_data_structures/values/juce_ValueTree.cpp
File diff suppressed because it is too large
View File


+ 0
- 578
source/modules/juce_data_structures/values/juce_ValueTree.h View File

@@ -1,578 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A powerful tree structure that can be used to hold free-form data, and which can
handle its own undo and redo behaviour.
A ValueTree contains a list of named properties as var objects, and also holds
any number of sub-trees.
Create ValueTree objects on the stack, and don't be afraid to copy them around, as
they're simply a lightweight reference to a shared data container. Creating a copy
of another ValueTree simply creates a new reference to the same underlying object - to
make a separate, deep copy of a tree you should explicitly call createCopy().
Each ValueTree has a type name, in much the same way as an XmlElement has a tag name,
and much of the structure of a ValueTree is similar to an XmlElement tree.
You can convert a ValueTree to and from an XmlElement, and as long as the XML doesn't
contain text elements, the conversion works well and makes a good serialisation
format. They can also be serialised to a binary format, which is very fast and compact.
All the methods that change data take an optional UndoManager, which will be used
to track any changes to the object. For this to work, you have to be careful to
consistently always use the same UndoManager for all operations to any node inside
the tree.
A ValueTree can only be a child of one parent at a time, so if you're moving one from
one tree to another, be careful to always remove it first, before adding it. This
could also mess up your undo/redo chain, so be wary! In a debug build you should hit
assertions if you try to do anything dangerous, but there are still plenty of ways it
could go wrong.
Note that although the children in a tree have a fixed order, the properties are not
guaranteed to be stored in any particular order, so don't expect that a property's index
will correspond to the order in which the property was added, or that it will remain
constant when other properties are added or removed.
Listeners can be added to a ValueTree to be told when properies change and when
nodes are added or removed.
@see var, XmlElement
*/
class JUCE_API ValueTree
{
public:
//==============================================================================
/** Creates an empty, invalid ValueTree.
A ValueTree that is created with this constructor can't actually be used for anything,
it's just a default 'null' ValueTree that can be returned to indicate some sort of failure.
To create a real one, use the constructor that takes a string.
*/
ValueTree() noexcept;
/** Creates an empty ValueTree with the given type name.
Like an XmlElement, each ValueTree node has a type, which you can access with
getType() and hasType().
*/
explicit ValueTree (const Identifier& type);
/** Creates a reference to another ValueTree. */
ValueTree (const ValueTree&) noexcept;
/** Makes this object reference another node. */
ValueTree& operator= (const ValueTree&);
/** Move constructor */
ValueTree (ValueTree&&) noexcept;
/** Destructor. */
~ValueTree();
/** Returns true if both this and the other tree node refer to the same underlying structure.
Note that this isn't a value comparison - two independently-created trees which
contain identical data are not considered equal.
*/
bool operator== (const ValueTree&) 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&) 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
returns true.
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&) const;
//==============================================================================
/** Returns true if this node refers to some valid data.
It's hard to create an invalid node, but you might get one returned, e.g. by an out-of-range
call to getChild().
*/
bool isValid() const noexcept { return object != nullptr; }
/** Returns a deep copy of this tree and all its sub-nodes. */
ValueTree createCopy() const;
//==============================================================================
/** Returns the type of this node.
The type is specified when the ValueTree is created.
@see hasType
*/
Identifier getType() const noexcept;
/** Returns true if the node has this type.
The comparison is case-sensitive.
*/
bool hasType (const Identifier& typeName) const noexcept;
//==============================================================================
/** Returns the value of a named property.
If no such property has been set, this will return a void variant.
You can also use operator[] to get a property.
@see var, setProperty, getPropertyPointer, hasProperty
*/
const var& getProperty (const Identifier& name) const noexcept;
/** Returns the value of a named property, or the value of defaultReturnValue
if the property doesn't exist.
You can also use operator[] and getProperty to get a property.
@see var, getProperty, getPropertyPointer, setProperty, hasProperty
*/
var getProperty (const Identifier& name, const var& defaultReturnValue) const;
/** Returns a pointer to the value of a named property, or nullptr if the property
doesn't exist.
@see var, getProperty, setProperty, hasProperty
*/
const var* getPropertyPointer (const Identifier& name) const noexcept;
/** 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 noexcept;
/** Changes a named property of the node.
The name identifier must not be an empty string.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
@see var, getProperty, removeProperty
@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);
/** Returns true if the node contains a named property. */
bool hasProperty (const Identifier& name) const noexcept;
/** 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);
/** Removes all properties from the node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeAllProperties (UndoManager* undoManager);
/** Returns the total number of properties that the node contains.
@see getProperty.
*/
int getNumProperties() const noexcept;
/** Returns the identifier of the property with a given index.
Note that properties are not guaranteed to be stored in any particular order, so don't
expect that the index will correspond to the order in which the property was added, or
that it will remain constant when other properties are added or removed.
@see getNumProperties
*/
Identifier getPropertyName (int index) const noexcept;
/** Returns a Value object that can be used to control and respond to one of the tree's properties.
The Value object will maintain a reference to this tree, and will use the undo manager when
it needs to change the value. Attaching a Value::Listener to the value object will provide
callbacks whenever the property changes.
*/
Value getPropertyAsValue (const 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.
@see getChild
*/
int getNumChildren() const noexcept;
/** Returns one of this node's child nodes.
If the index is out of range, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChild (int index) const;
/** Returns the first child node with the specified type name.
If no such node is found, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
@see getOrCreateChildWithName
*/
ValueTree getChildWithName (const Identifier& type) const;
/** Returns the first child node with the specified type name, creating and adding
a child with this name if there wasn't already one there.
The only time this will return an invalid object is when the object that you're calling
the method on is itself invalid.
@see getChildWithName
*/
ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager);
/** Looks for the first child node that has the specified property value.
This will scan the child nodes in order, until it finds one that has property that matches
the specified value.
If no such node is found, it'll return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getChildWithProperty (const Identifier& propertyName, const var& propertyValue) const;
/** Adds a child to this node.
Make sure that the child is removed from any former parent node before calling this, or
you'll hit an assertion.
If the index is < 0 or greater than the current number of child nodes, the new node will
be added at the end of the list.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void addChild (const ValueTree& child, int index, UndoManager* undoManager);
/** Removes the specified child from this node's child-list.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeChild (const ValueTree& child, UndoManager* undoManager);
/** Removes a child from this node's child-list.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeChild (int childIndex, UndoManager* undoManager);
/** Removes all child-nodes from this node.
If the undoManager parameter is non-null, its UndoManager::perform() method will be used,
so that this change can be undone.
*/
void removeAllChildren (UndoManager* undoManager);
/** Moves one of the children to a different index.
This will move the child to a specified index, shuffling along any intervening
items as required. So for example, if you have a list of { 0, 1, 2, 3, 4, 5 }, then
calling move (2, 4) would result in { 0, 1, 3, 4, 2, 5 }.
@param currentIndex the index of the item to be moved. If this isn't a
valid index, then nothing will be done
@param newIndex the index at which you'd like this item to end up. If this
is less than zero, the value will be moved to the end
of the list
@param undoManager the optional UndoManager to use to store this transaction
*/
void moveChild (int currentIndex, int newIndex, UndoManager* undoManager);
/** Returns true if this node is anywhere below the specified parent node.
This returns true if the node is a child-of-a-child, as well as a direct child.
*/
bool isAChildOf (const ValueTree& possibleParent) const noexcept;
/** Returns the index of a child item in this parent.
If the child isn't found, this returns -1.
*/
int indexOf (const ValueTree& child) const noexcept;
/** Returns the parent node that contains this one.
If the node has no parent, this will return an invalid node. (See isValid() to find out
whether a node is valid).
*/
ValueTree getParent() const noexcept;
/** Recusrively finds the highest-level parent node that contains this one.
If the node has no parent, this will return itself.
*/
ValueTree getRoot() const noexcept;
/** Returns one of this node's siblings in its parent's child list.
The delta specifies how far to move through the list, so a value of 1 would return the node
that follows this one, -1 would return the node before it, 0 will return this node itself, etc.
If the requested position is beyond the range of available nodes, this will return an empty ValueTree().
*/
ValueTree getSibling (int delta) const noexcept;
//==============================================================================
struct Iterator
{
Iterator (const ValueTree&, bool isEnd) noexcept;
Iterator& operator++() noexcept;
bool operator!= (const Iterator&) const noexcept;
ValueTree operator*() const;
private:
void* internal;
};
/** Returns a start iterator for the children in this tree. */
Iterator begin() const noexcept;
/** Returns an end iterator for the children in this tree. */
Iterator end() const noexcept;
//==============================================================================
/** Creates an XmlElement that holds a complete image of this node and all its children.
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().
The caller must delete the object that is returned.
@see fromXml
*/
XmlElement* createXml() const;
/** Tries to recreate a node from its XML representation.
This isn't designed to cope with random XML data - for a sensible result, it should only
be fed XML that was created by the createXml() method.
*/
static ValueTree fromXml (const XmlElement& xml);
/** 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.
Once written, the data can be read back with readFromStream().
It's much faster to load/save your tree in binary form than as XML, but
obviously isn't human-readable.
*/
void writeToStream (OutputStream& output) const;
/** Reloads a tree from a stream that was written with writeToStream(). */
static ValueTree readFromStream (InputStream& input);
/** Reloads a tree from a data block that was written with writeToStream(). */
static ValueTree readFromData (const void* data, size_t numBytes);
/** Reloads a tree from a data block that was written with writeToStream() and
then zipped using GZIPCompressorOutputStream.
*/
static ValueTree readFromGZIPData (const void* data, size_t numBytes);
//==============================================================================
/** Listener class for events that happen to a ValueTree.
To get events from a ValueTree, make your class implement this interface, and use
ValueTree::addListener() and ValueTree::removeListener() to register it.
*/
class JUCE_API Listener
{
public:
/** Destructor. */
virtual ~Listener() {}
/** This method is called when a property of this node (or of one of its sub-nodes) has
changed.
The tree parameter indicates which tree has had its property changed, and the property
parameter indicates the property.
Note that when you register a listener to a tree, it will receive this callback for
property changes in that tree, and also for any of its children, (recursively, at any depth).
If your tree has sub-trees but you only want to know about changes to the top level tree,
simply check the tree parameter in this callback to make sure it's the tree you're interested in.
*/
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
const Identifier& property) = 0;
/** This method is called when a child sub-tree is added.
Note that when you register a listener to a tree, it will receive this callback for
child changes in both that tree and any of its children, (recursively, at any depth).
If your tree has sub-trees but you only want to know about changes to the top level tree,
just check the parentTree parameter to make sure it's the one that you're interested in.
*/
virtual void valueTreeChildAdded (ValueTree& parentTree,
ValueTree& childWhichHasBeenAdded) = 0;
/** This method is called when a child sub-tree is removed.
Note that when you register a listener to a tree, it will receive this callback for
child changes in both that tree and any of its children, (recursively, at any depth).
If your tree has sub-trees but you only want to know about changes to the top level tree,
just check the parentTree parameter to make sure it's the one that you're interested in.
*/
virtual void valueTreeChildRemoved (ValueTree& parentTree,
ValueTree& childWhichHasBeenRemoved,
int indexFromWhichChildWasRemoved) = 0;
/** This method is called when a tree's children have been re-shuffled.
Note that when you register a listener to a tree, it will receive this callback for
child changes in both that tree and any of its children, (recursively, at any depth).
If your tree has sub-trees but you only want to know about changes to the top level tree,
just check the parameter to make sure it's the tree that you're interested in.
*/
virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
int oldIndex, int newIndex) = 0;
/** This method is called when a tree has been added or removed from a parent node.
This callback happens when the tree to which the listener was registered is added or
removed from a parent. Unlike the other callbacks, it applies only to the tree to which
the listener is registered, and not to any of its children.
*/
virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) = 0;
/** 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.
The listener is added to this specific ValueTree object, and not to the shared
object that it refers to. When this object is deleted, all the listeners will
be lost, even if other references to the same ValueTree still exist. And if you
use the operator= to make this refer to a different ValueTree, any listeners will
begin listening to changes to the new tree instead of the old one.
When you're adding a listener, make sure that you add it to a ValueTree instance that
will last for as long as you need the listener. In general, you'd never want to add a
listener to a local stack-based ValueTree, and would usually add one to a member variable.
@see removeListener
*/
void addListener (Listener* listener);
/** Removes a listener that was previously added with addListener(). */
void removeListener (Listener* listener);
/** Changes a named property of the node, but will not notify a specified listener of the change.
@see setProperty
*/
ValueTree& setPropertyExcludingListener (Listener* listenerToExclude,
const Identifier& name, const var& newValue,
UndoManager* undoManager);
/** Causes a property-change callback to be triggered for the specified property,
calling any listeners that are registered.
*/
void sendPropertyChangeMessage (const Identifier& property);
//==============================================================================
/** This method uses a comparator object to sort the tree's children into order.
The object provided must have a method of the form:
@code
int compareElements (const ValueTree& first, const ValueTree& second);
@endcode
..and this method must return:
- a value of < 0 if the first comes before the second
- a value of 0 if the two objects are equivalent
- a value of > 0 if the second comes before the first
To improve performance, the compareElements() method can be declared as static or const.
@param comparator the comparator to use for comparing elements.
@param undoManager optional UndoManager for storing the changes
@param retainOrderOfEquivalentItems if this is true, then items which the comparator says are
equivalent will be kept in the order in which they currently appear in the array.
This is slower to perform, but may be important in some cases. If it's false, a
faster algorithm is used, but equivalent elements may be rearranged.
*/
template <typename ElementComparator>
void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems)
{
if (object != nullptr)
{
OwnedArray<ValueTree> sortedList;
createListOfChildren (sortedList);
ComparatorAdapter<ElementComparator> adapter (comparator);
sortedList.sort (adapter, retainOrderOfEquivalentItems);
reorderChildren (sortedList, undoManager);
}
}
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
/** An invalid ValueTree that can be used if you need to return one as an error condition, etc.
This invalid object is equivalent to ValueTree created with its default constructor, but
you should always prefer to avoid it and use ValueTree() or {} instead.
*/
static const ValueTree invalid;
#endif
/** Returns the total number of references to the shared underlying data structure that this
ValueTree is using.
*/
int getReferenceCount() const noexcept;
private:
//==============================================================================
JUCE_PUBLIC_IN_DLL_BUILD (class SharedObject)
friend class SharedObject;
ReferenceCountedObjectPtr<SharedObject> object;
ListenerList<Listener> listeners;
template <typename ElementComparator>
struct ComparatorAdapter
{
ComparatorAdapter (ElementComparator& comp) noexcept : comparator (comp) {}
int compareElements (const ValueTree* const first, const ValueTree* const second)
{
return comparator.compareElements (*first, *second);
}
private:
ElementComparator& comparator;
JUCE_DECLARE_NON_COPYABLE (ComparatorAdapter)
};
void createListOfChildren (OwnedArray<ValueTree>&) const;
void reorderChildren (const OwnedArray<ValueTree>&, UndoManager*);
explicit ValueTree (SharedObject*) noexcept;
};
} // namespace juce

+ 0
- 242
source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.cpp View File

@@ -1,242 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace ValueTreeSynchroniserHelpers
{
enum ChangeType
{
propertyChanged = 1,
fullSync = 2,
childAdded = 3,
childRemoved = 4,
childMoved = 5,
propertyRemoved = 6
};
static void getValueTreePath (ValueTree v, const ValueTree& topLevelTree, Array<int>& path)
{
while (v != topLevelTree)
{
ValueTree parent (v.getParent());
if (! parent.isValid())
break;
path.add (parent.indexOf (v));
v = parent;
}
}
static void writeHeader (MemoryOutputStream& stream, ChangeType type)
{
stream.writeByte ((char) type);
}
static void writeHeader (ValueTreeSynchroniser& target, MemoryOutputStream& stream,
ChangeType type, ValueTree v)
{
writeHeader (stream, type);
Array<int> path;
getValueTreePath (v, target.getRoot(), path);
stream.writeCompressedInt (path.size());
for (int i = path.size(); --i >= 0;)
stream.writeCompressedInt (path.getUnchecked(i));
}
static ValueTree readSubTreeLocation (MemoryInputStream& input, ValueTree v)
{
const int numLevels = input.readCompressedInt();
if (! isPositiveAndBelow (numLevels, 65536)) // sanity-check
return {};
for (int i = numLevels; --i >= 0;)
{
const int index = input.readCompressedInt();
if (! isPositiveAndBelow (index, v.getNumChildren()))
return {};
v = v.getChild (index);
}
return v;
}
}
ValueTreeSynchroniser::ValueTreeSynchroniser (const ValueTree& tree) : valueTree (tree)
{
valueTree.addListener (this);
}
ValueTreeSynchroniser::~ValueTreeSynchroniser()
{
valueTree.removeListener (this);
}
void ValueTreeSynchroniser::sendFullSyncCallback()
{
MemoryOutputStream m;
writeHeader (m, ValueTreeSynchroniserHelpers::fullSync);
valueTree.writeToStream (m);
stateChanged (m.getData(), m.getDataSize());
}
void ValueTreeSynchroniser::valueTreePropertyChanged (ValueTree& vt, const Identifier& property)
{
MemoryOutputStream m;
if (auto* value = vt.getPropertyPointer (property))
{
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyChanged, vt);
m.writeString (property.toString());
value->writeToStream (m);
}
else
{
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::propertyRemoved, vt);
m.writeString (property.toString());
}
stateChanged (m.getData(), m.getDataSize());
}
void ValueTreeSynchroniser::valueTreeChildAdded (ValueTree& parentTree, ValueTree& childTree)
{
const int index = parentTree.indexOf (childTree);
jassert (index >= 0);
MemoryOutputStream m;
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childAdded, parentTree);
m.writeCompressedInt (index);
childTree.writeToStream (m);
stateChanged (m.getData(), m.getDataSize());
}
void ValueTreeSynchroniser::valueTreeChildRemoved (ValueTree& parentTree, ValueTree&, int oldIndex)
{
MemoryOutputStream m;
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childRemoved, parentTree);
m.writeCompressedInt (oldIndex);
stateChanged (m.getData(), m.getDataSize());
}
void ValueTreeSynchroniser::valueTreeChildOrderChanged (ValueTree& parent, int oldIndex, int newIndex)
{
MemoryOutputStream m;
ValueTreeSynchroniserHelpers::writeHeader (*this, m, ValueTreeSynchroniserHelpers::childMoved, parent);
m.writeCompressedInt (oldIndex);
m.writeCompressedInt (newIndex);
stateChanged (m.getData(), m.getDataSize());
}
void ValueTreeSynchroniser::valueTreeParentChanged (ValueTree&) {} // (No action needed here)
bool ValueTreeSynchroniser::applyChange (ValueTree& root, const void* data, size_t dataSize, UndoManager* undoManager)
{
MemoryInputStream input (data, dataSize, false);
const ValueTreeSynchroniserHelpers::ChangeType type = (ValueTreeSynchroniserHelpers::ChangeType) input.readByte();
if (type == ValueTreeSynchroniserHelpers::fullSync)
{
root = ValueTree::readFromStream (input);
return true;
}
ValueTree v (ValueTreeSynchroniserHelpers::readSubTreeLocation (input, root));
if (! v.isValid())
return false;
switch (type)
{
case ValueTreeSynchroniserHelpers::propertyChanged:
{
Identifier property (input.readString());
v.setProperty (property, var::readFromStream (input), undoManager);
return true;
}
case ValueTreeSynchroniserHelpers::propertyRemoved:
{
Identifier property (input.readString());
v.removeProperty (property, undoManager);
return true;
}
case ValueTreeSynchroniserHelpers::childAdded:
{
const int index = input.readCompressedInt();
v.addChild (ValueTree::readFromStream (input), index, undoManager);
return true;
}
case ValueTreeSynchroniserHelpers::childRemoved:
{
const int index = input.readCompressedInt();
if (isPositiveAndBelow (index, v.getNumChildren()))
{
v.removeChild (index, undoManager);
return true;
}
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
break;
}
case ValueTreeSynchroniserHelpers::childMoved:
{
const int oldIndex = input.readCompressedInt();
const int newIndex = input.readCompressedInt();
if (isPositiveAndBelow (oldIndex, v.getNumChildren())
&& isPositiveAndBelow (newIndex, v.getNumChildren()))
{
v.moveChild (oldIndex, newIndex, undoManager);
return true;
}
jassertfalse; // Either received some corrupt data, or the trees have drifted out of sync
break;
}
default:
jassertfalse; // Seem to have received some corrupt data?
break;
}
return false;
}
} // namespace juce

+ 0
- 97
source/modules/juce_data_structures/values/juce_ValueTreeSynchroniser.h View File

@@ -1,97 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
This class can be used to watch for all changes to the state of a ValueTree,
and to convert them to a transmittable binary encoding.
The purpose of this class is to allow two or more ValueTrees to be remotely
synchronised by transmitting encoded changes over some kind of transport
mechanism.
To use it, you'll need to implement a subclass of ValueTreeSynchroniser
and implement the stateChanged() method to transmit the encoded change (maybe
via a network or other means) to a remote destination, where it can be
applied to a target tree.
*/
class JUCE_API ValueTreeSynchroniser : private ValueTree::Listener
{
public:
/** Creates a ValueTreeSynchroniser that watches the given tree.
After creating an instance of this class and somehow attaching it to
a target tree, you probably want to call sendFullSyncCallback() to
get them into a common starting state.
*/
ValueTreeSynchroniser (const ValueTree& tree);
/** Destructor. */
virtual ~ValueTreeSynchroniser();
/** This callback happens when the ValueTree changes and the given state-change message
needs to be applied to any other trees that need to stay in sync with it.
The data is an opaque blob of binary that you should transmit to wherever your
target tree lives, and use applyChange() to apply this to the target tree.
*/
virtual void stateChanged (const void* encodedChange, size_t encodedChangeSize) = 0;
/** Forces the sending of a full state message, which may be large, as it
encodes the entire ValueTree.
This will internally invoke stateChanged() with the encoded version of the state.
*/
void sendFullSyncCallback();
/** Applies an encoded change to the given destination tree.
When you implement a receiver for changes that were sent by the stateChanged()
message, this is the function that you'll need to call to apply them to the
target tree that you want to be synced.
*/
static bool applyChange (ValueTree& target,
const void* encodedChangeData, size_t encodedChangeDataSize,
UndoManager* undoManager);
/** Returns the root ValueTree that is being observed. */
const ValueTree& getRoot() noexcept { return valueTree; }
private:
ValueTree valueTree;
void valueTreePropertyChanged (ValueTree&, const Identifier&) override;
void valueTreeChildAdded (ValueTree&, ValueTree&) override;
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override;
void valueTreeChildOrderChanged (ValueTree&, int, int) override;
void valueTreeParentChanged (ValueTree&) override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueTreeSynchroniser)
};
} // namespace juce

+ 0
- 123
source/modules/juce_events/Makefile View File

@@ -1,123 +0,0 @@
#!/usr/bin/make -f
# Makefile for juce_events #
# ------------------------ #
# Created by falkTX
#

CWD=../..
MODULENAME=juce_events
include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(JUCE_EVENTS_FLAGS) -I..

ifeq ($(WIN32),true)
BUILD_CXX_FLAGS += -Wno-missing-field-initializers
endif

# ----------------------------------------------------------------------------------------------------------------------------

ifeq ($(MACOS),true)
OBJS = $(OBJDIR)/$(MODULENAME).mm.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).mm.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).mm.posix64.o
else
OBJS = $(OBJDIR)/$(MODULENAME).cpp.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).cpp.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).cpp.posix64.o
endif
OBJS_win32 = $(OBJDIR)/$(MODULENAME).cpp.win32.o
OBJS_win64 = $(OBJDIR)/$(MODULENAME).cpp.win64.o

# ----------------------------------------------------------------------------------------------------------------------------

all: $(MODULEDIR)/$(MODULENAME).a
posix32: $(MODULEDIR)/$(MODULENAME).posix32.a
posix64: $(MODULEDIR)/$(MODULENAME).posix64.a
win32: $(MODULEDIR)/$(MODULENAME).win32.a
win64: $(MODULEDIR)/$(MODULENAME).win64.a

# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a

debug:
$(MAKE) DEBUG=true

# ----------------------------------------------------------------------------------------------------------------------------

$(MODULEDIR)/$(MODULENAME).a: $(OBJS)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix64.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win64.a"
@rm -f $@
@$(AR) crs $@ $^

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).cpp.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).mm.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -ObjC++ -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

-include $(OBJS:%.o=%.d)
-include $(OBJS_posix32:%.o=%.d)
-include $(OBJS_posix64:%.o=%.d)
-include $(OBJS_win32:%.o=%.d)
-include $(OBJS_win64:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------------

+ 0
- 92
source/modules/juce_events/broadcasters/juce_ActionBroadcaster.cpp View File

@@ -1,92 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class ActionBroadcaster::ActionMessage : public MessageManager::MessageBase
{
public:
ActionMessage (const ActionBroadcaster* ab,
const String& messageText, ActionListener* l) noexcept
: broadcaster (const_cast<ActionBroadcaster*> (ab)),
message (messageText),
listener (l)
{}
void messageCallback() override
{
if (const ActionBroadcaster* const b = broadcaster)
if (b->actionListeners.contains (listener))
listener->actionListenerCallback (message);
}
private:
WeakReference<ActionBroadcaster> broadcaster;
const String message;
ActionListener* const listener;
JUCE_DECLARE_NON_COPYABLE (ActionMessage)
};
//==============================================================================
ActionBroadcaster::ActionBroadcaster()
{
// are you trying to create this object before or after juce has been intialised??
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
}
ActionBroadcaster::~ActionBroadcaster()
{
// all event-based objects must be deleted BEFORE juce is shut down!
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
}
void ActionBroadcaster::addActionListener (ActionListener* const listener)
{
const ScopedLock sl (actionListenerLock);
if (listener != nullptr)
actionListeners.add (listener);
}
void ActionBroadcaster::removeActionListener (ActionListener* const listener)
{
const ScopedLock sl (actionListenerLock);
actionListeners.removeValue (listener);
}
void ActionBroadcaster::removeAllActionListeners()
{
const ScopedLock sl (actionListenerLock);
actionListeners.clear();
}
void ActionBroadcaster::sendActionMessage (const String& message) const
{
const ScopedLock sl (actionListenerLock);
for (int i = actionListeners.size(); --i >= 0;)
(new ActionMessage (this, message, actionListeners.getUnchecked(i)))->post();
}
} // namespace juce

+ 0
- 77
source/modules/juce_events/broadcasters/juce_ActionBroadcaster.h View File

@@ -1,77 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** Manages a list of ActionListeners, and can send them messages.
To quickly add methods to your class that can add/remove action
listeners and broadcast to them, you can derive from this.
@see ActionListener, ChangeListener
*/
class JUCE_API ActionBroadcaster
{
public:
//==============================================================================
/** Creates an ActionBroadcaster. */
ActionBroadcaster();
/** Destructor. */
virtual ~ActionBroadcaster();
//==============================================================================
/** Adds a listener to the list.
Trying to add a listener that's already on the list will have no effect.
*/
void addActionListener (ActionListener* listener);
/** Removes a listener from the list.
If the listener isn't on the list, this won't have any effect.
*/
void removeActionListener (ActionListener* listener);
/** Removes all listeners from the list. */
void removeAllActionListeners();
//==============================================================================
/** Broadcasts a message to all the registered listeners.
@see ActionListener::actionListenerCallback
*/
void sendActionMessage (const String& message) const;
private:
//==============================================================================
class ActionMessage;
friend class ActionMessage;
SortedSet<ActionListener*> actionListeners;
CriticalSection actionListenerLock;
JUCE_DECLARE_WEAK_REFERENCEABLE (ActionBroadcaster)
JUCE_DECLARE_NON_COPYABLE (ActionBroadcaster)
};
} // namespace juce

+ 0
- 46
source/modules/juce_events/broadcasters/juce_ActionListener.h View File

@@ -1,46 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Interface class for delivery of events that are sent by an ActionBroadcaster.
@see ActionBroadcaster, ChangeListener
*/
class JUCE_API ActionListener
{
public:
/** Destructor. */
virtual ~ActionListener() {}
/** Overridden by your subclass to receive the callback.
@param message the string that was specified when the event was triggered
by a call to ActionBroadcaster::sendActionMessage()
*/
virtual void actionListenerCallback (const String& message) = 0;
};
} // namespace juce

+ 0
- 93
source/modules/juce_events/broadcasters/juce_AsyncUpdater.cpp View File

@@ -1,93 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class AsyncUpdater::AsyncUpdaterMessage : public CallbackMessage
{
public:
AsyncUpdaterMessage (AsyncUpdater& au) : owner (au) {}
void messageCallback() override
{
if (shouldDeliver.compareAndSetBool (0, 1))
owner.handleAsyncUpdate();
}
AsyncUpdater& owner;
Atomic<int> shouldDeliver;
JUCE_DECLARE_NON_COPYABLE (AsyncUpdaterMessage)
};
//==============================================================================
AsyncUpdater::AsyncUpdater()
{
activeMessage = new AsyncUpdaterMessage (*this);
}
AsyncUpdater::~AsyncUpdater()
{
// You're deleting this object with a background thread while there's an update
// pending on the main event thread - that's pretty dodgy threading, as the callback could
// happen after this destructor has finished. You should either use a MessageManagerLock while
// deleting this object, or find some other way to avoid such a race condition.
jassert ((! isUpdatePending())
|| MessageManager::getInstanceWithoutCreating() == nullptr
|| MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
activeMessage->shouldDeliver.set (0);
}
void AsyncUpdater::triggerAsyncUpdate()
{
// If you're calling this before (or after) the MessageManager is
// running, then you're not going to get any callbacks!
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
if (activeMessage->shouldDeliver.compareAndSetBool (1, 0))
if (! activeMessage->post())
cancelPendingUpdate(); // if the message queue fails, this avoids getting
// trapped waiting for the message to arrive
}
void AsyncUpdater::cancelPendingUpdate() noexcept
{
activeMessage->shouldDeliver.set (0);
}
void AsyncUpdater::handleUpdateNowIfNeeded()
{
// This can only be called by the event thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
if (activeMessage->shouldDeliver.exchange (0) != 0)
handleAsyncUpdate();
}
bool AsyncUpdater::isUpdatePending() const noexcept
{
return activeMessage->shouldDeliver.value != 0;
}
} // namespace juce

+ 0
- 108
source/modules/juce_events/broadcasters/juce_AsyncUpdater.h View File

@@ -1,108 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Has a callback method that is triggered asynchronously.
This object allows an asynchronous callback function to be triggered, for
tasks such as coalescing multiple updates into a single callback later on.
Basically, one or more calls to the triggerAsyncUpdate() will result in the
message thread calling handleAsyncUpdate() as soon as it can.
*/
class JUCE_API AsyncUpdater
{
public:
//==============================================================================
/** Creates an AsyncUpdater object. */
AsyncUpdater();
/** Destructor.
If there are any pending callbacks when the object is deleted, these are lost.
*/
virtual ~AsyncUpdater();
//==============================================================================
/** Causes the callback to be triggered at a later time.
This method returns immediately, after which a callback to the
handleAsyncUpdate() method will be made by the message thread as
soon as possible.
If an update callback is already pending but hasn't happened yet, calling
this method will have no effect.
It's thread-safe to call this method from any thread, BUT beware of calling
it from a real-time (e.g. audio) thread, because it involves posting a message
to the system queue, which means it may block (and in general will do on
most OSes).
*/
void triggerAsyncUpdate();
/** This will stop any pending updates from happening.
If called after triggerAsyncUpdate() and before the handleAsyncUpdate()
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 the callback finishes, so
there's no guarantee that the callback isn't still running when the method returns.
*/
void cancelPendingUpdate() noexcept;
/** If an update has been triggered and is pending, this will invoke it
synchronously.
Use this as a kind of "flush" operation - if an update is pending, the
handleAsyncUpdate() method will be called immediately; if no update is
pending, then nothing will be done.
Because this may invoke the callback, this method must only be called on
the main event thread.
*/
void handleUpdateNowIfNeeded();
/** Returns true if there's an update callback in the pipeline. */
bool isUpdatePending() const noexcept;
//==============================================================================
/** Called back to do whatever your class needs to do.
This method is called by the message thread at the next convenient time
after the triggerAsyncUpdate() method has been called.
*/
virtual void handleAsyncUpdate() = 0;
private:
//==============================================================================
class AsyncUpdaterMessage;
friend class ReferenceCountedObjectPtr<AsyncUpdaterMessage>;
ReferenceCountedObjectPtr<AsyncUpdaterMessage> activeMessage;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncUpdater)
};
} // namespace juce

+ 0
- 99
source/modules/juce_events/broadcasters/juce_ChangeBroadcaster.cpp View File

@@ -1,99 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
ChangeBroadcaster::ChangeBroadcaster() noexcept
{
broadcastCallback.owner = this;
}
ChangeBroadcaster::~ChangeBroadcaster()
{
}
void ChangeBroadcaster::addChangeListener (ChangeListener* const listener)
{
// Listeners can only be safely added when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
changeListeners.add (listener);
}
void ChangeBroadcaster::removeChangeListener (ChangeListener* const listener)
{
// Listeners can only be safely removed when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
changeListeners.remove (listener);
}
void ChangeBroadcaster::removeAllChangeListeners()
{
// Listeners can only be safely removed when the event thread is locked
// You can use a MessageManagerLock if you need to call this from another thread.
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
changeListeners.clear();
}
void ChangeBroadcaster::sendChangeMessage()
{
if (changeListeners.size() > 0)
broadcastCallback.triggerAsyncUpdate();
}
void ChangeBroadcaster::sendSynchronousChangeMessage()
{
// This can only be called by the event thread.
jassert (MessageManager::getInstance()->isThisTheMessageThread());
broadcastCallback.cancelPendingUpdate();
callListeners();
}
void ChangeBroadcaster::dispatchPendingMessages()
{
broadcastCallback.handleUpdateNowIfNeeded();
}
void ChangeBroadcaster::callListeners()
{
changeListeners.call (&ChangeListener::changeListenerCallback, this);
}
//==============================================================================
ChangeBroadcaster::ChangeBroadcasterCallback::ChangeBroadcasterCallback()
: owner (nullptr)
{
}
void ChangeBroadcaster::ChangeBroadcasterCallback::handleAsyncUpdate()
{
jassert (owner != nullptr);
owner->callListeners();
}
} // namespace juce

+ 0
- 101
source/modules/juce_events/broadcasters/juce_ChangeBroadcaster.h View File

@@ -1,101 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Holds a list of ChangeListeners, and sends messages to them when instructed.
@see ChangeListener
*/
class JUCE_API ChangeBroadcaster
{
public:
//==============================================================================
/** Creates an ChangeBroadcaster. */
ChangeBroadcaster() noexcept;
/** Destructor. */
virtual ~ChangeBroadcaster();
//==============================================================================
/** Registers a listener to receive change callbacks from this broadcaster.
Trying to add a listener that's already on the list will have no effect.
*/
void addChangeListener (ChangeListener* listener);
/** Unregisters a listener from the list.
If the listener isn't on the list, this won't have any effect.
*/
void removeChangeListener (ChangeListener* listener);
/** Removes all listeners from the list. */
void removeAllChangeListeners();
//==============================================================================
/** Causes an asynchronous change message to be sent to all the registered listeners.
The message will be delivered asynchronously by the main message thread, so this
method will return immediately. To call the listeners synchronously use
sendSynchronousChangeMessage().
*/
void sendChangeMessage();
/** Sends a synchronous change message to all the registered listeners.
This will immediately call all the listeners that are registered. For thread-safety
reasons, you must only call this method on the main message thread.
@see dispatchPendingMessages
*/
void sendSynchronousChangeMessage();
/** If a change message has been sent but not yet dispatched, this will call
sendSynchronousChangeMessage() to make the callback immediately.
For thread-safety reasons, you must only call this method on the main message thread.
*/
void dispatchPendingMessages();
private:
//==============================================================================
class ChangeBroadcasterCallback : public AsyncUpdater
{
public:
ChangeBroadcasterCallback();
void handleAsyncUpdate() override;
ChangeBroadcaster* owner;
};
friend class ChangeBroadcasterCallback;
ChangeBroadcasterCallback broadcastCallback;
ListenerList <ChangeListener> changeListeners;
void callListeners();
JUCE_DECLARE_NON_COPYABLE (ChangeBroadcaster)
};
} // namespace juce

+ 0
- 61
source/modules/juce_events/broadcasters/juce_ChangeListener.h View File

@@ -1,61 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class ChangeBroadcaster;
//==============================================================================
/**
Receives change event callbacks that are sent out by a ChangeBroadcaster.
A ChangeBroadcaster keeps a set of listeners to which it broadcasts a message when
the ChangeBroadcaster::sendChangeMessage() method is called. A subclass of
ChangeListener is used to receive these callbacks.
Note that the major difference between an ActionListener and a ChangeListener
is that for a ChangeListener, multiple changes will be coalesced into fewer
callbacks, but ActionListeners perform one callback for every event posted.
@see ChangeBroadcaster, ActionListener
*/
class JUCE_API ChangeListener
{
public:
/** Destructor. */
virtual ~ChangeListener() {}
/** Your subclass should implement this method to receive the callback.
@param source the ChangeBroadcaster that triggered the callback.
*/
virtual void changeListenerCallback (ChangeBroadcaster* source) = 0;
//==============================================================================
#if JUCE_CATCH_DEPRECATED_CODE_MISUSE
// This method's signature has changed to take a ChangeBroadcaster parameter - please update your code!
private: virtual int changeListenerCallback (void*) { return 0; }
#endif
};
} // namespace juce

+ 0
- 267
source/modules/juce_events/interprocess/juce_ConnectedChildProcess.cpp View File

@@ -1,267 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
enum { magicMastSlaveConnectionHeader = 0x712baf04 };
static const char* startMessage = "__ipc_st";
static const char* killMessage = "__ipc_k_";
static const char* pingMessage = "__ipc_p_";
enum { specialMessageSize = 8, defaultTimeoutMs = 8000 };
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 (int timeout) : Thread ("IPC ping"), timeoutMs (timeout)
{
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<int> 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, int timeout)
: InterprocessConnection (false, magicMastSlaveConnectionHeader),
ChildProcessPingThread (timeout),
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, int timeoutMs, int streamFlags)
{
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, streamFlags))
{
connection = new Connection (*this, pipeName, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs);
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, int timeout)
: InterprocessConnection (false, magicMastSlaveConnectionHeader),
ChildProcessPingThread (timeout),
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,
int timeoutMs)
{
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, timeoutMs <= 0 ? defaultTimeoutMs : timeoutMs);
if (! connection->isConnected())
connection = nullptr;
}
}
return connection != nullptr;
}
} // namespace juce

+ 0
- 189
source/modules/juce_events/interprocess/juce_ConnectedChildProcess.h View File

@@ -1,189 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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().
The timeoutMs parameter lets you specify how long the child process is allowed
to run without receiving a ping from the master before the master is considered to
have died, and handleConnectionLost() will be called. Passing <= 0 for this timeout
makes it use a default value.
Returns true if the command-line matches and the connection is made successfully.
*/
bool initialiseFromCommandLine (const String& commandLine,
const String& commandLineUniqueID,
int timeoutMs = 0);
//==============================================================================
/** 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<Connection>;
ScopedPointer<Connection> 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.
The timeoutMs parameter lets you specify how long the child process is allowed
to go without sending a ping before it is considered to have died and
handleConnectionLost() will be called. Passing <= 0 for this timeout makes
it use a default value.
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,
int timeoutMs = 0,
int streamFlags = ChildProcess::wantStdOut | ChildProcess::wantStdErr);
/** 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<Connection>;
ScopedPointer<Connection> connection;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcessMaster)
};
} // namespace juce

+ 0
- 362
source/modules/juce_events/interprocess/juce_InterprocessConnection.cpp View File

@@ -1,362 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct InterprocessConnection::ConnectionThread : public Thread
{
ConnectionThread (InterprocessConnection& c) : Thread ("JUCE IPC"), owner (c) {}
void run() override { owner.runThread(); }
InterprocessConnection& owner;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionThread)
};
//==============================================================================
InterprocessConnection::InterprocessConnection (bool callbacksOnMessageThread, uint32 magicMessageHeaderNumber)
: useMessageThread (callbacksOnMessageThread),
magicMessageHeader (magicMessageHeaderNumber)
{
thread = new ConnectionThread (*this);
}
InterprocessConnection::~InterprocessConnection()
{
callbackConnectionState = false;
disconnect();
masterReference.clear();
thread = nullptr;
}
//==============================================================================
bool InterprocessConnection::connectToSocket (const String& hostName,
const int portNumber,
const int timeOutMillisecs)
{
disconnect();
const ScopedLock sl (pipeAndSocketLock);
socket = new StreamingSocket();
if (socket->connect (hostName, portNumber, timeOutMillisecs))
{
connectionMadeInt();
thread->startThread();
return true;
}
socket = nullptr;
return false;
}
bool InterprocessConnection::connectToPipe (const String& pipeName, const int timeoutMs)
{
disconnect();
ScopedPointer<NamedPipe> newPipe (new NamedPipe());
if (newPipe->openExisting (pipeName))
{
const ScopedLock sl (pipeAndSocketLock);
pipeReceiveMessageTimeout = timeoutMs;
initialiseWithPipe (newPipe.release());
return true;
}
return false;
}
bool InterprocessConnection::createPipe (const String& pipeName, const int timeoutMs, bool mustNotExist)
{
disconnect();
ScopedPointer<NamedPipe> newPipe (new NamedPipe());
if (newPipe->createNewPipe (pipeName, mustNotExist))
{
const ScopedLock sl (pipeAndSocketLock);
pipeReceiveMessageTimeout = timeoutMs;
initialiseWithPipe (newPipe.release());
return true;
}
return false;
}
void InterprocessConnection::disconnect()
{
thread->signalThreadShouldExit();
{
const ScopedLock sl (pipeAndSocketLock);
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()))
&& thread->isThreadRunning();
}
String InterprocessConnection::getConnectedHostName() const
{
{
const ScopedLock sl (pipeAndSocketLock);
if (pipe == nullptr && socket == nullptr)
return {};
if (socket != nullptr && ! socket->isLocal())
return socket->getHostName();
}
return IPAddress::local().toString();
}
//==============================================================================
bool InterprocessConnection::sendMessage (const MemoryBlock& message)
{
uint32 messageHeader[2] = { ByteOrder::swapIfBigEndian (magicMessageHeader),
ByteOrder::swapIfBigEndian ((uint32) message.getSize()) };
MemoryBlock messageData (sizeof (messageHeader) + message.getSize());
messageData.copyFrom (messageHeader, 0, sizeof (messageHeader));
messageData.copyFrom (message.getData(), sizeof (messageHeader), message.getSize());
return writeData (messageData.getData(), (int) messageData.getSize()) == (int) messageData.getSize();
}
int InterprocessConnection::writeData (void* data, int dataSize)
{
const ScopedLock sl (pipeAndSocketLock);
if (socket != nullptr)
return socket->write (data, dataSize);
if (pipe != nullptr)
return pipe->write (data, dataSize, pipeReceiveMessageTimeout);
return 0;
}
//==============================================================================
void InterprocessConnection::initialiseWithSocket (StreamingSocket* newSocket)
{
jassert (socket == nullptr && pipe == nullptr);
socket = newSocket;
connectionMadeInt();
thread->startThread();
}
void InterprocessConnection::initialiseWithPipe (NamedPipe* newPipe)
{
jassert (socket == nullptr && pipe == nullptr);
pipe = newPipe;
connectionMadeInt();
thread->startThread();
}
//==============================================================================
struct ConnectionStateMessage : public MessageManager::MessageBase
{
ConnectionStateMessage (InterprocessConnection* ipc, bool connected) noexcept
: owner (ipc), connectionMade (connected)
{}
void messageCallback() override
{
if (auto* ipc = owner.get())
{
if (connectionMade)
ipc->connectionMade();
else
ipc->connectionLost();
}
}
WeakReference<InterprocessConnection> owner;
bool connectionMade;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConnectionStateMessage)
};
void InterprocessConnection::connectionMadeInt()
{
if (! callbackConnectionState)
{
callbackConnectionState = true;
if (useMessageThread)
(new ConnectionStateMessage (this, true))->post();
else
connectionMade();
}
}
void InterprocessConnection::connectionLostInt()
{
if (callbackConnectionState)
{
callbackConnectionState = false;
if (useMessageThread)
(new ConnectionStateMessage (this, false))->post();
else
connectionLost();
}
}
struct DataDeliveryMessage : public Message
{
DataDeliveryMessage (InterprocessConnection* ipc, const MemoryBlock& d)
: owner (ipc), data (d)
{}
void messageCallback() override
{
if (auto* ipc = owner.get())
ipc->messageReceived (data);
}
WeakReference<InterprocessConnection> owner;
MemoryBlock data;
};
void InterprocessConnection::deliverDataInt (const MemoryBlock& data)
{
jassert (callbackConnectionState);
if (useMessageThread)
(new DataDeliveryMessage (this, data))->post();
else
messageReceived (data);
}
//==============================================================================
bool InterprocessConnection::readNextMessageInt()
{
uint32 messageHeader[2];
const int bytes = socket != nullptr ? socket->read (messageHeader, sizeof (messageHeader), true)
: 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)
{
MemoryBlock messageData ((size_t) bytesInMessage, true);
int bytesRead = 0;
while (bytesInMessage > 0)
{
if (thread->threadShouldExit())
return false;
const int numThisTime = jmin (bytesInMessage, 65536);
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;
bytesRead += bytesIn;
bytesInMessage -= bytesIn;
}
if (bytesRead >= 0)
deliverDataInt (messageData);
}
}
else if (bytes < 0)
{
if (socket != nullptr)
deletePipeAndSocket();
connectionLostInt();
return false;
}
return true;
}
void InterprocessConnection::runThread()
{
while (! thread->threadShouldExit())
{
if (socket != nullptr)
{
auto ready = socket->waitUntilReady (true, 0);
if (ready < 0)
{
deletePipeAndSocket();
connectionLostInt();
break;
}
if (ready == 0)
{
thread->wait (1);
continue;
}
}
else if (pipe != nullptr)
{
if (! pipe->isOpen())
{
deletePipeAndSocket();
connectionLostInt();
break;
}
}
else
{
break;
}
if (thread->threadShouldExit() || ! readNextMessageInt())
break;
}
}
} // namespace juce

+ 0
- 207
source/modules/juce_events/interprocess/juce_InterprocessConnection.h View File

@@ -1,207 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class InterprocessConnectionServer;
class MemoryBlock;
//==============================================================================
/**
Manages a simple two-way messaging connection to another process, using either
a socket or a named pipe as the transport medium.
To connect to a waiting socket or an open pipe, use the connectToSocket() or
connectToPipe() methods. If this succeeds, messages can be sent to the other end,
and incoming messages will result in a callback via the messageReceived()
method.
To open a pipe and wait for another client to connect to it, use the createPipe()
method.
To act as a socket server and create connections for one or more client, see the
InterprocessConnectionServer class.
@see InterprocessConnectionServer, Socket, NamedPipe
*/
class JUCE_API InterprocessConnection
{
public:
//==============================================================================
/** Creates a connection.
Connections are created manually, connecting them with the connectToSocket()
or connectToPipe() methods, or they are created automatically by a InterprocessConnectionServer
when a client wants to connect.
@param callbacksOnMessageThread if true, callbacks to the connectionMade(),
connectionLost() and messageReceived() methods will
always be made using the message thread; if false,
these will be called immediately on the connection's
own thread.
@param magicMessageHeaderNumber a magic number to use in the header to check the
validity of the data blocks being sent and received. This
can be any number, but the sender and receiver must obviously
use matching values or they won't recognise each other.
*/
InterprocessConnection (bool callbacksOnMessageThread = true,
uint32 magicMessageHeaderNumber = 0xf2b49e2c);
/** Destructor. */
virtual ~InterprocessConnection();
//==============================================================================
/** Tries to connect this object to a socket.
For this to work, the machine on the other end needs to have a InterprocessConnectionServer
object waiting to receive client connections on this port number.
@param hostName the host computer, either a network address or name
@param portNumber the socket port number to try to connect to
@param timeOutMillisecs how long to keep trying before giving up
@returns true if the connection is established successfully
@see Socket
*/
bool connectToSocket (const String& hostName,
int portNumber,
int timeOutMillisecs);
/** Tries to connect the object to an existing named pipe.
For this to work, another process on the same computer must already have opened
an InterprocessConnection object and used createPipe() to create a pipe for this
to connect to.
@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);
/** 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.
@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
@param mustNotExist if set to true, the method will fail if the pipe already exists
@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, bool mustNotExist = false);
/** Disconnects and closes any currently-open sockets or pipes. */
void disconnect();
/** True if a socket or pipe is currently active. */
bool isConnected() const;
/** Returns the socket that this connection is using (or nullptr if it uses a pipe). */
StreamingSocket* getSocket() const noexcept { return 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 may return an empty string if the name is unknown.
*/
String getConnectedHostName() const;
//==============================================================================
/** Tries to send a message to the other end of this connection.
This will fail if it's not connected, or if there's some kind of write error. If
it succeeds, the connection object at the other end will receive the message by
a callback to its messageReceived() method.
@see messageReceived
*/
bool sendMessage (const MemoryBlock& message);
//==============================================================================
/** Called when the connection is first connected.
If the connection was created with the callbacksOnMessageThread flag set, then
this will be called on the message thread; otherwise it will be called on a server
thread.
*/
virtual void connectionMade() = 0;
/** Called when the connection is broken.
If the connection was created with the callbacksOnMessageThread flag set, then
this will be called on the message thread; otherwise it will be called on a server
thread.
*/
virtual void connectionLost() = 0;
/** Called when a message arrives.
When the object at the other end of this connection sends us a message with sendMessage(),
this callback is used to deliver it to us.
If the connection was created with the callbacksOnMessageThread flag set, then
this will be called on the message thread; otherwise it will be called on a server
thread.
@see sendMessage
*/
virtual void messageReceived (const MemoryBlock& message) = 0;
private:
//==============================================================================
CriticalSection pipeAndSocketLock;
ScopedPointer<StreamingSocket> socket;
ScopedPointer<NamedPipe> pipe;
bool callbackConnectionState = false;
const bool useMessageThread;
const uint32 magicMessageHeader;
int pipeReceiveMessageTimeout = -1;
friend class InterprocessConnectionServer;
void initialiseWithSocket (StreamingSocket*);
void initialiseWithPipe (NamedPipe*);
void deletePipeAndSocket();
void connectionMadeInt();
void connectionLostInt();
void deliverDataInt (const MemoryBlock&);
bool readNextMessageInt();
struct ConnectionThread;
friend struct ConnectionThread;
friend struct ContainerDeletePolicy<ConnectionThread>;
ScopedPointer<ConnectionThread> thread;
void runThread();
int writeData (void*, int);
JUCE_DECLARE_WEAK_REFERENCEABLE (InterprocessConnection)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnection)
};
} // namespace juce

+ 0
- 81
source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.cpp View File

@@ -1,81 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
InterprocessConnectionServer::InterprocessConnectionServer()
: Thread ("Juce IPC server")
{
}
InterprocessConnectionServer::~InterprocessConnectionServer()
{
stop();
}
//==============================================================================
bool InterprocessConnectionServer::beginWaitingForSocket (const int portNumber, const String& bindAddress)
{
stop();
socket = new StreamingSocket();
if (socket->createListener (portNumber, bindAddress))
{
startThread();
return true;
}
socket = nullptr;
return false;
}
void InterprocessConnectionServer::stop()
{
signalThreadShouldExit();
if (socket != nullptr)
socket->close();
stopThread (4000);
socket = nullptr;
}
int InterprocessConnectionServer::getBoundPort() const noexcept
{
return (socket == nullptr) ? -1 : socket->getBoundPort();
}
void InterprocessConnectionServer::run()
{
while ((! threadShouldExit()) && socket != nullptr)
{
ScopedPointer<StreamingSocket> clientSocket (socket->waitForNextConnection());
if (clientSocket != nullptr)
if (InterprocessConnection* newConnection = createConnectionObject())
newConnection->initialiseWithSocket (clientSocket.release());
}
}
} // namespace juce

+ 0
- 104
source/modules/juce_events/interprocess/juce_InterprocessConnectionServer.h View File

@@ -1,104 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
An object that waits for client sockets to connect to a port on this host, and
creates InterprocessConnection objects for each one.
To use this, create a class derived from it which implements the createConnectionObject()
method, so that it creates suitable connection objects for each client that tries
to connect.
@see InterprocessConnection
*/
class JUCE_API InterprocessConnectionServer : private Thread
{
public:
//==============================================================================
/** Creates an uninitialised server object.
*/
InterprocessConnectionServer();
/** Destructor. */
~InterprocessConnectionServer();
//==============================================================================
/** Starts an internal thread which listens on the given port number.
While this is running, in another process tries to connect with the
InterprocessConnection::connectToSocket() method, this object will call
createConnectionObject() to create a connection to that client.
Use stop() to stop the thread running.
@param portNumber The port on which the server will receive
connections
@param bindAddress The address on which the server will listen
for connections. An empty string indicates
that it should listen on all addresses
assigned to this machine.
@see createConnectionObject, stop
*/
bool beginWaitingForSocket (int portNumber, const String& bindAddress = String());
/** Terminates the listener thread, if it's active.
@see beginWaitingForSocket
*/
void stop();
/** Returns the local port number to which this server is currently bound.
This is useful if you need to know to which port the OS has actually bound your
socket when calling beginWaitingForSocket with a port number of zero.
Returns -1 if the function fails.
*/
int getBoundPort() const noexcept;
protected:
/** Creates a suitable connection object for a client process that wants to
connect to this one.
This will be called by the listener thread when a client process tries
to connect, and must return a new InterprocessConnection object that will
then run as this end of the connection.
@see InterprocessConnection
*/
virtual InterprocessConnection* createConnectionObject() = 0;
private:
//==============================================================================
ScopedPointer<StreamingSocket> socket;
void run() override;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessConnectionServer)
};
} // namespace juce

+ 0
- 102
source/modules/juce_events/juce_events.cpp View File

@@ -1,102 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#ifdef JUCE_EVENTS_H_INCLUDED
/* 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
header files that the compiler may be using.
*/
#error "Incorrect use of JUCE cpp file"
#endif
#define JUCE_CORE_INCLUDE_OBJC_HELPERS 1
#define JUCE_CORE_INCLUDE_JNI_HELPERS 1
#define JUCE_CORE_INCLUDE_NATIVE_HEADERS 1
#define JUCE_CORE_INCLUDE_COM_SMART_PTR 1
#define JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW 1
#if JUCE_USE_WINRT_MIDI
#define JUCE_EVENTS_INCLUDE_WINRT_WRAPPER 1
#endif
#include "juce_events.h"
//==============================================================================
#if JUCE_MAC
#import <IOKit/IOKitLib.h>
#import <IOKit/IOCFPlugIn.h>
#import <IOKit/hid/IOHIDLib.h>
#import <IOKit/hid/IOHIDKeys.h>
#import <IOKit/pwr_mgt/IOPMLib.h>
#elif JUCE_LINUX
#include <unistd.h>
#endif
//==============================================================================
#include "messages/juce_ApplicationBase.cpp"
#include "messages/juce_DeletedAtShutdown.cpp"
#include "messages/juce_MessageListener.cpp"
#include "messages/juce_MessageManager.cpp"
#include "broadcasters/juce_ActionBroadcaster.cpp"
#include "broadcasters/juce_AsyncUpdater.cpp"
#include "broadcasters/juce_ChangeBroadcaster.cpp"
#include "timers/juce_MultiTimer.cpp"
#include "timers/juce_Timer.cpp"
#include "interprocess/juce_InterprocessConnection.cpp"
#include "interprocess/juce_InterprocessConnectionServer.cpp"
#include "interprocess/juce_ConnectedChildProcess.cpp"
//==============================================================================
#if JUCE_MAC || JUCE_IOS
#include "native/juce_osx_MessageQueue.h"
#if JUCE_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
#endif
#if JUCE_MAC
#include "native/juce_mac_MessageManager.mm"
#else
#include "native/juce_ios_MessageManager.mm"
#endif
#if JUCE_CLANG
#pragma clang diagnostic pop
#endif
#elif JUCE_WINDOWS
#include "native/juce_win32_Messaging.cpp"
#if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER
#include "native/juce_win32_WinRTWrapper.cpp"
#endif
#elif JUCE_LINUX
#include "native/juce_linux_Messaging.cpp"
#elif JUCE_ANDROID
#include "native/juce_android_Messaging.cpp"
#endif

+ 0
- 96
source/modules/juce_events/juce_events.h View File

@@ -1,96 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.txt file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_events
vendor: juce
version: 5.1.2
name: JUCE message and event handling classes
description: Classes for running an application's main event loop and sending/receiving messages, timers, etc.
website: http://www.juce.com/juce
license: ISC
dependencies: juce_core
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
#pragma once
#define JUCE_EVENTS_H_INCLUDED
#include <juce_core/juce_core.h>
//==============================================================================
/** Config: JUCE_EXECUTE_APP_SUSPEND_ON_IOS_BACKGROUND_TASK
Will execute your application's suspend method on an iOS background task, giving
you extra time to save your applications state.
*/
#ifndef JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK
#define JUCE_EXECUTE_APP_SUSPEND_ON_BACKGROUND_TASK 0
#endif
#if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER && JUCE_WINDOWS
#include <hstring.h>
#endif
#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 "messages/juce_MountedVolumeListChangeDetector.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"
#if JUCE_LINUX
#include "native/juce_linux_EventLoop.h"
#endif
#if JUCE_WINDOWS
#if JUCE_EVENTS_INCLUDE_WIN32_MESSAGE_WINDOW
#include "native/juce_win32_HiddenMessageWindow.h"
#endif
#if JUCE_EVENTS_INCLUDE_WINRT_WRAPPER
#include "native/juce_win32_WinRTWrapper.h"
#endif
#endif

+ 0
- 333
source/modules/juce_events/messages/juce_ApplicationBase.cpp View File

@@ -1,333 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
JUCEApplicationBase::CreateInstanceFunction JUCEApplicationBase::createInstance = 0;
JUCEApplicationBase* JUCEApplicationBase::appInstance = nullptr;
#if JUCE_IOS
void* JUCEApplicationBase::iOSCustomDelegate = nullptr;
#endif
JUCEApplicationBase::JUCEApplicationBase()
: appReturnValue (0),
stillInitialising (true)
{
jassert (isStandaloneApp() && appInstance == nullptr);
appInstance = this;
}
JUCEApplicationBase::~JUCEApplicationBase()
{
jassert (appInstance == this);
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<JUCEApplicationBase> app (appInstance);
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())
{
// If you hit this assertion then the __FILE__ macro is providing a
// relative path instead of an absolute path. On Windows this will be
// a path relative to the build directory rather than the currently
// running application. To fix this you must compile with the /FC flag.
jassert (File::isAbsolutePath (sourceFile));
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)
{
}
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 {}; }
String JUCEApplicationBase::getCommandLineParameters() { return {}; }
#else
#if JUCE_WINDOWS && ! defined (_CONSOLE)
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[], void* classPtr);
#endif
#if JUCE_MAC
extern void initialiseNSApplication();
#endif
#if JUCE_LINUX && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER)
extern int juce_gtkWebkitMain (int argc, const char* argv[]);
#endif
#if JUCE_WINDOWS
const char* const* juce_argv = nullptr;
int juce_argc = 0;
#else
extern const char* const* juce_argv; // declared in juce_core
extern int juce_argc;
#endif
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_LINUX && JUCE_MODULE_AVAILABLE_juce_gui_extra && (! defined(JUCE_WEB_BROWSER) || JUCE_WEB_BROWSER)
if (argc >= 2 && String (argv[1]) == "--juce-gtkwebkitfork-child")
return juce_gtkWebkitMain (argc, argv);
#endif
#if JUCE_IOS
return juce_iOSMain (argc, argv, iOSCustomDelegate);
#else
return JUCEApplicationBase::main();
#endif
}
}
#endif
//==============================================================================
int JUCEApplicationBase::main()
{
ScopedJuceInitialiser_GUI libraryInitialiser;
jassert (createInstance != nullptr);
const ScopedPointer<JUCEApplicationBase> app (createInstance());
jassert (app != nullptr);
if (! app->initialiseApp())
return app->shutdownApp();
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
#if JUCE_WINDOWS && JUCE_STANDALONE_APPLICATION && (! defined (_CONSOLE)) && (! JUCE_MINGW)
if (AttachConsole (ATTACH_PARENT_PROCESS) != 0)
{
// if we've launched a GUI app from cmd.exe or PowerShell, we need this to enable printf etc.
// However, only reassign stdout, stderr, stdin if they have not been already opened by
// a redirect or similar.
FILE* ignore;
if (_fileno(stdout) < 0) freopen_s (&ignore, "CONOUT$", "w", stdout);
if (_fileno(stderr) < 0) freopen_s (&ignore, "CONOUT$", "w", stderr);
if (_fileno(stdin) < 0) freopen_s (&ignore, "CONIN$", "r", stdin);
}
#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();
}
} // namespace juce

+ 0
- 312
source/modules/juce_events/messages/juce_ApplicationBase.h View File

@@ -1,312 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Abstract base class for application classes.
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.
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) override
{
myMainWindow = new MyApplicationWindow();
myMainWindow->setBounds (100, 100, 400, 500);
myMainWindow->setVisible (true);
}
void shutdown() override
{
myMainWindow = nullptr;
}
const String getApplicationName() override
{
return "Super JUCE-o-matic";
}
const String getApplicationVersion() override
{
return "1.0";
}
private:
ScopedPointer<MyApplicationWindow> myMainWindow;
};
// this generates boilerplate code to launch our app class:
START_JUCE_APPLICATION (MyJUCEApp)
@endcode
@see JUCEApplication, START_JUCE_APPLICATION
*/
class JUCE_API JUCEApplicationBase
{
protected:
//==============================================================================
JUCEApplicationBase();
public:
/** Destructor. */
virtual ~JUCEApplicationBase();
//==============================================================================
/** Returns the global instance of the application object that's running. */
static JUCEApplicationBase* getInstance() noexcept { return appInstance; }
//==============================================================================
/** Returns the application's name. */
virtual const String getApplicationName() = 0;
/** Returns the application's version number. */
virtual const String getApplicationVersion() = 0;
/** Checks whether multiple instances of the app are allowed.
If you application class returns true for this, more than one instance is
permitted to run (except on the Mac where this isn't possible).
If it's false, the second instance won't start, but it you will still get a
callback to anotherInstanceStarted() to tell you about this - which
gives you a chance to react to what the user was trying to do.
*/
virtual bool moreThanOneInstanceAllowed() = 0;
/** Called when the application starts.
This will be called once to let the application do whatever initialisation
it needs, create its windows, etc.
After the method returns, the normal event-dispatch loop will be run,
until the quit() method is called, at which point the shutdown()
method will be called to let the application clear up anything it needs
to delete.
If during the initialise() method, the application decides not to start-up
after all, it can just call the quit() method and the event loop won't be run.
@param commandLineParameters the line passed in does not include the name of
the executable, just the parameter list. To get the
parameters as an array, you can call
JUCEApplication::getCommandLineParameters()
@see shutdown, quit
*/
virtual void initialise (const String& commandLineParameters) = 0;
/* Called to allow the application to clear up before exiting.
After JUCEApplication::quit() has been called, the event-dispatch loop will
terminate, and this method will get called to allow the app to sort itself
out.
Be careful that nothing happens in this method that might rely on messages
being sent, or any kind of window activity, because the message loop is no
longer running at this point.
@see DeletedAtShutdown
*/
virtual void shutdown() = 0;
/** 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) = 0;
/** Called when the operating system is trying to close the application.
The default implementation of this method is to call quit(), but it may
be overloaded to ignore the request or do some other special behaviour
instead. For example, you might want to offer the user the chance to save
their changes before quitting, and give them the chance to cancel.
If you want to send a quit signal to your app, this is the correct method
to call, because it means that requests that come from the system get handled
in the same way as those from your own application code. So e.g. you'd
call this method from a "quit" item on a menu bar.
*/
virtual void systemRequestedQuit() = 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.
If the type of exception is derived from the std::exception class, the pointer
passed-in will be valid. If the exception is of unknown type, this pointer
will be null.
*/
virtual void unhandledException (const std::exception*,
const String& sourceFilename,
int lineNumber) = 0;
//==============================================================================
/** Override this method to be informed when the back button is pressed on a device.
This is currently only implemented on Android devices.
*/
virtual void backButtonPressed() { }
//==============================================================================
/** 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 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;
#if JUCE_IOS
static void* iOSCustomDelegate;
#endif
virtual bool initialiseApp();
int shutdownApp();
static void JUCE_CALLTYPE sendUnhandledException (const std::exception*, const char* sourceFile, int lineNumber);
bool sendCommandLineToPreexistingInstance();
#endif
private:
//==============================================================================
static JUCEApplicationBase* appInstance;
int appReturnValue;
bool stillInitialising;
struct MultipleInstanceHandler;
friend struct MultipleInstanceHandler;
friend struct ContainerDeletePolicy<MultipleInstanceHandler>;
ScopedPointer<MultipleInstanceHandler> multipleInstanceHandler;
JUCE_DECLARE_NON_COPYABLE (JUCEApplicationBase)
};
//==============================================================================
#if JUCE_CATCH_UNHANDLED_EXCEPTIONS || defined (DOXYGEN)
/** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to
the JUCEApplicationBase::sendUnhandledException() method.
This functionality can be enabled with the JUCE_CATCH_UNHANDLED_EXCEPTIONS macro.
*/
#define JUCE_TRY try
/** The JUCE_TRY/JUCE_CATCH_EXCEPTION wrappers can be used to pass any uncaught exceptions to
the JUCEApplicationBase::sendUnhandledException() method.
This functionality can be enabled with the JUCE_CATCH_UNHANDLED_EXCEPTIONS macro.
*/
#define JUCE_CATCH_EXCEPTION \
catch (const std::exception& e) { juce::JUCEApplicationBase::sendUnhandledException (&e, __FILE__, __LINE__); } \
catch (...) { juce::JUCEApplicationBase::sendUnhandledException (nullptr, __FILE__, __LINE__); }
#else
#define JUCE_TRY
#define JUCE_CATCH_EXCEPTION
#endif
} // namespace juce

+ 0
- 72
source/modules/juce_events/messages/juce_CallbackMessage.h View File

@@ -1,72 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A message that invokes a callback method when it gets delivered.
You can use this class to fire off actions that you want to be performed later
on the message thread.
To use it, create a subclass of CallbackMessage which implements the messageCallback()
method, then call post() to dispatch it. The event thread will then invoke your
messageCallback() method later on, and will automatically delete the message object
afterwards.
Always create a new instance of a CallbackMessage on the heap, as it will be
deleted automatically after the message has been delivered.
Note that this class was essential back in the days before C++11, but in modern
times you may prefer to use MessageManager::callAsync() with a lambda.
@see MessageManager::callAsync, MessageListener, ActionListener, ChangeListener
*/
class JUCE_API CallbackMessage : public MessageManager::MessageBase
{
public:
//==============================================================================
CallbackMessage() noexcept {}
/** Destructor. */
~CallbackMessage() {}
//==============================================================================
/** Called when the message is delivered.
You should implement this method and make it do whatever action you want
to perform.
Note that like all other messages, this object will be deleted immediately
after this method has been invoked.
*/
virtual void messageCallback() = 0;
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)
};
} // namespace juce

+ 0
- 94
source/modules/juce_events/messages/juce_DeletedAtShutdown.cpp View File

@@ -1,94 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static SpinLock deletedAtShutdownLock; // use a spin lock because it can be statically initialised
static Array<DeletedAtShutdown*>& getDeletedAtShutdownObjects()
{
static Array<DeletedAtShutdown*> objects;
return objects;
}
DeletedAtShutdown::DeletedAtShutdown()
{
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
getDeletedAtShutdownObjects().add (this);
}
DeletedAtShutdown::~DeletedAtShutdown()
{
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
getDeletedAtShutdownObjects().removeFirstMatchingValue (this);
}
#if JUCE_MSVC
// Disable unreachable code warning, in case the compiler manages to figure out that
// you have no classes of DeletedAtShutdown that could throw an exception in their destructor.
#pragma warning (push)
#pragma warning (disable: 4702)
#endif
void DeletedAtShutdown::deleteAll()
{
// make a local copy of the array, so it can't get into a loop if something
// creates another DeletedAtShutdown object during its destructor.
Array<DeletedAtShutdown*> localCopy;
{
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
localCopy = getDeletedAtShutdownObjects();
}
for (int i = localCopy.size(); --i >= 0;)
{
JUCE_TRY
{
auto* deletee = localCopy.getUnchecked(i);
// double-check that it's not already been deleted during another object's destructor.
{
const SpinLock::ScopedLockType sl (deletedAtShutdownLock);
if (! getDeletedAtShutdownObjects().contains (deletee))
deletee = nullptr;
}
delete deletee;
}
JUCE_CATCH_EXCEPTION
}
// if this fails, then it's likely that some new DeletedAtShutdown objects were
// created while executing the destructors of the other ones.
jassert (getDeletedAtShutdownObjects().isEmpty());
getDeletedAtShutdownObjects().clear(); // just to make sure the array doesn't have any memory still allocated
}
#if JUCE_MSVC
#pragma warning (pop)
#endif
} // namespace juce

+ 0
- 63
source/modules/juce_events/messages/juce_DeletedAtShutdown.h View File

@@ -1,63 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Classes derived from this will be automatically deleted when the application exits.
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.
So if you've got a singleton and don't want to have to explicitly delete it, just
inherit from this and it'll be taken care of.
*/
class JUCE_API DeletedAtShutdown
{
protected:
/** Creates a DeletedAtShutdown object. */
DeletedAtShutdown();
/** Destructor.
It's ok to delete these objects explicitly - it's only the ones left
dangling at the end that will be deleted automatically.
*/
virtual ~DeletedAtShutdown();
public:
/** Deletes all extant objects.
This shouldn't be used by applications, as it's called automatically
in the shutdown code of the JUCEApplicationBase class.
*/
static void deleteAll();
private:
JUCE_DECLARE_NON_COPYABLE (DeletedAtShutdown)
};
} // namespace juce

+ 0
- 200
source/modules/juce_events/messages/juce_Initialisation.h View File

@@ -1,200 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/** 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)
#else
#if JUCE_WINDOWS && ! defined (_CONSOLE)
#define JUCE_MAIN_FUNCTION int __stdcall WinMain (struct HINSTANCE__*, struct HINSTANCE__*, char*, int)
#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
#if JUCE_IOS
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
void* juce_GetIOSCustomDelegateClass() { return nullptr; }
#define JUCE_CREATE_APPLICATION_DEFINE_CUSTOM_DELEGATE(AppClass, DelegateClass) \
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); } \
void* juce_GetIOSCustomDelegateClass() { return [DelegateClass class]; }
#define JUCE_MAIN_FUNCTION_DEFINITION \
extern "C" JUCE_MAIN_FUNCTION \
{ \
juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; \
juce::JUCEApplicationBase::iOSCustomDelegate = juce_GetIOSCustomDelegateClass(); \
return juce::JUCEApplicationBase::main (JUCE_MAIN_FUNCTION_ARGS); \
}
#elif JUCE_ANDROID
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
#define JUCE_MAIN_FUNCTION_DEFINITION
#else
#define JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
juce::JUCEApplicationBase* juce_CreateApplication() { return new AppClass(); }
#define JUCE_MAIN_FUNCTION_DEFINITION \
extern "C" JUCE_MAIN_FUNCTION \
{ \
juce::JUCEApplicationBase::createInstance = &juce_CreateApplication; \
return juce::JUCEApplicationBase::main (JUCE_MAIN_FUNCTION_ARGS); \
}
#endif
#if JucePlugin_Build_Standalone
#if JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP
#define START_JUCE_APPLICATION(AppClass) JUCE_CREATE_APPLICATION_DEFINE(AppClass)
#if JUCE_IOS
#define START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE(AppClass, DelegateClass) JUCE_CREATE_APPLICATION_DEFINE_CUSTOM_DELEGATE(AppClass, DelegateClass)
#endif
#else
#define START_JUCE_APPLICATION(AppClass) static_assert(false, "You are trying to use START_JUCE_APPLICATION in an audio plug-in. Define JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 if you want to use a custom standalone target app.");
#if JUCE_IOS
#define START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE(AppClass, DelegateClass) static_assert(false, "You are trying to use START_JUCE_APPLICATION in an audio plug-in. Define JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1 if you want to use a custom standalone target app.");
#endif
#endif
#else
#define START_JUCE_APPLICATION(AppClass) \
JUCE_CREATE_APPLICATION_DEFINE(AppClass) \
JUCE_MAIN_FUNCTION_DEFINITION
#if JUCE_IOS
/**
You can instruct JUCE to use a custom iOS app delegate class instaed of JUCE's default
app delegate. For JUCE to work you must pass all messages to JUCE's internal app delegate.
Below is an example of minimal forwarding custom delegate. Note that you are at your own
risk if you decide to use your own delegate an subtle, hard to debug bugs may occur.
@interface MyCustomDelegate : NSObject <UIApplicationDelegate> { NSObject<UIApplicationDelegate>* juceDelegate; } @end
@implementation MyCustomDelegate
-(id) init
{
self = [super init];
juceDelegate = reinterpret_cast<NSObject<UIApplicationDelegate>*> ([[NSClassFromString (@"JuceAppStartupDelegate") alloc] init]);
return self;
}
-(void)dealloc
{
[juceDelegate release];
[super dealloc];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if (juceDelegate != nullptr && [juceDelegate respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:juceDelegate];
else
[super forwardInvocation:anInvocation];
}
-(BOOL)respondsToSelector:(SEL)aSelector
{
if (juceDelegate != nullptr && [juceDelegate respondsToSelector:aSelector])
return YES;
return [super respondsToSelector:aSelector];
}
@end
*/
#define START_JUCE_APPLICATION_WITH_CUSTOM_DELEGATE(AppClass, DelegateClass) \
JUCE_CREATE_APPLICATION_DEFINE_CUSTOM_DELEGATE(AppClass, DelegateClass) \
JUCE_MAIN_FUNCTION_DEFINITION
#endif
#endif
#endif
} // namespace juce

+ 0
- 62
source/modules/juce_events/messages/juce_Message.h View File

@@ -1,62 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class MessageListener;
//==============================================================================
/** The base class for objects that can be sent to a MessageListener.
If you want to send a message that carries some kind of custom data, just
create a subclass of Message with some appropriate member variables to hold
your data.
Always create a new instance of a Message object on the heap, as it will be
deleted automatically after the message has been delivered.
@see MessageListener, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API Message : public MessageManager::MessageBase
{
public:
//==============================================================================
/** Creates an uninitialised message. */
Message() noexcept;
~Message();
typedef ReferenceCountedObjectPtr<Message> Ptr;
//==============================================================================
private:
friend class MessageListener;
WeakReference<MessageListener> recipient;
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)
};
} // namespace juce

+ 0
- 52
source/modules/juce_events/messages/juce_MessageListener.cpp View File

@@ -1,52 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
Message::Message() noexcept {}
Message::~Message() {}
void Message::messageCallback()
{
if (MessageListener* const r = recipient)
r->handleMessage (*this);
}
MessageListener::MessageListener() noexcept
{
// Are you trying to create a messagelistener before or after juce has been intialised??
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
}
MessageListener::~MessageListener()
{
masterReference.clear();
}
void MessageListener::postMessage (Message* const message) const
{
message->recipient = const_cast<MessageListener*> (this);
message->post();
}
} // namespace juce

+ 0
- 68
source/modules/juce_events/messages/juce_MessageListener.h View File

@@ -1,68 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
MessageListener subclasses can post and receive Message objects.
@see Message, MessageManager, ActionListener, ChangeListener
*/
class JUCE_API MessageListener
{
public:
//==============================================================================
MessageListener() noexcept;
/** Destructor. */
virtual ~MessageListener();
//==============================================================================
/** This is the callback method that receives incoming messages.
This is called by the MessageManager from its dispatch loop.
@see postMessage
*/
virtual void handleMessage (const Message& message) = 0;
//==============================================================================
/** Sends a message to the message queue, for asynchronous delivery to this listener
later on.
This method can be called safely by any thread.
@param message the message object to send - this will be deleted
automatically by the message queue, so make sure it's
allocated on the heap, not the stack!
@see handleMessage
*/
void postMessage (Message* message) const;
private:
WeakReference<MessageListener>::Master masterReference;
friend class WeakReference<MessageListener>;
};
} // namespace juce

+ 0
- 387
source/modules/juce_events/messages/juce_MessageManager.cpp View File

@@ -1,387 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
MessageManager::MessageManager() noexcept
: messageThreadId (Thread::getCurrentThreadId())
{
if (JUCEApplicationBase::isStandaloneApp())
Thread::setCurrentThreadName ("Juce Message Thread");
}
MessageManager::~MessageManager() noexcept
{
broadcaster = nullptr;
doPlatformSpecificShutdown();
jassert (instance == this);
instance = nullptr; // do this last in case this instance is still needed by doPlatformSpecificShutdown()
}
MessageManager* MessageManager::instance = nullptr;
MessageManager* MessageManager::getInstance()
{
if (instance == nullptr)
{
instance = new MessageManager();
doPlatformSpecificInitialisation();
}
return instance;
}
MessageManager* MessageManager::getInstanceWithoutCreating() noexcept
{
return instance;
}
void MessageManager::deleteInstance()
{
deleteAndZero (instance);
}
//==============================================================================
bool MessageManager::MessageBase::post()
{
auto* mm = MessageManager::instance;
if (mm == nullptr || mm->quitMessagePosted || ! postMessageToSystemQueue (this))
{
Ptr deleter (this); // (this will delete messages that were just created with a 0 ref count)
return false;
}
return true;
}
//==============================================================================
#if JUCE_MODAL_LOOPS_PERMITTED && ! (JUCE_MAC || JUCE_IOS)
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
{
jassert (isThisTheMessageThread()); // must only be called by the message thread
const int64 endTime = Time::currentTimeMillis() + millisecondsToRunFor;
while (! quitMessageReceived)
{
JUCE_TRY
{
if (! dispatchNextMessageOnSystemQueue (millisecondsToRunFor >= 0))
Thread::sleep (1);
}
JUCE_CATCH_EXCEPTION
if (millisecondsToRunFor >= 0 && Time::currentTimeMillis() >= endTime)
break;
}
return ! quitMessageReceived;
}
#endif
#if ! (JUCE_MAC || JUCE_IOS || JUCE_ANDROID)
class MessageManager::QuitMessage : public MessageManager::MessageBase
{
public:
QuitMessage() {}
void messageCallback() override
{
if (auto* mm = MessageManager::instance)
mm->quitMessageReceived = true;
}
JUCE_DECLARE_NON_COPYABLE (QuitMessage)
};
void MessageManager::runDispatchLoop()
{
jassert (isThisTheMessageThread()); // must only be called by the message thread
while (! quitMessageReceived)
{
JUCE_TRY
{
if (! dispatchNextMessageOnSystemQueue (false))
Thread::sleep (1);
}
JUCE_CATCH_EXCEPTION
}
}
void MessageManager::stopDispatchLoop()
{
(new QuitMessage())->post();
quitMessagePosted = true;
}
#endif
//==============================================================================
class AsyncFunctionCallback : public MessageManager::MessageBase
{
public:
AsyncFunctionCallback (MessageCallbackFunction* const f, void* const param)
: func (f), parameter (param)
{}
void messageCallback() override
{
result = (*func) (parameter);
finished.signal();
}
WaitableEvent finished;
void* volatile result = nullptr;
private:
MessageCallbackFunction* const func;
void* const parameter;
JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback)
};
void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* const func, void* const parameter)
{
if (isThisTheMessageThread())
return func (parameter);
// If this thread has the message manager locked, then this will deadlock!
jassert (! currentThreadHasLockedMessageManager());
const ReferenceCountedObjectPtr<AsyncFunctionCallback> message (new AsyncFunctionCallback (func, parameter));
if (message->post())
{
message->finished.wait();
return message->result;
}
jassertfalse; // the OS message queue failed to send the message!
return nullptr;
}
//==============================================================================
void MessageManager::deliverBroadcastMessage (const String& value)
{
if (broadcaster != nullptr)
broadcaster->sendActionMessage (value);
}
void MessageManager::registerBroadcastListener (ActionListener* const listener)
{
if (broadcaster == nullptr)
broadcaster = new ActionBroadcaster();
broadcaster->addActionListener (listener);
}
void MessageManager::deregisterBroadcastListener (ActionListener* const listener)
{
if (broadcaster != nullptr)
broadcaster->removeActionListener (listener);
}
//==============================================================================
bool MessageManager::isThisTheMessageThread() const noexcept
{
return Thread::getCurrentThreadId() == messageThreadId;
}
void MessageManager::setCurrentThreadAsMessageThread()
{
const Thread::ThreadID thisThread = Thread::getCurrentThreadId();
if (messageThreadId != thisThread)
{
messageThreadId = thisThread;
// This is needed on windows to make sure the message window is created by this thread
doPlatformSpecificShutdown();
doPlatformSpecificInitialisation();
}
}
bool MessageManager::currentThreadHasLockedMessageManager() const noexcept
{
const Thread::ThreadID thisThread = Thread::getCurrentThreadId();
return thisThread == messageThreadId || thisThread == threadWithLock;
}
//==============================================================================
//==============================================================================
/* The only safe way to lock the message thread while another thread does
some work is by posting a special message, whose purpose is to tie up the event
loop until the other thread has finished its business.
Any other approach can get horribly deadlocked if the OS uses its own hidden locks which
get locked before making an event callback, because if the same OS lock gets indirectly
accessed from another thread inside a MM lock, you're screwed. (this is exactly what happens
in Cocoa).
*/
class MessageManagerLock::BlockingMessage : public MessageManager::MessageBase
{
public:
BlockingMessage() noexcept {}
void messageCallback() override
{
lockedEvent.signal();
releaseEvent.wait();
}
WaitableEvent lockedEvent, releaseEvent;
JUCE_DECLARE_NON_COPYABLE (BlockingMessage)
};
//==============================================================================
MessageManagerLock::MessageManagerLock (Thread* const threadToCheck)
: blockingMessage(), checker (threadToCheck, nullptr),
locked (attemptLock (threadToCheck != nullptr ? &checker : nullptr))
{
}
MessageManagerLock::MessageManagerLock (ThreadPoolJob* const jobToCheckForExitSignal)
: blockingMessage(), checker (nullptr, jobToCheckForExitSignal),
locked (attemptLock (jobToCheckForExitSignal != nullptr ? &checker : nullptr))
{
}
MessageManagerLock::MessageManagerLock (BailOutChecker& bailOutChecker)
: blockingMessage(), checker (nullptr, nullptr),
locked (attemptLock (&bailOutChecker))
{
}
bool MessageManagerLock::attemptLock (BailOutChecker* bailOutChecker)
{
auto* mm = MessageManager::instance;
if (mm == nullptr)
return false;
if (mm->currentThreadHasLockedMessageManager())
return true;
if (bailOutChecker == nullptr)
{
mm->lockingLock.enter();
}
else
{
while (! mm->lockingLock.tryEnter())
{
if (bailOutChecker->shouldAbortAcquiringLock())
return false;
Thread::yield();
}
}
blockingMessage = new BlockingMessage();
if (! blockingMessage->post())
{
blockingMessage = nullptr;
return false;
}
while (! blockingMessage->lockedEvent.wait (20))
{
if (bailOutChecker != nullptr && bailOutChecker->shouldAbortAcquiringLock())
{
blockingMessage->releaseEvent.signal();
blockingMessage = nullptr;
mm->lockingLock.exit();
return false;
}
}
jassert (mm->threadWithLock == 0);
mm->threadWithLock = Thread::getCurrentThreadId();
return true;
}
MessageManagerLock::~MessageManagerLock() noexcept
{
if (blockingMessage != nullptr)
{
auto* mm = MessageManager::instance;
jassert (mm == nullptr || mm->currentThreadHasLockedMessageManager());
blockingMessage->releaseEvent.signal();
blockingMessage = nullptr;
if (mm != nullptr)
{
mm->threadWithLock = 0;
mm->lockingLock.exit();
}
}
}
//==============================================================================
MessageManagerLock::ThreadChecker::ThreadChecker (Thread* const threadToUse,
ThreadPoolJob* const threadJobToUse)
: threadToCheck (threadToUse), job (threadJobToUse)
{
}
bool MessageManagerLock::ThreadChecker::shouldAbortAcquiringLock()
{
return (threadToCheck != nullptr && threadToCheck->threadShouldExit())
|| (job != nullptr && job->shouldExit());
}
//==============================================================================
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI();
JUCE_API void JUCE_CALLTYPE initialiseJuce_GUI()
{
JUCE_AUTORELEASEPOOL
{
MessageManager::getInstance();
}
}
JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI();
JUCE_API void JUCE_CALLTYPE shutdownJuce_GUI()
{
JUCE_AUTORELEASEPOOL
{
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(); }
} // namespace juce

+ 0
- 381
source/modules/juce_events/messages/juce_MessageManager.h View File

@@ -1,381 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class MessageManagerLock;
class ThreadPoolJob;
class ActionListener;
class ActionBroadcaster;
//==============================================================================
#if JUCE_MODULE_AVAILABLE_juce_opengl
class OpenGLContext;
#endif
//==============================================================================
/** See MessageManager::callFunctionOnMessageThread() for use of this function type. */
typedef void* (MessageCallbackFunction) (void* userData);
//==============================================================================
/**
This class is in charge of the application's event-dispatch loop.
@see Message, CallbackMessage, MessageManagerLock, JUCEApplication, JUCEApplicationBase
*/
class JUCE_API MessageManager
{
public:
//==============================================================================
/** Returns the global instance of the MessageManager. */
static MessageManager* getInstance();
/** Returns the global instance of the MessageManager, or nullptr if it doesn't exist. */
static MessageManager* getInstanceWithoutCreating() noexcept;
/** Deletes the global MessageManager instance.
Does nothing if no instance had been created.
*/
static void deleteInstance();
//==============================================================================
/** Runs the event dispatch loop until a stop message is posted.
This method is only intended to be run by the application's startup routine,
as it blocks, and will only return after the stopDispatchLoop() method has been used.
@see stopDispatchLoop
*/
void runDispatchLoop();
/** Sends a signal that the dispatch loop should terminate.
After this is called, the runDispatchLoop() or runDispatchLoopUntil() methods
will be interrupted and will return.
@see runDispatchLoop
*/
void stopDispatchLoop();
/** Returns true if the stopDispatchLoop() method has been called.
*/
bool hasStopMessageBeenSent() const noexcept { return quitMessagePosted; }
#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(),
otherwise returns true.
*/
bool runDispatchLoopUntil (int millisecondsToRunFor);
#endif
//==============================================================================
/** Asynchronously invokes a function or C++11 lambda on the message thread. */
template <typename FunctionType>
static void callAsync (FunctionType functionToCall)
{
new AsyncCallInvoker<FunctionType> (functionToCall);
}
/** Calls a function using the message-thread.
This can be used by any thread to cause this function to be called-back
by the message thread. If it's the message-thread that's calling this method,
then the function will just be called; if another thread is calling, a message
will be posted to the queue, and this method will block until that message
is delivered, the function is called, and the result is returned.
Be careful not to cause any deadlocks with this! It's easy to do - e.g. if the caller
thread has a critical section locked, which an unrelated message callback then tries to lock
before the message thread gets round to processing this callback.
@param callback the function to call - its signature must be @code
void* myCallbackFunction (void*) @endcode
@param userData a user-defined pointer that will be passed to the function that gets called
@returns the value that the callback function returns.
@see MessageManagerLock
*/
void* callFunctionOnMessageThread (MessageCallbackFunction* callback, void* userData);
/** Returns true if the caller-thread is the message thread. */
bool isThisTheMessageThread() const noexcept;
/** Called to tell the manager that the current thread is the one that's running the dispatch loop.
(Best to ignore this method unless you really know what you're doing..)
@see getCurrentMessageThread
*/
void setCurrentThreadAsMessageThread();
/** Returns the ID of the current message thread, as set by setCurrentThreadAsMessageThread().
(Best to ignore this method unless you really know what you're doing..)
@see setCurrentThreadAsMessageThread
*/
Thread::ThreadID getCurrentMessageThread() const noexcept { return messageThreadId; }
/** Returns true if the caller thread has currently got the message manager locked.
see the MessageManagerLock class for more info about this.
This will be true if the caller is the message thread, because that automatically
gains a lock while a message is being dispatched.
*/
bool currentThreadHasLockedMessageManager() const noexcept;
//==============================================================================
/** Sends a message to all other JUCE applications that are running.
@param messageText the string that will be passed to the actionListenerCallback()
method of the broadcast listeners in the other app.
@see registerBroadcastListener, ActionListener
*/
static void broadcastMessage (const String& messageText);
/** Registers a listener to get told about broadcast messages.
The actionListenerCallback() callback's string parameter
is the message passed into broadcastMessage().
@see broadcastMessage
*/
void registerBroadcastListener (ActionListener* listener);
/** Deregisters a broadcast listener. */
void deregisterBroadcastListener (ActionListener* listener);
//==============================================================================
/** Internal class used as the base class for all message objects.
You shouldn't need to use this directly - see the CallbackMessage or Message
classes instead.
*/
class JUCE_API MessageBase : public ReferenceCountedObject
{
public:
MessageBase() noexcept {}
virtual ~MessageBase() {}
virtual void messageCallback() = 0;
bool post();
typedef ReferenceCountedObjectPtr<MessageBase> Ptr;
JUCE_DECLARE_NON_COPYABLE (MessageBase)
};
//==============================================================================
#ifndef DOXYGEN
// Internal methods - do not use!
void deliverBroadcastMessage (const String&);
~MessageManager() noexcept;
static bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages);
#endif
private:
//==============================================================================
MessageManager() noexcept;
static MessageManager* instance;
friend class MessageBase;
class QuitMessage;
friend class QuitMessage;
friend class MessageManagerLock;
ScopedPointer<ActionBroadcaster> broadcaster;
bool quitMessagePosted = false, quitMessageReceived = false;
Thread::ThreadID messageThreadId;
Thread::ThreadID volatile threadWithLock = {};
CriticalSection lockingLock;
static bool postMessageToSystemQueue (MessageBase*);
static void* exitModalLoopCallback (void*);
static void doPlatformSpecificInitialisation();
static void doPlatformSpecificShutdown();
template <typename FunctionType>
struct AsyncCallInvoker : public MessageBase
{
AsyncCallInvoker (FunctionType f) : callback (f) { post(); }
void messageCallback() override { callback(); }
FunctionType callback;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncCallInvoker)
};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager)
};
//==============================================================================
/** Used to make sure that the calling thread has exclusive access to the message loop.
Because it's not thread-safe to call any of the Component or other UI classes
from threads other than the message thread, one of these objects can be used to
lock the message loop and allow this to be done. The message thread will be
suspended for the lifetime of the MessageManagerLock object, so create one on
the stack like this: @code
void MyThread::run()
{
someData = 1234;
const MessageManagerLock mmLock;
// the event loop will now be locked so it's safe to make a few calls..
myComponent->setBounds (newBounds);
myComponent->repaint();
// ..the event loop will now be unlocked as the MessageManagerLock goes out of scope
}
@endcode
Obviously be careful not to create one of these and leave it lying around, or
your app will grind to a halt!
MessageManagerLocks are re-entrant, so can be safely nested if the current thread
already has the lock.
Another caveat is that using this in conjunction with other CriticalSections
can create lots of interesting ways of producing a deadlock! In particular, if
your message thread calls stopThread() for a thread that uses these locks,
you'll get an (occasional) deadlock..
@see MessageManager, MessageManager::currentThreadHasLockedMessageManager
*/
class JUCE_API MessageManagerLock
{
public:
//==============================================================================
/** Tries to acquire a lock on the message manager.
The constructor attempts to gain a lock on the message loop, and the lock will be
kept for the lifetime of this object.
Optionally, you can pass a thread object here, and while waiting to obtain the lock,
this method will keep checking whether the thread has been given the
Thread::signalThreadShouldExit() signal. If this happens, then it will return
without gaining the lock. If you pass a thread, you must check whether the lock was
successful by calling lockWasGained(). If this is false, your thread is being told to
die, so you should take evasive action.
If you pass nullptr for the thread object, it will wait indefinitely for the lock - be
careful when doing this, because it's very easy to deadlock if your message thread
attempts to call stopThread() on a thread just as that thread attempts to get the
message lock.
If the calling thread already has the lock, nothing will be done, so it's safe and
quick to use these locks recursively.
E.g.
@code
void run()
{
...
while (! threadShouldExit())
{
MessageManagerLock mml (Thread::getCurrentThread());
if (! mml.lockWasGained())
return; // another thread is trying to kill us!
..do some locked stuff here..
}
..and now the MM is now unlocked..
}
@endcode
*/
MessageManagerLock (Thread* threadToCheckForExitSignal = nullptr);
//==============================================================================
/** This has the same behaviour as the other constructor, but takes a ThreadPoolJob
instead of a thread.
See the MessageManagerLock (Thread*) constructor for details on how this works.
*/
MessageManagerLock (ThreadPoolJob* jobToCheckForExitSignal);
//==============================================================================
struct BailOutChecker
{
virtual ~BailOutChecker() {}
/** Return true if acquiring the lock should be aborted. */
virtual bool shouldAbortAcquiringLock() = 0;
};
/** This is an abstraction of the other constructors. You can pass this constructor
a functor which is periodically checked if attempting the lock should be aborted.
See the MessageManagerLock (Thread*) constructor for details on how this works.
*/
MessageManagerLock (BailOutChecker&);
//==============================================================================
/** Releases the current thread's lock on the message manager.
Make sure this object is created and deleted by the same thread,
otherwise there are no guarantees what will happen!
*/
~MessageManagerLock() noexcept;
//==============================================================================
/** Returns true if the lock was successfully acquired.
(See the constructor that takes a Thread for more info).
*/
bool lockWasGained() const noexcept { return locked; }
private:
class BlockingMessage;
friend class ReferenceCountedObjectPtr<BlockingMessage>;
ReferenceCountedObjectPtr<BlockingMessage> blockingMessage;
struct ThreadChecker : BailOutChecker
{
ThreadChecker (Thread* const, ThreadPoolJob* const);
// Required to supress VS2013 compiler warnings
ThreadChecker& operator= (const ThreadChecker&) = delete;
bool shouldAbortAcquiringLock() override;
Thread* const threadToCheck;
ThreadPoolJob* const job;
};
//==============================================================================
ThreadChecker checker;
bool locked;
//==============================================================================
bool attemptLock (BailOutChecker*);
JUCE_DECLARE_NON_COPYABLE (MessageManagerLock)
};
} // namespace juce

+ 0
- 57
source/modules/juce_events/messages/juce_MountedVolumeListChangeDetector.h View File

@@ -1,57 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#if JUCE_MAC || JUCE_WINDOWS || defined (DOXYGEN)
//==============================================================================
/**
An instance of this class will provide callbacks when drives are
mounted or unmounted on the system.
Just inherit from this class and implement the pure virtual method
to get the callbacks, there's no need to do anything else.
@see File::findFileSystemRoots()
*/
class JUCE_API MountedVolumeListChangeDetector
{
public:
MountedVolumeListChangeDetector();
virtual ~MountedVolumeListChangeDetector();
/** This method is called when a volume is mounted or unmounted. */
virtual void mountedVolumeListChanged() = 0;
private:
JUCE_PUBLIC_IN_DLL_BUILD (struct Pimpl)
friend struct ContainerDeletePolicy<Pimpl>;
ScopedPointer<Pimpl> pimpl;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MountedVolumeListChangeDetector)
};
#endif
} // namespace juce

+ 0
- 39
source/modules/juce_events/messages/juce_NotificationType.h View File

@@ -1,39 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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. */
};
} // namespace juce

+ 0
- 147
source/modules/juce_events/native/juce_android_Messaging.cpp View File

@@ -1,147 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
METHOD (constructor, "<init>", "()V") \
METHOD (post, "post", "(Ljava/lang/Runnable;)Z") \
DECLARE_JNI_CLASS (JNIHandler, "android/os/Handler");
#undef JNI_CLASS_MEMBERS
//==============================================================================
namespace Android
{
class Runnable : public juce::AndroidInterfaceImplementer
{
public:
virtual void run() = 0;
private:
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
{
auto* env = getEnv();
auto methodName = juce::juceString ((jstring) env->CallObjectMethod (method, Method.getName));
if (methodName == "run")
{
run();
return nullptr;
}
// invoke base class
return AndroidInterfaceImplementer::invoke (proxy, method, args);
}
};
struct Handler
{
juce_DeclareSingleton (Handler, false)
Handler() : nativeHandler (getEnv()->NewObject (JNIHandler, JNIHandler.constructor)) {}
bool post (Runnable* runnable)
{
return (getEnv()->CallBooleanMethod (nativeHandler.get(), JNIHandler.post,
CreateJavaInterface (runnable, "java/lang/Runnable").get()) != 0);
}
GlobalRef nativeHandler;
};
juce_ImplementSingleton (Handler);
}
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation() { Android::Handler::getInstance(); }
void MessageManager::doPlatformSpecificShutdown() {}
//==============================================================================
bool MessageManager::dispatchNextMessageOnSystemQueue (const bool)
{
Logger::outputDebugString ("*** Modal loops are not possible in Android!! Exiting...");
exit (1);
return true;
}
//==============================================================================
struct AndroidMessageCallback : public Android::Runnable
{
AndroidMessageCallback (const MessageManager::MessageBase::Ptr& messageToDeliver)
: message (messageToDeliver)
{}
AndroidMessageCallback (MessageManager::MessageBase::Ptr && messageToDeliver)
: message (static_cast<MessageManager::MessageBase::Ptr&&> (messageToDeliver))
{}
void run() override
{
JUCE_TRY
{
message->messageCallback();
// delete the message already here as Java will only run the
// destructor of this runnable the next time the garbage
// collector kicks in.
message = nullptr;
}
JUCE_CATCH_EXCEPTION
}
MessageManager::MessageBase::Ptr message;
};
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
{
return Android::Handler::getInstance()->post (new AndroidMessageCallback (message));
}
//==============================================================================
void MessageManager::broadcastMessage (const String&)
{
}
void MessageManager::runDispatchLoop()
{
}
void MessageManager::stopDispatchLoop()
{
struct QuitCallback : public CallbackMessage
{
QuitCallback() {}
void messageCallback() override
{
android.activity.callVoidMethod (JuceAppActivity.finish);
}
};
(new QuitCallback())->post();
quitMessagePosted = true;
}
} // namespace juce

+ 0
- 103
source/modules/juce_events/native/juce_ios_MessageManager.mm View File

@@ -1,103 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
void MessageManager::runDispatchLoop()
{
jassert (isThisTheMessageThread()); // must only be called by the message thread
while (! quitMessagePosted)
{
JUCE_AUTORELEASEPOOL
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]];
}
}
}
void MessageManager::stopDispatchLoop()
{
if (! SystemStats::isRunningInAppExtensionSandbox())
[[[UIApplication sharedApplication] delegate] applicationWillTerminate: [UIApplication sharedApplication]];
exit (0); // iOS apps get no mercy..
}
#if JUCE_MODAL_LOOPS_PERMITTED
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
{
JUCE_AUTORELEASEPOOL
{
jassert (isThisTheMessageThread()); // must only be called by the message thread
uint32 startTime = Time::getMillisecondCounter();
NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow: millisecondsToRunFor * 0.001];
while (! quitMessagePosted)
{
JUCE_AUTORELEASEPOOL
{
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
beforeDate: endDate];
if (millisecondsToRunFor >= 0
&& Time::getMillisecondCounter() >= startTime + (uint32) millisecondsToRunFor)
break;
}
}
return ! quitMessagePosted;
}
}
#endif
//==============================================================================
static ScopedPointer<MessageQueue> messageQueue;
void MessageManager::doPlatformSpecificInitialisation()
{
if (messageQueue == nullptr)
messageQueue = new MessageQueue();
}
void MessageManager::doPlatformSpecificShutdown()
{
messageQueue = nullptr;
}
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
{
if (messageQueue != nullptr)
messageQueue->post (message);
return true;
}
void MessageManager::broadcastMessage (const String&)
{
// N/A on current iOS
}
} // namespace juce

+ 0
- 55
source/modules/juce_events/native/juce_linux_EventLoop.h View File

@@ -1,55 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace LinuxEventLoop
{
struct CallbackFunctionBase
{
virtual ~CallbackFunctionBase() {}
virtual bool operator()(int fd) = 0;
bool active = true;
};
template <typename FdCallbackFunction>
struct CallbackFunction : public CallbackFunctionBase
{
FdCallbackFunction callback;
CallbackFunction (FdCallbackFunction c) : callback (c) {}
bool operator() (int fd) override { return callback (fd); }
};
template <typename FdCallbackFunction>
void setWindowSystemFd (int fd, FdCallbackFunction readCallback)
{
setWindowSystemFdInternal (fd, new CallbackFunction<FdCallbackFunction> (readCallback));
}
void removeWindowSystemFd() noexcept;
void setWindowSystemFdInternal (int fd, CallbackFunctionBase* readCallback) noexcept;
}
} // namespace juce

+ 0
- 265
source/modules/juce_events/native/juce_linux_Messaging.cpp View File

@@ -1,265 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include <poll.h>
enum FdType
{
INTERNAL_QUEUE_FD,
WINDOW_SYSTEM_FD,
FD_COUNT,
};
namespace juce
{
//==============================================================================
class InternalMessageQueue
{
public:
InternalMessageQueue()
{
auto ret = ::socketpair (AF_LOCAL, SOCK_STREAM, 0, fd);
ignoreUnused (ret); jassert (ret == 0);
auto internalQueueCb = [this] (int _fd)
{
if (const MessageManager::MessageBase::Ptr msg = this->popNextMessage (_fd))
{
JUCE_TRY
{
msg->messageCallback();
return true;
}
JUCE_CATCH_EXCEPTION
}
return false;
};
pfds[INTERNAL_QUEUE_FD].fd = getReadHandle();
pfds[INTERNAL_QUEUE_FD].events = POLLIN;
readCallback[INTERNAL_QUEUE_FD] = new LinuxEventLoop::CallbackFunction<decltype(internalQueueCb)> (internalQueueCb);
}
~InternalMessageQueue()
{
close (getReadHandle());
close (getWriteHandle());
clearSingletonInstance();
}
//==============================================================================
void postMessage (MessageManager::MessageBase* const msg) noexcept
{
ScopedLock sl (lock);
queue.add (msg);
const int maxBytesInSocketQueue = 128;
if (bytesInSocket < maxBytesInSocketQueue)
{
bytesInSocket++;
ScopedUnlock ul (lock);
const unsigned char x = 0xff;
ssize_t bytesWritten = write (getWriteHandle(), &x, 1);
ignoreUnused (bytesWritten);
}
}
void setWindowSystemFd (int _fd, LinuxEventLoop::CallbackFunctionBase* _readCallback)
{
jassert (fdCount == 1);
ScopedLock sl (lock);
fdCount = 2;
pfds[WINDOW_SYSTEM_FD].fd = _fd;
pfds[WINDOW_SYSTEM_FD].events = POLLIN;
readCallback[WINDOW_SYSTEM_FD] = _readCallback;
readCallback[WINDOW_SYSTEM_FD]->active = true;
}
void removeWindowSystemFd()
{
jassert (fdCount == FD_COUNT);
ScopedLock sl (lock);
fdCount = 1;
readCallback[WINDOW_SYSTEM_FD]->active = false;
}
bool dispatchNextEvent() noexcept
{
for (int counter = 0; counter < fdCount; counter++)
{
const int i = loopCount++;
loopCount %= fdCount;
if (readCallback[i] != nullptr && readCallback[i]->active)
if ((*readCallback[i]) (pfds[i].fd))
return true;
}
return false;
}
bool sleepUntilEvent (const int timeoutMs)
{
const int pnum = poll (pfds, static_cast<nfds_t> (fdCount), timeoutMs);
return (pnum > 0);
}
//==============================================================================
juce_DeclareSingleton_SingleThreaded_Minimal (InternalMessageQueue)
private:
CriticalSection lock;
ReferenceCountedArray <MessageManager::MessageBase> queue;
int fd[2];
pollfd pfds[FD_COUNT];
ScopedPointer<LinuxEventLoop::CallbackFunctionBase> readCallback[FD_COUNT];
int fdCount = 1;
int loopCount = 0;
int bytesInSocket = 0;
int getWriteHandle() const noexcept { return fd[0]; }
int getReadHandle() const noexcept { return fd[1]; }
MessageManager::MessageBase::Ptr popNextMessage (int _fd) noexcept
{
const ScopedLock sl (lock);
if (bytesInSocket > 0)
{
--bytesInSocket;
const ScopedUnlock ul (lock);
unsigned char x;
ssize_t numBytes = read (_fd, &x, 1);
ignoreUnused (numBytes);
}
return queue.removeAndReturn (0);
}
};
juce_ImplementSingleton_SingleThreaded (InternalMessageQueue)
//==============================================================================
namespace LinuxErrorHandling
{
static bool keyboardBreakOccurred = false;
//==============================================================================
void keyboardBreakSignalHandler (int sig)
{
if (sig == SIGINT)
keyboardBreakOccurred = true;
}
void installKeyboardBreakHandler()
{
struct sigaction saction;
sigset_t maskSet;
sigemptyset (&maskSet);
saction.sa_handler = keyboardBreakSignalHandler;
saction.sa_mask = maskSet;
saction.sa_flags = 0;
sigaction (SIGINT, &saction, 0);
}
}
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation()
{
if (JUCEApplicationBase::isStandaloneApp())
LinuxErrorHandling::installKeyboardBreakHandler();
// Create the internal message queue
auto* queue = InternalMessageQueue::getInstance();
ignoreUnused (queue);
}
void MessageManager::doPlatformSpecificShutdown()
{
InternalMessageQueue::deleteInstance();
}
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
{
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
{
queue->postMessage (message);
return true;
}
return false;
}
void MessageManager::broadcastMessage (const String&)
{
// TODO
}
// this function expects that it will NEVER be called simultaneously for two concurrent threads
bool MessageManager::dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages)
{
for (;;)
{
if (LinuxErrorHandling::keyboardBreakOccurred)
JUCEApplicationBase::getInstance()->quit();
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
{
if (queue->dispatchNextEvent())
break;
if (returnIfNoPendingMessages)
return false;
// wait for 2000ms for next events if necessary
queue->sleepUntilEvent (2000);
}
}
return true;
}
//==============================================================================
void LinuxEventLoop::setWindowSystemFdInternal (int fd, LinuxEventLoop::CallbackFunctionBase* readCallback) noexcept
{
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
queue->setWindowSystemFd (fd, readCallback);
}
void LinuxEventLoop::removeWindowSystemFd() noexcept
{
if (auto* queue = InternalMessageQueue::getInstanceWithoutCreating())
queue->removeWindowSystemFd();
}
} // namespace juce

+ 0
- 430
source/modules/juce_events/native/juce_mac_MessageManager.mm View File

@@ -1,430 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
typedef void (*AppFocusChangeCallback)();
AppFocusChangeCallback appFocusChangeCallback = nullptr;
typedef bool (*CheckEventBlockedByModalComps) (NSEvent*);
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
typedef void (*MenuTrackingChangedCallback)(bool);
MenuTrackingChangedCallback menuTrackingChangedCallback = nullptr;
//==============================================================================
struct AppDelegate
{
public:
AppDelegate()
{
static AppDelegateClass cls;
delegate = [cls.createInstance() init];
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: getBroadcastEventName()
object: nil
suspensionBehavior: NSNotificationSuspensionBehaviorDeliverImmediately];
}
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: getBroadcastEventName()
object: nil];
}
[delegate release];
}
static NSString* getBroadcastEventName()
{
return juceStringToNS ("juce_" + String::toHexString (File::getSpecialLocation (File::currentExecutableFile).hashCode64()));
}
MessageQueue messageQueue;
id delegate;
private:
//==============================================================================
struct AppDelegateClass : public ObjCClass<NSObject>
{
AppDelegateClass() : ObjCClass<NSObject> ("JUCEAppDelegate_")
{
addMethod (@selector (applicationWillFinishLaunching:), applicationWillFinishLaunching, "v@:@@");
addMethod (@selector (getUrl:withReplyEvent:), getUrl_withReplyEvent, "v@:@@");
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@:");
registerClass();
}
private:
static void applicationWillFinishLaunching (id self, SEL, NSApplication*, NSNotification*)
{
[[NSAppleEventManager sharedAppleEventManager] setEventHandler: self
andSelector: @selector (getUrl:withReplyEvent:)
forEventClass: kInternetEventClass
andEventID: kAEGetURL];
}
static NSApplicationTerminateReply applicationShouldTerminate (id /*self*/, SEL, NSApplication*)
{
if (auto* 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 (auto* app = JUCEApplicationBase::getInstance())
{
app->anotherInstanceStarted (quotedIfContainsSpaces (filename));
return YES;
}
return NO;
}
static void application_openFiles (id /*self*/, SEL, NSApplication*, NSArray* filenames)
{
if (auto* 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)
static void focusChanged()
{
if (appFocusChangeCallback != nullptr)
(*appFocusChangeCallback)();
}
static void getUrl_withReplyEvent (id /*self*/, SEL, NSAppleEventDescriptor* event, NSAppleEventDescriptor*)
{
if (auto* app = JUCEApplicationBase::getInstance())
app->anotherInstanceStarted (quotedIfContainsSpaces ([[event paramDescriptorForKeyword: keyDirectObject] stringValue]));
}
static String quotedIfContainsSpaces (NSString* file)
{
String s (nsStringToJuce (file));
if (s.containsChar (' '))
s = s.quoted ('"');
return s;
}
};
};
//==============================================================================
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_PROJUCER_LIVE_BUILD
runDispatchLoopUntil (std::numeric_limits<int>::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]);
JUCEApplicationBase::sendUnhandledException (&ex, __FILE__, __LINE__);
}
@finally
{
}
#else
[NSApp run];
#endif
#endif
}
}
}
static void shutdownNSApp()
{
[NSApp stop: nil];
[NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1];
}
void MessageManager::stopDispatchLoop()
{
#if JUCE_PROJUCER_LIVE_BUILD
quitMessagePosted = true;
#else
if (isThisTheMessageThread())
{
quitMessagePosted = true;
shutdownNSApp();
}
else
{
struct QuitCallback : public CallbackMessage
{
QuitCallback() {}
void messageCallback() override { MessageManager::getInstance()->stopDispatchLoop(); }
};
(new QuitCallback())->post();
}
#endif
}
#if JUCE_MODAL_LOOPS_PERMITTED
bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor)
{
jassert (millisecondsToRunFor >= 0);
jassert (isThisTheMessageThread()); // must only be called by the message thread
uint32 endTime = Time::getMillisecondCounter() + (uint32) millisecondsToRunFor;
while (! quitMessagePosted)
{
JUCE_AUTORELEASEPOOL
{
CFRunLoopRunInMode (kCFRunLoopDefaultMode, 0.001, true);
NSEvent* e = [NSApp nextEventMatchingMask: NSEventMaskAny
untilDate: [NSDate dateWithTimeIntervalSinceNow: 0.001]
inMode: NSDefaultRunLoopMode
dequeue: YES];
if (e != nil && (isEventBlockedByModalComps == nullptr || ! (*isEventBlockedByModalComps) (e)))
[NSApp sendEvent: e];
if (Time::getMillisecondCounter() >= endTime)
break;
}
}
return ! quitMessagePosted;
}
#endif
//==============================================================================
void initialiseNSApplication();
void initialiseNSApplication()
{
JUCE_AUTORELEASEPOOL
{
[NSApplication sharedApplication];
}
}
static AppDelegate* appDelegate = nullptr;
void MessageManager::doPlatformSpecificInitialisation()
{
if (appDelegate == nil)
appDelegate = new AppDelegate();
}
void MessageManager::doPlatformSpecificShutdown()
{
delete appDelegate;
appDelegate = nullptr;
}
bool MessageManager::postMessageToSystemQueue (MessageBase* message)
{
jassert (appDelegate != nil);
appDelegate->messageQueue.post (message);
return true;
}
void MessageManager::broadcastMessage (const String& message)
{
NSDictionary* info = [NSDictionary dictionaryWithObject: juceStringToNS (message)
forKey: nsStringLiteral ("message")];
[[NSDistributedNotificationCenter defaultCenter] postNotificationName: AppDelegate::getBroadcastEventName()
object: nil
userInfo: info];
}
// Special function used by some plugin classes to re-post carbon events
void __attribute__ ((visibility("default"))) repostCurrentNSEvent();
void __attribute__ ((visibility("default"))) 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();
}
//==============================================================================
#if JUCE_MAC
struct MountedVolumeListChangeDetector::Pimpl
{
Pimpl (MountedVolumeListChangeDetector& d) : owner (d)
{
static ObserverClass cls;
delegate = [cls.createInstance() init];
ObserverClass::setOwner (delegate, this);
NSNotificationCenter* nc = [[NSWorkspace sharedWorkspace] notificationCenter];
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidMountNotification object: nil];
[nc addObserver: delegate selector: @selector (changed:) name: NSWorkspaceDidUnmountNotification object: nil];
}
~Pimpl()
{
[[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver: delegate];
[delegate release];
}
private:
MountedVolumeListChangeDetector& owner;
id delegate;
struct ObserverClass : public ObjCClass<NSObject>
{
ObserverClass() : ObjCClass<NSObject> ("JUCEDriveObserver_")
{
addIvar<Pimpl*> ("owner");
addMethod (@selector (changed:), changed, "v@:@");
addProtocol (@protocol (NSTextInput));
registerClass();
}
static Pimpl* getOwner (id self) { return getIvar<Pimpl*> (self, "owner"); }
static void setOwner (id self, Pimpl* owner) { object_setInstanceVariable (self, "owner", owner); }
static void changed (id self, SEL, NSNotification*)
{
getOwner (self)->owner.mountedVolumeListChanged();
}
};
};
MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); }
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
#endif
} // namespace juce

+ 0
- 105
source/modules/juce_events/native/juce_osx_MessageQueue.h View File

@@ -1,105 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/* An internal message pump class used in OSX and iOS. */
class MessageQueue
{
public:
MessageQueue()
{
#if JUCE_IOS
runLoop = CFRunLoopGetCurrent();
#else
runLoop = CFRunLoopGetMain();
#endif
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);
CFRunLoopAddSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
}
~MessageQueue() noexcept
{
CFRunLoopRemoveSource (runLoop, runLoopSource, kCFRunLoopCommonModes);
CFRunLoopSourceInvalidate (runLoopSource);
CFRelease (runLoopSource);
}
void post (MessageManager::MessageBase* const message)
{
messages.add (message);
wakeUp();
}
private:
ReferenceCountedArray<MessageManager::MessageBase, CriticalSection> messages;
CFRunLoopRef runLoop;
CFRunLoopSourceRef runLoopSource;
void wakeUp() noexcept
{
CFRunLoopSourceSignal (runLoopSource);
CFRunLoopWakeUp (runLoop);
}
bool deliverNextMessage()
{
const MessageManager::MessageBase::Ptr nextMessage (messages.removeAndReturn (0));
if (nextMessage == nullptr)
return false;
JUCE_AUTORELEASEPOOL
{
JUCE_TRY
{
nextMessage->messageCallback();
}
JUCE_CATCH_EXCEPTION
}
return true;
}
void runLoopCallback() noexcept
{
for (int i = 4; --i >= 0;)
if (! deliverNextMessage())
return;
wakeUp();
}
static void runLoopSourceCallback (void* info) noexcept
{
static_cast<MessageQueue*> (info)->runLoopCallback();
}
};
} // namespace juce

+ 0
- 135
source/modules/juce_events/native/juce_win32_HiddenMessageWindow.h View File

@@ -1,135 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
class HiddenMessageWindow
{
public:
HiddenMessageWindow (const TCHAR* const messageWindowName, WNDPROC wndProc)
{
String className ("JUCE_");
className << String::toHexString (Time::getHighResolutionTicks());
HMODULE moduleHandle = (HMODULE) Process::getCurrentModuleInstanceHandle();
WNDCLASSEX wc = { 0 };
wc.cbSize = sizeof (wc);
wc.lpfnWndProc = wndProc;
wc.cbWndExtra = 4;
wc.hInstance = moduleHandle;
wc.lpszClassName = className.toWideCharPointer();
atom = RegisterClassEx (&wc);
jassert (atom != 0);
hwnd = CreateWindow (getClassNameFromAtom(), messageWindowName,
0, 0, 0, 0, 0, 0, 0, moduleHandle, 0);
jassert (hwnd != 0);
}
~HiddenMessageWindow()
{
DestroyWindow (hwnd);
UnregisterClass (getClassNameFromAtom(), 0);
}
inline HWND getHWND() const noexcept { return hwnd; }
private:
ATOM atom;
HWND hwnd;
LPCTSTR getClassNameFromAtom() noexcept { return (LPCTSTR) (pointer_sized_uint) atom; }
};
//==============================================================================
class JuceWindowIdentifier
{
public:
static bool isJUCEWindow (HWND hwnd) noexcept
{
return GetWindowLongPtr (hwnd, GWLP_USERDATA) == getImprobableWindowNumber();
}
static void setAsJUCEWindow (HWND hwnd, bool isJuceWindow) noexcept
{
SetWindowLongPtr (hwnd, GWLP_USERDATA, isJuceWindow ? getImprobableWindowNumber() : 0);
}
private:
static LONG_PTR getImprobableWindowNumber() noexcept
{
static auto number = (LONG_PTR) Random().nextInt64();
return number;
}
};
//==============================================================================
class DeviceChangeDetector : private Timer
{
public:
DeviceChangeDetector (const wchar_t* const name)
: messageWindow (name, (WNDPROC) deviceChangeEventCallback)
{
SetWindowLongPtr (messageWindow.getHWND(), GWLP_USERDATA, (LONG_PTR) this);
}
virtual ~DeviceChangeDetector() {}
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;
static LRESULT CALLBACK deviceChangeEventCallback (HWND h, const UINT message,
const WPARAM wParam, const LPARAM lParam)
{
if (message == WM_DEVICECHANGE
&& (wParam == 0x8000 /*DBT_DEVICEARRIVAL*/
|| wParam == 0x8004 /*DBT_DEVICEREMOVECOMPLETE*/
|| wParam == 0x0007 /*DBT_DEVNODES_CHANGED*/))
{
((DeviceChangeDetector*) GetWindowLongPtr (h, GWLP_USERDATA))
->triggerAsyncDeviceChangeCallback();
}
return DefWindowProc (h, message, wParam, lParam);
}
void timerCallback() override
{
stopTimer();
systemDeviceChanged();
}
};
} // namespace juce

+ 0
- 232
source/modules/juce_events/native/juce_win32_Messaging.cpp View File

@@ -1,232 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
extern HWND juce_messageWindowHandle;
typedef bool (*CheckEventBlockedByModalComps) (const MSG&);
CheckEventBlockedByModalComps isEventBlockedByModalComps = nullptr;
//==============================================================================
namespace WindowsMessageHelpers
{
const unsigned int customMessageID = WM_USER + 123;
const unsigned int broadcastMessageMagicNumber = 0xc403;
const TCHAR messageWindowName[] = _T("JUCEWindow");
ScopedPointer<HiddenMessageWindow> messageWindow;
void dispatchMessageFromLParam (LPARAM lParam)
{
if (MessageManager::MessageBase* message = reinterpret_cast<MessageManager::MessageBase*> (lParam))
{
JUCE_TRY
{
message->messageCallback();
}
JUCE_CATCH_EXCEPTION
message->decReferenceCount();
}
}
BOOL CALLBACK broadcastEnumWindowProc (HWND hwnd, LPARAM lParam)
{
if (hwnd != juce_messageWindowHandle)
{
TCHAR windowName[64] = { 0 }; // no need to read longer strings than this
GetWindowText (hwnd, windowName, 63);
if (String (windowName) == messageWindowName)
reinterpret_cast<Array<HWND>*> (lParam)->add (hwnd);
}
return TRUE;
}
void handleBroadcastMessage (const COPYDATASTRUCT* const data)
{
if (data != nullptr && data->dwData == broadcastMessageMagicNumber)
{
struct BroadcastMessage : public CallbackMessage
{
BroadcastMessage (CharPointer_UTF32 text, size_t length) : message (text, length) {}
void messageCallback() override { MessageManager::getInstance()->deliverBroadcastMessage (message); }
String message;
};
(new BroadcastMessage (CharPointer_UTF32 ((const CharPointer_UTF32::CharType*) data->lpData),
data->cbData / sizeof (CharPointer_UTF32::CharType)))
->post();
}
}
//==============================================================================
LRESULT CALLBACK messageWndProc (HWND h, const UINT message, const WPARAM wParam, const LPARAM lParam) noexcept
{
if (h == juce_messageWindowHandle)
{
if (message == customMessageID)
{
// (These are trapped early in our dispatch loop, but must also be checked
// here in case some 3rd-party code is running the dispatch loop).
dispatchMessageFromLParam (lParam);
return 0;
}
if (message == WM_COPYDATA)
{
handleBroadcastMessage (reinterpret_cast<const COPYDATASTRUCT*> (lParam));
return 0;
}
}
return DefWindowProc (h, message, wParam, lParam);
}
}
#if JUCE_MODULE_AVAILABLE_juce_gui_extra && ! JUCE_MINGW
LRESULT juce_offerEventToActiveXControl (::MSG&);
#endif
//==============================================================================
bool MessageManager::dispatchNextMessageOnSystemQueue (const bool returnIfNoPendingMessages)
{
using namespace WindowsMessageHelpers;
MSG m;
if (returnIfNoPendingMessages && ! PeekMessage (&m, (HWND) 0, 0, 0, PM_NOREMOVE))
return false;
if (GetMessage (&m, (HWND) 0, 0, 0) >= 0)
{
#if JUCE_MODULE_AVAILABLE_juce_gui_extra && ! JUCE_MINGW
if (juce_offerEventToActiveXControl (m) != S_FALSE)
return true;
#endif
if (m.message == customMessageID && m.hwnd == juce_messageWindowHandle)
{
dispatchMessageFromLParam (m.lParam);
}
else if (m.message == WM_QUIT)
{
if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance())
app->systemRequestedQuit();
}
else if (isEventBlockedByModalComps == nullptr || ! isEventBlockedByModalComps (m))
{
if ((m.message == WM_LBUTTONDOWN || m.message == WM_RBUTTONDOWN)
&& ! JuceWindowIdentifier::isJUCEWindow (m.hwnd))
{
// if it's someone else's window being clicked on, and the focus is
// currently on a juce window, pass the kb focus over..
HWND currentFocus = GetFocus();
if (currentFocus == 0 || JuceWindowIdentifier::isJUCEWindow (currentFocus))
SetFocus (m.hwnd);
}
TranslateMessage (&m);
DispatchMessage (&m);
}
}
return true;
}
bool MessageManager::postMessageToSystemQueue (MessageManager::MessageBase* const message)
{
message->incReferenceCount();
return PostMessage (juce_messageWindowHandle, WindowsMessageHelpers::customMessageID, 0, (LPARAM) message) != 0;
}
void MessageManager::broadcastMessage (const String& value)
{
const String localCopy (value);
Array<HWND> windows;
EnumWindows (&WindowsMessageHelpers::broadcastEnumWindowProc, (LPARAM) &windows);
for (int i = windows.size(); --i >= 0;)
{
COPYDATASTRUCT data;
data.dwData = WindowsMessageHelpers::broadcastMessageMagicNumber;
data.cbData = (localCopy.length() + 1) * sizeof (CharPointer_UTF32::CharType);
data.lpData = (void*) localCopy.toUTF32().getAddress();
DWORD_PTR result;
SendMessageTimeout (windows.getUnchecked (i), WM_COPYDATA,
(WPARAM) juce_messageWindowHandle,
(LPARAM) &data,
SMTO_BLOCK | SMTO_ABORTIFHUNG, 8000, &result);
}
}
//==============================================================================
void MessageManager::doPlatformSpecificInitialisation()
{
OleInitialize (0);
using namespace WindowsMessageHelpers;
messageWindow = new HiddenMessageWindow (messageWindowName, (WNDPROC) messageWndProc);
juce_messageWindowHandle = messageWindow->getHWND();
}
void MessageManager::doPlatformSpecificShutdown()
{
WindowsMessageHelpers::messageWindow = nullptr;
OleUninitialize();
}
//==============================================================================
struct MountedVolumeListChangeDetector::Pimpl : private DeviceChangeDetector
{
Pimpl (MountedVolumeListChangeDetector& d) : DeviceChangeDetector (L"MountedVolumeList"), owner (d)
{
File::findFileSystemRoots (lastVolumeList);
}
void systemDeviceChanged() override
{
Array<File> newList;
File::findFileSystemRoots (newList);
if (lastVolumeList != newList)
{
lastVolumeList = newList;
owner.mountedVolumeListChanged();
}
}
MountedVolumeListChangeDetector& owner;
Array<File> lastVolumeList;
};
MountedVolumeListChangeDetector::MountedVolumeListChangeDetector() { pimpl = new Pimpl (*this); }
MountedVolumeListChangeDetector::~MountedVolumeListChangeDetector() {}
} // namespace juce

+ 0
- 26
source/modules/juce_events/native/juce_win32_WinRTWrapper.cpp View File

@@ -1,26 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
juce_ImplementSingleton (WinRTWrapper)
}

+ 0
- 131
source/modules/juce_events/native/juce_win32_WinRTWrapper.h View File

@@ -1,131 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class WinRTWrapper : public DeletedAtShutdown
{
public:
juce_DeclareSingleton (WinRTWrapper, true)
class ScopedHString
{
public:
ScopedHString (String str)
{
if (WinRTWrapper::getInstance()->isInitialised())
WinRTWrapper::getInstance()->createHString (str.toWideCharPointer(),
static_cast<uint32_t> (str.length()),
&hstr);
}
~ScopedHString()
{
if (WinRTWrapper::getInstance()->isInitialised() && hstr != nullptr)
WinRTWrapper::getInstance()->deleteHString (hstr);
}
HSTRING get() const noexcept
{
return hstr;
}
private:
HSTRING hstr = nullptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ScopedHString)
};
~WinRTWrapper()
{
if (winRTHandle != nullptr)
::FreeLibrary (winRTHandle);
}
String hStringToString (HSTRING hstr)
{
if (isInitialised())
if (const wchar_t* str = getHStringRawBuffer (hstr, nullptr))
return String (str);
return {};
}
bool isInitialised() const noexcept
{
return initialised;
}
template <class ComClass>
ComSmartPtr<ComClass> getWRLFactory (const wchar_t* runtimeClassID)
{
ComSmartPtr<ComClass> comPtr;
if (isInitialised())
{
ScopedHString classID (runtimeClassID);
if (classID.get() != nullptr)
roGetActivationFactory (classID.get(), __uuidof (ComClass), (void**) comPtr.resetAndGetPointerAddress());
}
return comPtr;
}
private:
WinRTWrapper()
{
winRTHandle = ::LoadLibraryA ("api-ms-win-core-winrt-l1-1-0");
if (winRTHandle == nullptr)
return;
roInitialize = (RoInitializeFuncPtr) ::GetProcAddress (winRTHandle, "RoInitialize");
createHString = (WindowsCreateStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsCreateString");
deleteHString = (WindowsDeleteStringFuncPtr) ::GetProcAddress (winRTHandle, "WindowsDeleteString");
getHStringRawBuffer = (WindowsGetStringRawBufferFuncPtr) ::GetProcAddress (winRTHandle, "WindowsGetStringRawBuffer");
roGetActivationFactory = (RoGetActivationFactoryFuncPtr) ::GetProcAddress (winRTHandle, "RoGetActivationFactory");
if (roInitialize == nullptr || createHString == nullptr || deleteHString == nullptr
|| getHStringRawBuffer == nullptr || roGetActivationFactory == nullptr)
return;
HRESULT status = roInitialize (1);
initialised = ! (status != S_OK && status != S_FALSE && status != 0x80010106L);
}
HMODULE winRTHandle = nullptr;
bool initialised = false;
typedef HRESULT (WINAPI* RoInitializeFuncPtr) (int);
typedef HRESULT (WINAPI* WindowsCreateStringFuncPtr) (LPCWSTR, UINT32, HSTRING*);
typedef HRESULT (WINAPI* WindowsDeleteStringFuncPtr) (HSTRING);
typedef PCWSTR (WINAPI* WindowsGetStringRawBufferFuncPtr) (HSTRING, UINT32*);
typedef HRESULT (WINAPI* RoGetActivationFactoryFuncPtr) (HSTRING, REFIID, void**);
RoInitializeFuncPtr roInitialize = nullptr;
WindowsCreateStringFuncPtr createHString = nullptr;
WindowsDeleteStringFuncPtr deleteHString = nullptr;
WindowsGetStringRawBufferFuncPtr getHStringRawBuffer = nullptr;
RoGetActivationFactoryFuncPtr roGetActivationFactory = nullptr;
};
} // namespace juce

+ 0
- 108
source/modules/juce_events/timers/juce_MultiTimer.cpp View File

@@ -1,108 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
struct MultiTimerCallback : public Timer
{
MultiTimerCallback (const int tid, MultiTimer& mt) noexcept
: owner (mt), timerID (tid)
{
}
void timerCallback() override
{
owner.timerCallback (timerID);
}
MultiTimer& owner;
const int timerID;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MultiTimerCallback)
};
//==============================================================================
MultiTimer::MultiTimer() noexcept {}
MultiTimer::MultiTimer (const MultiTimer&) noexcept {}
MultiTimer::~MultiTimer()
{
const SpinLock::ScopedLockType sl (timerListLock);
timers.clear();
}
//==============================================================================
Timer* MultiTimer::getCallback (int timerID) const noexcept
{
for (int i = timers.size(); --i >= 0;)
{
MultiTimerCallback* const t = static_cast<MultiTimerCallback*> (timers.getUnchecked(i));
if (t->timerID == timerID)
return t;
}
return nullptr;
}
void MultiTimer::startTimer (const int timerID, const int intervalInMilliseconds) noexcept
{
const SpinLock::ScopedLockType sl (timerListLock);
Timer* timer = getCallback (timerID);
if (timer == nullptr)
timers.add (timer = new MultiTimerCallback (timerID, *this));
timer->startTimer (intervalInMilliseconds);
}
void MultiTimer::stopTimer (const int timerID) noexcept
{
const SpinLock::ScopedLockType sl (timerListLock);
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
{
const SpinLock::ScopedLockType sl (timerListLock);
if (Timer* const t = getCallback (timerID))
return t->getTimerInterval();
return 0;
}
} // namespace juce

+ 0
- 123
source/modules/juce_events/timers/juce_MultiTimer.h View File

@@ -1,123 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A type of timer class that can run multiple timers with different frequencies,
all of which share a single callback.
This class is very similar to the Timer class, but allows you run multiple
separate timers, where each one has a unique ID number. The methods in this
class are exactly equivalent to those in Timer, but with the addition of
this ID number.
To use it, you need to create a subclass of MultiTimer, implementing the
timerCallback() method. Then you can start timers with startTimer(), and
each time the callback is triggered, it passes in the ID of the timer that
caused it.
@see Timer
*/
class JUCE_API MultiTimer
{
protected:
//==============================================================================
/** Creates a MultiTimer.
When created, no timers are running, so use startTimer() to start things off.
*/
MultiTimer() noexcept;
/** Creates a copy of another timer.
Note that this timer will not contain any running timers, even if the one you're
copying from was running.
*/
MultiTimer (const MultiTimer&) noexcept;
public:
//==============================================================================
/** Destructor. */
virtual ~MultiTimer();
//==============================================================================
/** The user-defined callback routine that actually gets called by each of the
timers that are running.
It's perfectly ok to call startTimer() or stopTimer() from within this
callback to change the subsequent intervals.
*/
virtual void timerCallback (int timerID) = 0;
//==============================================================================
/** Starts a timer and sets the length of interval required.
If the timer is already started, this will reset it, so the
time between calling this method and the next timer callback
will not be less than the interval length passed in.
@param timerID a unique Id number that identifies the timer to
start. This is the id that will be passed back
to the timerCallback() method when this timer is
triggered
@param intervalInMilliseconds the interval to use (any values less than 1 will be
rounded up to 1)
*/
void startTimer (int timerID, int intervalInMilliseconds) noexcept;
/** Stops a timer.
If a timer has been started with the given ID number, it will be cancelled.
No more callbacks will be made for the specified timer after this method returns.
If this is called from a different thread, any callbacks that may
be currently executing may be allowed to finish before the method
returns.
*/
void stopTimer (int timerID) 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;
/** Returns the interval for a specified timer ID.
@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;
//==============================================================================
private:
SpinLock timerListLock;
OwnedArray<Timer> timers;
Timer* getCallback (int) const noexcept;
MultiTimer& operator= (const MultiTimer&);
};
} // namespace juce

+ 0
- 371
source/modules/juce_events/timers/juce_Timer.cpp View File

@@ -1,371 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class Timer::TimerThread : private Thread,
private DeletedAtShutdown,
private AsyncUpdater
{
public:
typedef CriticalSection LockType; // (mysteriously, using a SpinLock here causes problems on some XP machines..)
TimerThread()
: Thread ("Juce Timer"),
firstTimer (nullptr)
{
triggerAsyncUpdate();
}
~TimerThread() noexcept
{
signalThreadShouldExit();
callbackArrived.signal();
stopThread (4000);
jassert (instance == this || instance == nullptr);
if (instance == this)
instance = nullptr;
}
void run() override
{
uint32 lastTime = Time::getMillisecondCounter();
MessageManager::MessageBase::Ptr messageToSend (new CallTimersMessage());
while (! threadShouldExit())
{
const uint32 now = Time::getMillisecondCounter();
const int elapsed = (int) (now >= lastTime ? (now - lastTime)
: (std::numeric_limits<uint32>::max() - (lastTime - now)));
lastTime = now;
const int timeUntilFirstTimer = getTimeUntilFirstTimer (elapsed);
if (timeUntilFirstTimer <= 0)
{
if (callbackArrived.wait (0))
{
// already a message in flight - do nothing..
}
else
{
messageToSend->post();
if (! callbackArrived.wait (300))
{
// Sometimes our message can get discarded by the OS (e.g. when running as an RTAS
// when the app has a modal loop), so this is how long to wait before assuming the
// message has been lost and trying again.
messageToSend->post();
}
continue;
}
}
// don't wait for too long because running this loop also helps keep the
// Time::getApproximateMillisecondTimer value stay up-to-date
wait (jlimit (1, 100, timeUntilFirstTimer));
}
}
void callTimers()
{
// avoid getting stuck in a loop if a timer callback repeatedly takes too long
const uint32 timeout = Time::getMillisecondCounter() + 100;
const LockType::ScopedLockType sl (lock);
while (firstTimer != nullptr && firstTimer->timerCountdownMs <= 0)
{
Timer* const t = firstTimer;
t->timerCountdownMs = t->timerPeriodMs;
removeTimer (t);
addTimer (t);
const LockType::ScopedUnlockType ul (lock);
JUCE_TRY
{
t->timerCallback();
}
JUCE_CATCH_EXCEPTION
if (Time::getMillisecondCounter() > timeout)
break;
}
callbackArrived.signal();
}
void callTimersSynchronously()
{
if (! isThreadRunning())
{
// (This is relied on by some plugins in cases where the MM has
// had to restart and the async callback never started)
cancelPendingUpdate();
triggerAsyncUpdate();
}
callTimers();
}
static inline void add (Timer* const tim) noexcept
{
if (instance == nullptr)
instance = new TimerThread();
instance->addTimer (tim);
}
static inline void remove (Timer* const tim) noexcept
{
if (instance != nullptr)
instance->removeTimer (tim);
}
static inline void resetCounter (Timer* const tim, const int newCounter) noexcept
{
if (instance != nullptr)
{
tim->timerCountdownMs = newCounter;
tim->timerPeriodMs = newCounter;
if ((tim->nextTimer != nullptr && tim->nextTimer->timerCountdownMs < tim->timerCountdownMs)
|| (tim->previousTimer != nullptr && tim->previousTimer->timerCountdownMs > tim->timerCountdownMs))
{
instance->removeTimer (tim);
instance->addTimer (tim);
}
}
}
static TimerThread* instance;
static LockType lock;
private:
Timer* volatile firstTimer;
WaitableEvent callbackArrived;
struct CallTimersMessage : public MessageManager::MessageBase
{
CallTimersMessage() {}
void messageCallback() override
{
if (instance != nullptr)
instance->callTimers();
}
};
//==============================================================================
void addTimer (Timer* const t) noexcept
{
#if JUCE_DEBUG
// 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;
if (i == nullptr || i->timerCountdownMs > t->timerCountdownMs)
{
t->nextTimer = firstTimer;
firstTimer = t;
}
else
{
while (i->nextTimer != nullptr && i->nextTimer->timerCountdownMs <= t->timerCountdownMs)
i = i->nextTimer;
jassert (i != nullptr);
t->nextTimer = i->nextTimer;
t->previousTimer = i;
i->nextTimer = t;
}
if (t->nextTimer != nullptr)
t->nextTimer->previousTimer = t;
jassert ((t->nextTimer == nullptr || t->nextTimer->timerCountdownMs >= t->timerCountdownMs)
&& (t->previousTimer == nullptr || t->previousTimer->timerCountdownMs <= t->timerCountdownMs));
notify();
}
void removeTimer (Timer* const t) noexcept
{
#if JUCE_DEBUG
// 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 (timerExists (t));
#endif
if (t->previousTimer != nullptr)
{
jassert (firstTimer != t);
t->previousTimer->nextTimer = t->nextTimer;
}
else
{
jassert (firstTimer == t);
firstTimer = t->nextTimer;
}
if (t->nextTimer != nullptr)
t->nextTimer->previousTimer = t->previousTimer;
t->nextTimer = nullptr;
t->previousTimer = nullptr;
}
int getTimeUntilFirstTimer (const int numMillisecsElapsed) const
{
const LockType::ScopedLockType sl (lock);
for (Timer* t = firstTimer; t != nullptr; t = t->nextTimer)
t->timerCountdownMs -= numMillisecsElapsed;
return firstTimer != nullptr ? firstTimer->timerCountdownMs : 1000;
}
void handleAsyncUpdate() override
{
startThread (7);
}
#if JUCE_DEBUG
bool timerExists (Timer* const t) const noexcept
{
for (Timer* tt = firstTimer; tt != nullptr; tt = tt->nextTimer)
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;
//==============================================================================
Timer::Timer() noexcept
: timerCountdownMs (0),
timerPeriodMs (0),
previousTimer (nullptr),
nextTimer (nullptr)
{
}
Timer::Timer (const Timer&) noexcept
: timerCountdownMs (0),
timerPeriodMs (0),
previousTimer (nullptr),
nextTimer (nullptr)
{
}
Timer::~Timer()
{
stopTimer();
}
void Timer::startTimer (const int interval) noexcept
{
// If you're calling this before (or after) the MessageManager is
// running, then you're not going to get any timer callbacks!
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
if (timerPeriodMs == 0)
{
timerCountdownMs = interval;
timerPeriodMs = jmax (1, interval);
TimerThread::add (this);
}
else
{
TimerThread::resetCounter (this, interval);
}
}
void Timer::startTimerHz (int timerFrequencyHz) noexcept
{
if (timerFrequencyHz > 0)
startTimer (1000 / timerFrequencyHz);
else
stopTimer();
}
void Timer::stopTimer() noexcept
{
const TimerThread::LockType::ScopedLockType sl (TimerThread::lock);
if (timerPeriodMs > 0)
{
TimerThread::remove (this);
timerPeriodMs = 0;
}
}
void JUCE_CALLTYPE Timer::callPendingTimersSynchronously()
{
if (TimerThread::instance != nullptr)
TimerThread::instance->callTimersSynchronously();
}
struct LambdaInvoker : private Timer
{
LambdaInvoker (int milliseconds, std::function<void()> f) : function (f)
{
startTimer (milliseconds);
}
void timerCallback() override
{
auto f = function;
delete this;
f();
}
std::function<void()> function;
JUCE_DECLARE_NON_COPYABLE (LambdaInvoker)
};
void JUCE_CALLTYPE Timer::callAfterDelay (int milliseconds, std::function<void()> f)
{
new LambdaInvoker (milliseconds, f);
}
} // namespace juce

+ 0
- 135
source/modules/juce_events/timers/juce_Timer.h View File

@@ -1,135 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
The code included in this file is provided under the terms of the ISC license
http://www.isc.org/downloads/software-support-policy/isc-license. 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 IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Makes repeated callbacks to a virtual method at a specified time interval.
A Timer's timerCallback() method will be repeatedly called at a given
interval. When you create a Timer object, it will do nothing until the
startTimer() method is called, which will cause the message thread to
start making callbacks at the specified interval, until stopTimer() is called
or the object is deleted.
The time interval isn't guaranteed to be precise to any more than maybe
10-20ms, and the intervals may end up being much longer than requested if the
system is busy. Because the callbacks are made by the main message thread,
anything that blocks the message queue for a period of time will also prevent
any timers from running until it can carry on.
If you need to have a single callback that is shared by multiple timers with
different frequencies, then the MultiTimer class allows you to do that - its
structure is very similar to the Timer class, but contains multiple timers
internally, each one identified by an ID number.
@see HighResolutionTimer, MultiTimer
*/
class JUCE_API Timer
{
protected:
//==============================================================================
/** Creates a Timer.
When created, the timer is stopped, so use startTimer() to get it going.
*/
Timer() noexcept;
/** Creates a copy of another timer.
Note that this timer won't be started, even if the one you're copying
is running.
*/
Timer (const Timer&) noexcept;
public:
//==============================================================================
/** Destructor. */
virtual ~Timer();
//==============================================================================
/** The user-defined callback routine that actually gets called periodically.
It's perfectly ok to call startTimer() or stopTimer() from within this
callback to change the subsequent intervals.
*/
virtual void timerCallback() = 0;
//==============================================================================
/** Starts the timer and sets the length of interval required.
If the timer is already started, this will reset it, so the
time between calling this method and the next timer callback
will not be less than the interval length passed in.
@param intervalInMilliseconds the interval to use (any value less
than 1 will be rounded up to 1)
*/
void startTimer (int intervalInMilliseconds) noexcept;
/** Starts the timer with an interval specified in Hertz.
This is effectively the same as calling startTimer (1000 / timerFrequencyHz).
*/
void startTimerHz (int timerFrequencyHz) noexcept;
/** Stops the timer.
No more timer callbacks will be triggered after this method returns.
Note that if you call this from a background thread while the message-thread
is already in the middle of your callback, then this method will cancel any
future timer callbacks, but it will return without waiting for the current one
to finish. The current callback will continue, possibly still running some of
your timer code after this method has returned.
*/
void stopTimer() noexcept;
//==============================================================================
/** Returns true if the timer is currently running. */
bool isTimerRunning() const noexcept { return timerPeriodMs > 0; }
/** Returns the timer's interval.
@returns the timer's interval in milliseconds if it's running, or 0 if it's not.
*/
int getTimerInterval() const noexcept { return timerPeriodMs; }
//==============================================================================
/** Invokes a lambda after a given number of milliseconds. */
static void JUCE_CALLTYPE callAfterDelay (int milliseconds, std::function<void()> functionToCall);
//==============================================================================
/** For internal use only: invokes any timers that need callbacks.
Don't call this unless you really know what you're doing!
*/
static void JUCE_CALLTYPE callPendingTimersSynchronously();
private:
class TimerThread;
friend class TimerThread;
int timerCountdownMs, timerPeriodMs; // NB: these member variable names are a little verbose
Timer* previousTimer, *nextTimer; // to reduce risk of name-clashes with user subclasses
Timer& operator= (const Timer&) JUCE_DELETED_FUNCTION;
};
} // namespace juce

+ 0
- 123
source/modules/juce_graphics/Makefile View File

@@ -1,123 +0,0 @@
#!/usr/bin/make -f
# Makefile for juce_graphics #
# -------------------------- #
# Created by falkTX
#

CWD=../..
MODULENAME=juce_graphics
include ../Makefile.mk

# ----------------------------------------------------------------------------------------------------------------------------

BUILD_CXX_FLAGS += $(JUCE_GRAPHICS_FLAGS) -I..

ifeq ($(WIN32),true)
BUILD_CXX_FLAGS += -Wno-missing-field-initializers -Wno-strict-overflow
endif

# ----------------------------------------------------------------------------------------------------------------------------

ifeq ($(MACOS),true)
OBJS = $(OBJDIR)/$(MODULENAME).mm.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).mm.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).mm.posix64.o
else
OBJS = $(OBJDIR)/$(MODULENAME).cpp.o
OBJS_posix32 = $(OBJDIR)/$(MODULENAME).cpp.posix32.o
OBJS_posix64 = $(OBJDIR)/$(MODULENAME).cpp.posix64.o
endif
OBJS_win32 = $(OBJDIR)/$(MODULENAME).cpp.win32.o
OBJS_win64 = $(OBJDIR)/$(MODULENAME).cpp.win64.o

# ----------------------------------------------------------------------------------------------------------------------------

all: $(MODULEDIR)/$(MODULENAME).a
posix32: $(MODULEDIR)/$(MODULENAME).posix32.a
posix64: $(MODULEDIR)/$(MODULENAME).posix64.a
win32: $(MODULEDIR)/$(MODULENAME).win32.a
win64: $(MODULEDIR)/$(MODULENAME).win64.a

# ----------------------------------------------------------------------------------------------------------------------------

clean:
rm -f $(OBJDIR)/*.o $(MODULEDIR)/$(MODULENAME)*.a

debug:
$(MAKE) DEBUG=true

# ----------------------------------------------------------------------------------------------------------------------------

$(MODULEDIR)/$(MODULENAME).a: $(OBJS)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix32.a: $(OBJS_posix32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).posix64.a: $(OBJS_posix64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).posix64.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win32.a: $(OBJS_win32)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win32.a"
@rm -f $@
@$(AR) crs $@ $^

$(MODULEDIR)/$(MODULENAME).win64.a: $(OBJS_win64)
-@mkdir -p $(MODULEDIR)
@echo "Creating $(MODULENAME).win64.a"
@rm -f $@
@$(AR) crs $@ $^

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).cpp.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@

$(OBJDIR)/$(MODULENAME).cpp.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

$(OBJDIR)/$(MODULENAME).mm.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $<"
@$(CXX) $< $(BUILD_CXX_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%32.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (32bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -ObjC++ -c -o $@

$(OBJDIR)/$(MODULENAME).mm.%64.o: $(MODULENAME).cpp
-@mkdir -p $(OBJDIR)
@echo "Compiling $< (64bit)"
@$(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -ObjC++ -c -o $@

# ----------------------------------------------------------------------------------------------------------------------------

-include $(OBJS:%.o=%.d)
-include $(OBJS_posix32:%.o=%.d)
-include $(OBJS_posix64:%.o=%.d)
-include $(OBJS_win32:%.o=%.d)
-include $(OBJS_win64:%.o=%.d)

# ----------------------------------------------------------------------------------------------------------------------------

+ 0
- 468
source/modules/juce_graphics/colour/juce_Colour.cpp View File

@@ -1,468 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace ColourHelpers
{
static uint8 floatToUInt8 (const float n) noexcept
{
return n <= 0.0f ? 0 : (n >= 1.0f ? 255 : static_cast<uint8> (n * 255.996f));
}
//==============================================================================
struct HSB
{
HSB (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)
{
saturation = (hi - lo) / (float) hi;
if (saturation > 0)
{
const float invDiff = 1.0f / (hi - lo);
const float red = (hi - r) * invDiff;
const float green = (hi - g) * invDiff;
const float blue = (hi - b) * invDiff;
if (r == hi)
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 = 0;
}
}
else
{
saturation = hue = 0;
}
brightness = hi / 255.0f;
}
Colour toColour (Colour original) const noexcept
{
return Colour (hue, saturation, brightness, original.getAlpha());
}
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);
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
{
YIQ (Colour c) noexcept
{
const float r = c.getFloatRed();
const float g = c.getFloatGreen();
const float b = c.getFloatBlue();
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();
}
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);
}
float y, i, q, alpha;
};
}
//==============================================================================
Colour::Colour() noexcept
: argb (0, 0, 0, 0)
{
}
Colour::Colour (const Colour& other) noexcept
: argb (other.argb)
{
}
Colour& Colour::operator= (const Colour& other) noexcept
{
argb = other.argb;
return *this;
}
bool Colour::operator== (const Colour& other) const noexcept { return argb.getNativeARGB() == other.argb.getNativeARGB(); }
bool Colour::operator!= (const Colour& other) const noexcept { return argb.getNativeARGB() != other.argb.getNativeARGB(); }
//==============================================================================
Colour::Colour (const uint32 col) noexcept
: argb ((col >> 24) & 0xff, (col >> 16) & 0xff, (col >> 8) & 0xff, col & 0xff)
{
}
Colour::Colour (const uint8 red, const uint8 green, const uint8 blue) noexcept
{
argb.setARGB (0xff, red, green, blue);
}
Colour Colour::fromRGB (const uint8 red, const uint8 green, const uint8 blue) noexcept
{
return Colour (red, green, blue);
}
Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
{
argb.setARGB (alpha, red, green, blue);
}
Colour Colour::fromRGBA (const uint8 red, const uint8 green, const uint8 blue, const uint8 alpha) noexcept
{
return Colour (red, green, blue, alpha);
}
Colour::Colour (const uint8 red, const uint8 green, const uint8 blue, const float alpha) noexcept
{
argb.setARGB (ColourHelpers::floatToUInt8 (alpha), red, green, blue);
}
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);
}
Colour::Colour (const float hue, const float saturation, const float brightness, const float alpha) noexcept
: argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, ColourHelpers::floatToUInt8 (alpha)))
{
}
Colour Colour::fromHSV (const float hue, const float saturation, const float brightness, const float alpha) noexcept
{
return Colour (hue, saturation, brightness, alpha);
}
Colour::Colour (const float hue, const float saturation, const float brightness, const uint8 alpha) noexcept
: argb (ColourHelpers::HSB::toRGB (hue, saturation, brightness, alpha))
{
}
Colour::Colour (PixelARGB argb_) noexcept
: argb (argb_)
{
}
Colour::Colour (PixelRGB rgb) noexcept
: argb (Colour (rgb.getInARGBMaskOrder()).argb)
{
}
Colour::Colour (PixelAlpha alpha) noexcept
: argb (Colour (alpha.getInARGBMaskOrder()).argb)
{
}
Colour::~Colour() noexcept
{
}
//==============================================================================
const PixelARGB Colour::getPixelARGB() const noexcept
{
PixelARGB p (argb);
p.premultiply();
return p;
}
uint32 Colour::getARGB() const noexcept
{
return argb.getInARGBMaskOrder();
}
//==============================================================================
bool Colour::isTransparent() const noexcept
{
return getAlpha() == 0;
}
bool Colour::isOpaque() const noexcept
{
return getAlpha() == 0xff;
}
Colour Colour::withAlpha (const uint8 newAlpha) const noexcept
{
PixelARGB newCol (argb);
newCol.setAlpha (newAlpha);
return Colour (newCol);
}
Colour Colour::withAlpha (const float newAlpha) const noexcept
{
jassert (newAlpha >= 0 && newAlpha <= 1.0f);
PixelARGB newCol (argb);
newCol.setAlpha (ColourHelpers::floatToUInt8 (newAlpha));
return Colour (newCol);
}
Colour Colour::withMultipliedAlpha (const float alphaMultiplier) const noexcept
{
jassert (alphaMultiplier >= 0);
PixelARGB newCol (argb);
newCol.setAlpha ((uint8) jmin (0xff, roundToInt (alphaMultiplier * newCol.getAlpha())));
return Colour (newCol);
}
//==============================================================================
Colour Colour::overlaidWith (Colour src) const noexcept
{
const int destAlpha = getAlpha();
if (destAlpha <= 0)
return src;
const int invA = 0xff - (int) src.getAlpha();
const int resA = 0xff - (((0xff - destAlpha) * invA) >> 8);
if (resA <= 0)
return *this;
const int da = (invA * destAlpha) / resA;
return Colour ((uint8) (src.getRed() + ((((int) getRed() - src.getRed()) * da) >> 8)),
(uint8) (src.getGreen() + ((((int) getGreen() - src.getGreen()) * da) >> 8)),
(uint8) (src.getBlue() + ((((int) getBlue() - src.getBlue()) * da) >> 8)),
(uint8) resA);
}
Colour Colour::interpolatedWith (Colour other, float proportionOfOther) const noexcept
{
if (proportionOfOther <= 0)
return *this;
if (proportionOfOther >= 1.0f)
return other;
PixelARGB c1 (getPixelARGB());
const PixelARGB c2 (other.getPixelARGB());
c1.tween (c2, (uint32) roundToInt (proportionOfOther * 255.0f));
c1.unpremultiply();
return Colour (c1);
}
//==============================================================================
float Colour::getFloatRed() const noexcept { return getRed() / 255.0f; }
float Colour::getFloatGreen() const noexcept { return getGreen() / 255.0f; }
float Colour::getFloatBlue() const noexcept { return getBlue() / 255.0f; }
float Colour::getFloatAlpha() const noexcept { return getAlpha() / 255.0f; }
//==============================================================================
void Colour::getHSB (float& h, float& s, float& v) const noexcept
{
const ColourHelpers::HSB hsb (*this);
h = hsb.hue;
s = hsb.saturation;
v = hsb.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 { 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
{
ColourHelpers::HSB hsb (*this);
hsb.hue += amountToRotate;
return hsb.toColour (*this);
}
Colour Colour::withMultipliedSaturation (const float amount) const noexcept
{
ColourHelpers::HSB hsb (*this);
hsb.saturation = jmin (1.0f, hsb.saturation * amount);
return hsb.toColour (*this);
}
Colour Colour::withMultipliedBrightness (const float amount) const noexcept
{
ColourHelpers::HSB hsb (*this);
hsb.brightness = jmin (1.0f, hsb.brightness * amount);
return hsb.toColour (*this);
}
//==============================================================================
Colour Colour::brighter (float amount) const noexcept
{
amount = 1.0f / (1.0f + amount);
return Colour ((uint8) (255 - (amount * (255 - getRed()))),
(uint8) (255 - (amount * (255 - getGreen()))),
(uint8) (255 - (amount * (255 - getBlue()))),
getAlpha());
}
Colour Colour::darker (float amount) const noexcept
{
amount = 1.0f / (1.0f + amount);
return Colour ((uint8) (amount * getRed()),
(uint8) (amount * getGreen()),
(uint8) (amount * getBlue()),
getAlpha());
}
//==============================================================================
Colour Colour::greyLevel (const float brightness) noexcept
{
const uint8 level = ColourHelpers::floatToUInt8 (brightness);
return Colour (level, level, level);
}
//==============================================================================
Colour Colour::contrasting (const float amount) const noexcept
{
return overlaidWith ((getPerceivedBrightness() >= 0.5f
? Colours::black
: Colours::white).withAlpha (amount));
}
Colour Colour::contrasting (Colour target, float minContrast) const noexcept
{
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;
for (float i = 0.0f; i < 1.0f; i += 0.02f)
{
const float d1 = std::abs (i - b1);
const float d2 = std::abs (i - b2);
const float dist = jmin (d1, d2, 1.0f - d1, 1.0f - d2);
if (dist > bestDist)
{
best = i;
bestDist = dist;
}
}
return colour1.overlaidWith (colour2.withMultipliedAlpha (0.5f))
.withBrightness (best);
}
//==============================================================================
String Colour::toString() const
{
return String::toHexString ((int) argb.getInARGBMaskOrder());
}
Colour Colour::fromString (StringRef encodedColourString)
{
return Colour ((uint32) CharacterFunctions::HexParser<int>::parse (encodedColourString.text));
}
String Colour::toDisplayString (const bool includeAlphaValue) const
{
return String::toHexString ((int) (argb.getInARGBMaskOrder() & (includeAlphaValue ? 0xffffffff : 0xffffff)))
.paddedLeft ('0', includeAlphaValue ? 8 : 6)
.toUpperCase();
}
} // namespace juce

+ 0
- 367
source/modules/juce_graphics/colour/juce_Colour.h View File

@@ -1,367 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a colour, also including a transparency value.
The colour is stored internally as unsigned 8-bit red, green, blue and alpha values.
*/
class JUCE_API Colour
{
public:
//==============================================================================
/** Creates a transparent black colour. */
Colour() noexcept;
/** Creates a copy of another Colour object. */
Colour (const Colour& other) noexcept;
/** Creates a colour from a 32-bit ARGB value.
The format of this number is:
((alpha << 24) | (red << 16) | (green << 8) | blue).
All components in the range 0x00 to 0xff.
An alpha of 0x00 is completely transparent, alpha of 0xff is opaque.
@see getPixelARGB
*/
explicit Colour (uint32 argb) noexcept;
/** Creates an opaque colour using 8-bit red, green and blue values */
Colour (uint8 red,
uint8 green,
uint8 blue) noexcept;
/** Creates an opaque colour using 8-bit red, green and blue values */
static Colour fromRGB (uint8 red,
uint8 green,
uint8 blue) noexcept;
/** Creates a colour using 8-bit red, green, blue and alpha values. */
Colour (uint8 red,
uint8 green,
uint8 blue,
uint8 alpha) noexcept;
/** Creates a colour using 8-bit red, green, blue and alpha values. */
static Colour fromRGBA (uint8 red,
uint8 green,
uint8 blue,
uint8 alpha) noexcept;
/** Creates a colour from 8-bit red, green, and blue values, and a floating-point alpha.
Alpha of 0.0 is transparent, alpha of 1.0f is opaque.
Values outside the valid range will be clipped.
*/
Colour (uint8 red,
uint8 green,
uint8 blue,
float alpha) noexcept;
/** Creates a colour using floating point red, green, blue and alpha values.
Numbers outside the range 0..1 will be clipped.
*/
static Colour fromFloatRGBA (float red,
float green,
float blue,
float alpha) noexcept;
/** Creates a colour using floating point hue, saturation and brightness values, and an 8-bit alpha.
The floating point values must be between 0.0 and 1.0.
An alpha of 0x00 is completely transparent, alpha of 0xff is opaque.
Values outside the valid range will be clipped.
*/
Colour (float hue,
float saturation,
float brightness,
uint8 alpha) noexcept;
/** Creates a colour using floating point hue, saturation, brightness and alpha values.
All values must be between 0.0 and 1.0.
Numbers outside the valid range will be clipped.
*/
Colour (float hue,
float saturation,
float brightness,
float alpha) noexcept;
/** Creates a colour using a PixelARGB object. This function assumes that the argb pixel is
not premultiplied.
*/
Colour (PixelARGB argb) noexcept;
/** Creates a colour using a PixelRGB object.
*/
Colour (PixelRGB rgb) noexcept;
/** Creates a colour using a PixelAlpha object.
*/
Colour (PixelAlpha alpha) noexcept;
/** Creates a colour using floating point hue, saturation and brightness values, and an 8-bit alpha.
The floating point values must be between 0.0 and 1.0.
An alpha of 0x00 is completely transparent, alpha of 0xff is opaque.
Values outside the valid range will be clipped.
*/
static Colour fromHSV (float hue,
float saturation,
float brightness,
float alpha) noexcept;
/** Destructor. */
~Colour() noexcept;
/** Copies another Colour object. */
Colour& operator= (const Colour& other) noexcept;
/** Compares two colours. */
bool operator== (const Colour& other) const noexcept;
/** Compares two colours. */
bool operator!= (const Colour& other) const noexcept;
//==============================================================================
/** 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;
/** Returns a premultiplied ARGB pixel object that represents this colour.
*/
const PixelARGB getPixelARGB() const noexcept;
/** Returns a 32-bit integer that represents this colour.
The format of this number is:
((alpha << 24) | (red << 16) | (green << 16) | blue).
*/
uint32 getARGB() const noexcept;
//==============================================================================
/** Returns the colour's alpha (opacity).
Alpha of 0x00 is completely transparent, 0xff is completely opaque.
*/
uint8 getAlpha() const noexcept { return argb.getAlpha(); }
/** Returns the colour's alpha (opacity) as a floating point value.
Alpha of 0.0 is completely transparent, 1.0 is completely opaque.
*/
float getFloatAlpha() const noexcept;
/** Returns true if this colour is completely opaque.
Equivalent to (getAlpha() == 0xff).
*/
bool isOpaque() const noexcept;
/** Returns true if this colour is completely transparent.
Equivalent to (getAlpha() == 0x00).
*/
bool isTransparent() const noexcept;
/** Returns a colour that's the same colour as this one, but with a new alpha value. */
Colour withAlpha (uint8 newAlpha) const noexcept;
/** Returns a colour that's the same colour as this one, but with a new alpha value. */
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.
*/
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 (Colour other, float proportionOfOther) const noexcept;
//==============================================================================
/** Returns the colour's hue component.
The value returned is in the range 0.0 to 1.0
*/
float getHue() const noexcept;
/** Returns the colour's saturation component.
The value returned is in the range 0.0 to 1.0
*/
float getSaturation() const noexcept;
/** Returns the colour's brightness component.
The value returned is in the range 0.0 to 1.0
*/
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
*/
void getHSB (float& hue,
float& saturation,
float& brightness) const noexcept;
//==============================================================================
/** Returns a copy of this colour with a different hue. */
Colour withHue (float newHue) const noexcept;
/** Returns a copy of this colour with a different saturation. */
Colour withSaturation (float newSaturation) const noexcept;
/** Returns a copy of this colour with a different brightness.
@see brighter, darker, withMultipliedBrightness
*/
Colour withBrightness (float newBrightness) const noexcept;
/** Returns a copy of this colour with it hue rotated.
The new colour's hue is ((this->getHue() + amountToRotate) % 1.0)
@see brighter, darker, withMultipliedBrightness
*/
Colour withRotatedHue (float amountToRotate) const noexcept;
/** Returns a copy of this colour with its saturation multiplied by the given value.
The new colour's saturation is (this->getSaturation() * multiplier)
(the result is clipped to legal limits).
*/
Colour withMultipliedSaturation (float multiplier) const noexcept;
/** Returns a copy of this colour with its brightness multiplied by the given value.
The new colour's brightness is (this->getBrightness() * multiplier)
(the result is clipped to legal limits).
*/
Colour withMultipliedBrightness (float amount) const noexcept;
//==============================================================================
/** Returns a brighter version of this colour.
@param amountBrighter how much brighter to make it - a value from 0 to 1.0 where 0 is
unchanged, and higher values make it brighter
@see withMultipliedBrightness
*/
Colour brighter (float amountBrighter = 0.4f) const noexcept;
/** Returns a darker version of this colour.
@param amountDarker how much darker to make it - a value from 0 to 1.0 where 0 is
unchanged, and higher values make it darker
@see withMultipliedBrightness
*/
Colour darker (float amountDarker = 0.4f) const noexcept;
//==============================================================================
/** Returns a colour that will be clearly visible against this colour.
The amount parameter indicates how contrasting the new colour should
be, so e.g. Colours::black.contrasting (0.1f) will return a colour
that's just a little bit lighter; Colours::black.contrasting (1.0f) will
return white; Colours::white.contrasting (1.0f) will return black, etc.
*/
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 (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 (StringRef encodedColourString);
/** Returns the colour as a hex string in the form RRGGBB or AARRGGBB. */
String toDisplayString (bool includeAlphaValue) const;
private:
//==============================================================================
PixelARGB argb;
};
} // namespace juce

+ 0
- 244
source/modules/juce_graphics/colour/juce_ColourGradient.cpp View File

@@ -1,244 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
ColourGradient::ColourGradient() noexcept
{
#if JUCE_DEBUG
point1.setX (987654.0f);
#define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED jassert (point1.x != 987654.0f);
#else
#define JUCE_COLOURGRADIENT_CHECK_COORDS_INITIALISED
#endif
}
ColourGradient::ColourGradient (Colour colour1, const float x1, const float y1,
Colour colour2, const float x2, const float y2,
const bool radial)
: point1 (x1, y1),
point2 (x2, y2),
isRadial (radial)
{
colours.add (ColourPoint (0.0, colour1));
colours.add (ColourPoint (1.0, colour2));
}
ColourGradient::ColourGradient (Colour colour1, Point<float> p1,
Colour colour2, Point<float> p2,
const bool radial)
: point1 (p1),
point2 (p2),
isRadial (radial)
{
colours.add (ColourPoint (0.0, colour1));
colours.add (ColourPoint (1.0, colour2));
}
ColourGradient::~ColourGradient()
{
}
bool ColourGradient::operator== (const ColourGradient& other) const noexcept
{
return point1 == other.point1 && point2 == other.point2
&& isRadial == other.isRadial
&& colours == other.colours;
}
bool ColourGradient::operator!= (const ColourGradient& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
void ColourGradient::clearColours()
{
colours.clear();
}
int ColourGradient::addColour (const double proportionAlongGradient, Colour colour)
{
// must be within the two end-points
jassert (proportionAlongGradient >= 0 && proportionAlongGradient <= 1.0);
if (proportionAlongGradient <= 0)
{
colours.set (0, ColourPoint (0.0, colour));
return 0;
}
const double pos = jmin (1.0, proportionAlongGradient);
int i;
for (i = 0; i < colours.size(); ++i)
if (colours.getReference(i).position > pos)
break;
colours.insert (i, ColourPoint (pos, colour));
return i;
}
void ColourGradient::removeColour (int index)
{
jassert (index > 0 && index < colours.size() - 1);
colours.remove (index);
}
void ColourGradient::multiplyOpacity (const float multiplier) noexcept
{
for (int i = 0; i < colours.size(); ++i)
{
Colour& c = colours.getReference(i).colour;
c = c.withMultipliedAlpha (multiplier);
}
}
//==============================================================================
int ColourGradient::getNumColours() const noexcept
{
return colours.size();
}
double ColourGradient::getColourPosition (const int index) const noexcept
{
if (isPositiveAndBelow (index, colours.size()))
return colours.getReference (index).position;
return 0;
}
Colour ColourGradient::getColour (const int index) const noexcept
{
if (isPositiveAndBelow (index, colours.size()))
return colours.getReference (index).colour;
return Colour();
}
void ColourGradient::setColour (int index, Colour newColour) noexcept
{
if (isPositiveAndBelow (index, colours.size()))
colours.getReference (index).colour = newColour;
}
Colour ColourGradient::getColourAtPosition (const double position) const noexcept
{
jassert (colours.getReference(0).position == 0.0); // the first colour specified has to go at position 0
if (position <= 0 || colours.size() <= 1)
return colours.getReference(0).colour;
int i = colours.size() - 1;
while (position < colours.getReference(i).position)
--i;
auto& p1 = colours.getReference (i);
if (i >= colours.size() - 1)
return p1.colour;
auto& p2 = colours.getReference (i + 1);
return p1.colour.interpolatedWith (p2.colour, (float) ((position - p1.position) / (p2.position - p1.position)));
}
//==============================================================================
void ColourGradient::createLookupTable (PixelARGB* const lookupTable, const int numEntries) const noexcept
{
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.0); // The first colour specified has to go at position 0
PixelARGB pix1 (colours.getReference (0).colour.getPixelARGB());
int index = 0;
for (int j = 1; j < colours.size(); ++j)
{
const ColourPoint& p = colours.getReference (j);
const int numToDo = roundToInt (p.position * (numEntries - 1)) - index;
const PixelARGB pix2 (p.colour.getPixelARGB());
for (int i = 0; i < numToDo; ++i)
{
jassert (index >= 0 && index < numEntries);
lookupTable[index] = pix1;
lookupTable[index].tween (pix2, (uint32) ((i << 8) / numToDo));
++index;
}
pix1 = pix2;
}
while (index < numEntries)
lookupTable [index++] = pix1;
}
int ColourGradient::createLookupTable (const AffineTransform& transform, HeapBlock<PixelARGB>& lookupTable) const
{
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),
3 * (int) point1.transformedBy (transform)
.getDistanceFrom (point2.transformedBy (transform)));
lookupTable.malloc ((size_t) numEntries);
createLookupTable (lookupTable, numEntries);
return numEntries;
}
bool ColourGradient::isOpaque() const noexcept
{
for (int i = 0; i < colours.size(); ++i)
if (! colours.getReference(i).colour.isOpaque())
return false;
return true;
}
bool ColourGradient::isInvisible() const noexcept
{
for (int i = 0; i < colours.size(); ++i)
if (! colours.getReference(i).colour.isTransparent())
return false;
return true;
}
bool ColourGradient::ColourPoint::operator== (const ColourPoint& other) const noexcept
{
return position == other.position && colour == other.colour;
}
bool ColourGradient::ColourPoint::operator!= (const ColourPoint& other) const noexcept
{
return position != other.position || colour != other.colour;
}
} // namespace juce

+ 0
- 202
source/modules/juce_graphics/colour/juce_ColourGradient.h View File

@@ -1,202 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Describes the layout and colours that should be used to paint a colour gradient.
@see Graphics::setGradientFill
*/
class JUCE_API ColourGradient
{
public:
//==============================================================================
/** Creates a gradient object.
(x1, y1) is the location to draw with colour1. Likewise (x2, y2) is where
colour2 should be. In between them there's a gradient.
If isRadial is true, the colours form a circular gradient with (x1, y1) at
its centre.
The alpha transparencies of the colours are used, so note that
if you blend from transparent to a solid colour, the RGB of the transparent
colour will become visible in parts of the gradient. e.g. blending
from Colour::transparentBlack to Colours::white will produce a
muddy grey colour midway, but Colour::transparentWhite to Colours::white
will be white all the way across.
@see ColourGradient
*/
ColourGradient (Colour colour1, float x1, float y1,
Colour colour2, float x2, float y2,
bool isRadial);
/** Creates a gradient object.
point1 is the location to draw with colour1. Likewise point2 is where
colour2 should be. In between them there's a gradient.
If isRadial is true, the colours form a circular gradient with point1 at
its centre.
The alpha transparencies of the colours are used, so note that
if you blend from transparent to a solid colour, the RGB of the transparent
colour will become visible in parts of the gradient. e.g. blending
from Colour::transparentBlack to Colours::white will produce a
muddy grey colour midway, but Colour::transparentWhite to Colours::white
will be white all the way across.
@see ColourGradient
*/
ColourGradient (Colour colour1, Point<float> point1,
Colour colour2, Point<float> point2,
bool isRadial);
/** Creates an uninitialised gradient.
If you use this constructor instead of the other one, be sure to set all the
object's public member variables before using it!
*/
ColourGradient() noexcept;
/** Destructor */
~ColourGradient();
//==============================================================================
/** Removes any colours that have been added.
This will also remove any start and end colours, so the gradient won't work. You'll
need to add more colours with addColour().
*/
void clearColours();
/** Adds a colour at a point along the length of the gradient.
This allows the gradient to go through a spectrum of colours, instead of just a
start and end colour.
@param proportionAlongGradient a value between 0 and 1.0, which is the proportion
of the distance along the line between the two points
at which the colour should occur.
@param colour the colour that should be used at this point
@returns the index at which the new point was added
*/
int addColour (double proportionAlongGradient,
Colour colour);
/** Removes one of the colours from the gradient. */
void removeColour (int index);
/** Multiplies the alpha value of all the colours by the given scale factor */
void multiplyOpacity (float multiplier) noexcept;
//==============================================================================
/** Returns the number of colour-stops that have been added. */
int getNumColours() const noexcept;
/** Returns the position along the length of the gradient of the colour with this index.
The index is from 0 to getNumColours() - 1. The return value will be between 0.0 and 1.0
*/
double getColourPosition (int index) const noexcept;
/** Returns the colour that was added with a given index.
The index is from 0 to getNumColours() - 1.
*/
Colour getColour (int index) const noexcept;
/** Changes the colour at a given index.
The index is from 0 to getNumColours() - 1.
*/
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
*/
Colour getColourAtPosition (double position) const noexcept;
//==============================================================================
/** Creates a set of interpolated premultiplied ARGB values.
This will resize the HeapBlock, fill it with the colours, and will return the number of
colours that it added.
When calling this, the ColourGradient must have at least 2 colour stops specified.
*/
int createLookupTable (const AffineTransform& transform, HeapBlock<PixelARGB>& resultLookupTable) const;
/** Creates a set of interpolated premultiplied ARGB values.
This will fill an array of a user-specified size with the gradient, interpolating to fit.
The numEntries argument specifies the size of the array, and this size must be greater than zero.
When calling this, the ColourGradient must have at least 2 colour stops specified.
*/
void createLookupTable (PixelARGB* resultLookupTable, int numEntries) const noexcept;
/** Returns true if all colours are opaque. */
bool isOpaque() const noexcept;
/** Returns true if all colours are completely transparent. */
bool isInvisible() const noexcept;
//==============================================================================
Point<float> point1, point2;
/** If true, the gradient should be filled circularly, centred around
point1, with point2 defining a point on the circumference.
If false, the gradient is linear between the two points.
*/
bool isRadial;
bool operator== (const ColourGradient&) const noexcept;
bool operator!= (const ColourGradient&) const noexcept;
private:
//==============================================================================
struct ColourPoint
{
ColourPoint() noexcept {}
ColourPoint (const double pos, Colour col) noexcept
: position (pos), colour (col)
{}
bool operator== (const ColourPoint&) const noexcept;
bool operator!= (const ColourPoint&) const noexcept;
double position;
Colour colour;
};
Array<ColourPoint> colours;
JUCE_LEAK_DETECTOR (ColourGradient)
};
} // namespace juce

+ 0
- 335
source/modules/juce_graphics/colour/juce_Colours.cpp View File

@@ -1,335 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
const Colour Colours::transparentBlack (0);
const Colour Colours::transparentWhite (0x00ffffff);
const Colour Colours::aliceblue (0xfff0f8ff);
const Colour Colours::antiquewhite (0xfffaebd7);
const Colour Colours::aqua (0xff00ffff);
const Colour Colours::aquamarine (0xff7fffd4);
const Colour Colours::azure (0xfff0ffff);
const Colour Colours::beige (0xfff5f5dc);
const Colour Colours::bisque (0xffffe4c4);
const Colour Colours::black (0xff000000);
const Colour Colours::blanchedalmond (0xffffebcd);
const Colour Colours::blue (0xff0000ff);
const Colour Colours::blueviolet (0xff8a2be2);
const Colour Colours::brown (0xffa52a2a);
const Colour Colours::burlywood (0xffdeb887);
const Colour Colours::cadetblue (0xff5f9ea0);
const Colour Colours::chartreuse (0xff7fff00);
const Colour Colours::chocolate (0xffd2691e);
const Colour Colours::coral (0xffff7f50);
const Colour Colours::cornflowerblue (0xff6495ed);
const Colour Colours::cornsilk (0xfffff8dc);
const Colour Colours::crimson (0xffdc143c);
const Colour Colours::cyan (0xff00ffff);
const Colour Colours::darkblue (0xff00008b);
const Colour Colours::darkcyan (0xff008b8b);
const Colour Colours::darkgoldenrod (0xffb8860b);
const Colour Colours::darkgrey (0xff555555);
const Colour Colours::darkgreen (0xff006400);
const Colour Colours::darkkhaki (0xffbdb76b);
const Colour Colours::darkmagenta (0xff8b008b);
const Colour Colours::darkolivegreen (0xff556b2f);
const Colour Colours::darkorange (0xffff8c00);
const Colour Colours::darkorchid (0xff9932cc);
const Colour Colours::darkred (0xff8b0000);
const Colour Colours::darksalmon (0xffe9967a);
const Colour Colours::darkseagreen (0xff8fbc8f);
const Colour Colours::darkslateblue (0xff483d8b);
const Colour Colours::darkslategrey (0xff2f4f4f);
const Colour Colours::darkturquoise (0xff00ced1);
const Colour Colours::darkviolet (0xff9400d3);
const Colour Colours::deeppink (0xffff1493);
const Colour Colours::deepskyblue (0xff00bfff);
const Colour Colours::dimgrey (0xff696969);
const Colour Colours::dodgerblue (0xff1e90ff);
const Colour Colours::firebrick (0xffb22222);
const Colour Colours::floralwhite (0xfffffaf0);
const Colour Colours::forestgreen (0xff228b22);
const Colour Colours::fuchsia (0xffff00ff);
const Colour Colours::gainsboro (0xffdcdcdc);
const Colour Colours::ghostwhite (0xfff8f8ff);
const Colour Colours::gold (0xffffd700);
const Colour Colours::goldenrod (0xffdaa520);
const Colour Colours::grey (0xff808080);
const Colour Colours::green (0xff008000);
const Colour Colours::greenyellow (0xffadff2f);
const Colour Colours::honeydew (0xfff0fff0);
const Colour Colours::hotpink (0xffff69b4);
const Colour Colours::indianred (0xffcd5c5c);
const Colour Colours::indigo (0xff4b0082);
const Colour Colours::ivory (0xfffffff0);
const Colour Colours::khaki (0xfff0e68c);
const Colour Colours::lavender (0xffe6e6fa);
const Colour Colours::lavenderblush (0xfffff0f5);
const Colour Colours::lawngreen (0xff7cfc00);
const Colour Colours::lemonchiffon (0xfffffacd);
const Colour Colours::lightblue (0xffadd8e6);
const Colour Colours::lightcoral (0xfff08080);
const Colour Colours::lightcyan (0xffe0ffff);
const Colour Colours::lightgoldenrodyellow (0xfffafad2);
const Colour Colours::lightgreen (0xff90ee90);
const Colour Colours::lightgrey (0xffd3d3d3);
const Colour Colours::lightpink (0xffffb6c1);
const Colour Colours::lightsalmon (0xffffa07a);
const Colour Colours::lightseagreen (0xff20b2aa);
const Colour Colours::lightskyblue (0xff87cefa);
const Colour Colours::lightslategrey (0xff778899);
const Colour Colours::lightsteelblue (0xffb0c4de);
const Colour Colours::lightyellow (0xffffffe0);
const Colour Colours::lime (0xff00ff00);
const Colour Colours::limegreen (0xff32cd32);
const Colour Colours::linen (0xfffaf0e6);
const Colour Colours::magenta (0xffff00ff);
const Colour Colours::maroon (0xff800000);
const Colour Colours::mediumaquamarine (0xff66cdaa);
const Colour Colours::mediumblue (0xff0000cd);
const Colour Colours::mediumorchid (0xffba55d3);
const Colour Colours::mediumpurple (0xff9370db);
const Colour Colours::mediumseagreen (0xff3cb371);
const Colour Colours::mediumslateblue (0xff7b68ee);
const Colour Colours::mediumspringgreen (0xff00fa9a);
const Colour Colours::mediumturquoise (0xff48d1cc);
const Colour Colours::mediumvioletred (0xffc71585);
const Colour Colours::midnightblue (0xff191970);
const Colour Colours::mintcream (0xfff5fffa);
const Colour Colours::mistyrose (0xffffe4e1);
const Colour Colours::moccasin (0xffffe4b5);
const Colour Colours::navajowhite (0xffffdead);
const Colour Colours::navy (0xff000080);
const Colour Colours::oldlace (0xfffdf5e6);
const Colour Colours::olive (0xff808000);
const Colour Colours::olivedrab (0xff6b8e23);
const Colour Colours::orange (0xffffa500);
const Colour Colours::orangered (0xffff4500);
const Colour Colours::orchid (0xffda70d6);
const Colour Colours::palegoldenrod (0xffeee8aa);
const Colour Colours::palegreen (0xff98fb98);
const Colour Colours::paleturquoise (0xffafeeee);
const Colour Colours::palevioletred (0xffdb7093);
const Colour Colours::papayawhip (0xffffefd5);
const Colour Colours::peachpuff (0xffffdab9);
const Colour Colours::peru (0xffcd853f);
const Colour Colours::pink (0xffffc0cb);
const Colour Colours::plum (0xffdda0dd);
const Colour Colours::powderblue (0xffb0e0e6);
const Colour Colours::purple (0xff800080);
const Colour Colours::rebeccapurple (0xff663399);
const Colour Colours::red (0xffff0000);
const Colour Colours::rosybrown (0xffbc8f8f);
const Colour Colours::royalblue (0xff4169e1);
const Colour Colours::saddlebrown (0xff8b4513);
const Colour Colours::salmon (0xfffa8072);
const Colour Colours::sandybrown (0xfff4a460);
const Colour Colours::seagreen (0xff2e8b57);
const Colour Colours::seashell (0xfffff5ee);
const Colour Colours::sienna (0xffa0522d);
const Colour Colours::silver (0xffc0c0c0);
const Colour Colours::skyblue (0xff87ceeb);
const Colour Colours::slateblue (0xff6a5acd);
const Colour Colours::slategrey (0xff708090);
const Colour Colours::snow (0xfffffafa);
const Colour Colours::springgreen (0xff00ff7f);
const Colour Colours::steelblue (0xff4682b4);
const Colour Colours::tan (0xffd2b48c);
const Colour Colours::teal (0xff008080);
const Colour Colours::thistle (0xffd8bfd8);
const Colour Colours::tomato (0xffff6347);
const Colour Colours::turquoise (0xff40e0d0);
const Colour Colours::violet (0xffee82ee);
const Colour Colours::wheat (0xfff5deb3);
const Colour Colours::white (0xffffffff);
const Colour Colours::whitesmoke (0xfff5f5f5);
const Colour Colours::yellow (0xffffff00);
const Colour Colours::yellowgreen (0xff9acd32);
//==============================================================================
Colour Colours::findColourForName (const String& colourName,
Colour defaultColour)
{
static const uint32 presets[] =
{
// (first value is the string's hashcode, second is ARGB)
0x05978fff, 0xff000000, /* black */
0x06bdcc29, 0xffffffff, /* white */
0x002e305a, 0xff0000ff, /* blue */
0x00308adf, 0xff808080, /* grey */
0x05e0cf03, 0xff008000, /* green */
0x0001b891, 0xffff0000, /* red */
0xd43c6474, 0xffffff00, /* yellow */
0x620886da, 0xfff0f8ff, /* aliceblue */
0x20a2676a, 0xfffaebd7, /* antiquewhite */
0x002dcebc, 0xff00ffff, /* aqua */
0x46bb5f7e, 0xff7fffd4, /* aquamarine */
0x0590228f, 0xfff0ffff, /* azure */
0x05947fe4, 0xfff5f5dc, /* beige */
0xad388e35, 0xffffe4c4, /* bisque */
0x00674f7e, 0xffffebcd, /* blanchedalmond */
0x39129959, 0xff8a2be2, /* blueviolet */
0x059a8136, 0xffa52a2a, /* brown */
0x89cea8f9, 0xffdeb887, /* burlywood */
0x0fa260cf, 0xff5f9ea0, /* cadetblue */
0x6b748956, 0xff7fff00, /* chartreuse */
0x2903623c, 0xffd2691e, /* chocolate */
0x05a74431, 0xffff7f50, /* coral */
0x618d42dd, 0xff6495ed, /* cornflowerblue */
0xe4b479fd, 0xfffff8dc, /* cornsilk */
0x3d8c4edf, 0xffdc143c, /* crimson */
0x002ed323, 0xff00ffff, /* cyan */
0x67cc74d0, 0xff00008b, /* darkblue */
0x67cd1799, 0xff008b8b, /* darkcyan */
0x31bbd168, 0xffb8860b, /* darkgoldenrod */
0x67cecf55, 0xff555555, /* darkgrey */
0x920b194d, 0xff006400, /* darkgreen */
0x923edd4c, 0xffbdb76b, /* darkkhaki */
0x5c293873, 0xff8b008b, /* darkmagenta */
0x6b6671fe, 0xff556b2f, /* darkolivegreen */
0xbcfd2524, 0xffff8c00, /* darkorange */
0xbcfdf799, 0xff9932cc, /* darkorchid */
0x55ee0d5b, 0xff8b0000, /* darkred */
0xc2e5f564, 0xffe9967a, /* darksalmon */
0x61be858a, 0xff8fbc8f, /* darkseagreen */
0xc2b0f2bd, 0xff483d8b, /* darkslateblue */
0xc2b34d42, 0xff2f4f4f, /* darkslategrey */
0x7cf2b06b, 0xff00ced1, /* darkturquoise */
0xc8769375, 0xff9400d3, /* darkviolet */
0x25832862, 0xffff1493, /* deeppink */
0xfcad568f, 0xff00bfff, /* deepskyblue */
0x634c8b67, 0xff696969, /* dimgrey */
0x45c1ce55, 0xff1e90ff, /* dodgerblue */
0xef19e3cb, 0xffb22222, /* firebrick */
0xb852b195, 0xfffffaf0, /* floralwhite */
0xd086fd06, 0xff228b22, /* forestgreen */
0xe106b6d7, 0xffff00ff, /* fuchsia */
0x7880d61e, 0xffdcdcdc, /* gainsboro */
0x2018a2fa, 0xfff8f8ff, /* ghostwhite */
0x00308060, 0xffffd700, /* gold */
0xb3b3bc1e, 0xffdaa520, /* goldenrod */
0xbab8a537, 0xffadff2f, /* greenyellow */
0xe4cacafb, 0xfff0fff0, /* honeydew */
0x41892743, 0xffff69b4, /* hotpink */
0xd5796f1a, 0xffcd5c5c, /* indianred */
0xb969fed2, 0xff4b0082, /* indigo */
0x05fef6a9, 0xfffffff0, /* ivory */
0x06149302, 0xfff0e68c, /* khaki */
0xad5a05c7, 0xffe6e6fa, /* lavender */
0x7c4d5b99, 0xfffff0f5, /* lavenderblush */
0x41cc4377, 0xff7cfc00, /* lawngreen */
0x195756f0, 0xfffffacd, /* lemonchiffon */
0x28e4ea70, 0xffadd8e6, /* lightblue */
0xf3c7ccdb, 0xfff08080, /* lightcoral */
0x28e58d39, 0xffe0ffff, /* lightcyan */
0x21234e3c, 0xfffafad2, /* lightgoldenrodyellow */
0xf40157ad, 0xff90ee90, /* lightgreen */
0x28e744f5, 0xffd3d3d3, /* lightgrey */
0x28eb3b8c, 0xffffb6c1, /* lightpink */
0x9fb78304, 0xffffa07a, /* lightsalmon */
0x50632b2a, 0xff20b2aa, /* lightseagreen */
0x68fb7b25, 0xff87cefa, /* lightskyblue */
0xa8a35ba2, 0xff778899, /* lightslategrey */
0xa20d484f, 0xffb0c4de, /* lightsteelblue */
0xaa2cf10a, 0xffffffe0, /* lightyellow */
0x0032afd5, 0xff00ff00, /* lime */
0x607bbc4e, 0xff32cd32, /* limegreen */
0x06234efa, 0xfffaf0e6, /* linen */
0x316858a9, 0xffff00ff, /* magenta */
0xbf8ca470, 0xff800000, /* maroon */
0xbd58e0b3, 0xff66cdaa, /* mediumaquamarine */
0x967dfd4f, 0xff0000cd, /* mediumblue */
0x056f5c58, 0xffba55d3, /* mediumorchid */
0x07556b71, 0xff9370db, /* mediumpurple */
0x5369b689, 0xff3cb371, /* mediumseagreen */
0x066be19e, 0xff7b68ee, /* mediumslateblue */
0x3256b281, 0xff00fa9a, /* mediumspringgreen */
0xc0ad9f4c, 0xff48d1cc, /* mediumturquoise */
0x628e63dd, 0xffc71585, /* mediumvioletred */
0x168eb32a, 0xff191970, /* midnightblue */
0x4306b960, 0xfff5fffa, /* mintcream */
0x4cbc0e6b, 0xffffe4e1, /* mistyrose */
0xd9447d59, 0xffffe4b5, /* moccasin */
0xe97218a6, 0xffffdead, /* navajowhite */
0x00337bb6, 0xff000080, /* navy */
0xadd2d33e, 0xfffdf5e6, /* oldlace */
0x064ee1db, 0xff808000, /* olive */
0x9e33a98a, 0xff6b8e23, /* olivedrab */
0xc3de262e, 0xffffa500, /* orange */
0x58bebba3, 0xffff4500, /* orangered */
0xc3def8a3, 0xffda70d6, /* orchid */
0x28cb4834, 0xffeee8aa, /* palegoldenrod */
0x3d9dd619, 0xff98fb98, /* palegreen */
0x74022737, 0xffafeeee, /* paleturquoise */
0x15e2ebc8, 0xffdb7093, /* palevioletred */
0x5fd898e2, 0xffffefd5, /* papayawhip */
0x93e1b776, 0xffffdab9, /* peachpuff */
0x003472f8, 0xffcd853f, /* peru */
0x00348176, 0xffffc0cb, /* pink */
0x00348d94, 0xffdda0dd, /* plum */
0xd036be93, 0xffb0e0e6, /* powderblue */
0xc5c507bc, 0xff800080, /* purple */
0xf381f607, 0xff663399, /* rebeccapurple */
0xa89d65b3, 0xffbc8f8f, /* rosybrown */
0xbd9413e1, 0xff4169e1, /* royalblue */
0xf456044f, 0xff8b4513, /* saddlebrown */
0xc9c6f66e, 0xfffa8072, /* salmon */
0x0bb131e1, 0xfff4a460, /* sandybrown */
0x34636c14, 0xff2e8b57, /* seagreen */
0x3507fb41, 0xfffff5ee, /* seashell */
0xca348772, 0xffa0522d, /* sienna */
0xca37d30d, 0xffc0c0c0, /* silver */
0x80da74fb, 0xff87ceeb, /* skyblue */
0x44a8dd73, 0xff6a5acd, /* slateblue */
0x44ab37f8, 0xff708090, /* slategrey */
0x0035f183, 0xfffffafa, /* snow */
0xd5440d16, 0xff00ff7f, /* springgreen */
0x3e1524a5, 0xff4682b4, /* steelblue */
0x0001bfa1, 0xffd2b48c, /* tan */
0x0036425c, 0xff008080, /* teal */
0xafc8858f, 0xffd8bfd8, /* thistle */
0xcc41600a, 0xffff6347, /* tomato */
0xfeea9b21, 0xff40e0d0, /* turquoise */
0xcf57947f, 0xffee82ee, /* violet */
0x06bdbae7, 0xfff5deb3, /* wheat */
0x10802ee6, 0xfff5f5f5, /* whitesmoke */
0xe1b5130f, 0xff9acd32 /* yellowgreen */
};
const uint32 hash = (uint32) colourName.trim().toLowerCase().hashCode();
for (int i = 0; i < numElementsInArray (presets); i += 2)
if (presets [i] == hash)
return Colour (presets [i + 1]);
return defaultColour;
}
} // namespace juce

+ 0
- 109
source/modules/juce_graphics/colour/juce_Colours.h View File

@@ -1,109 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Contains a set of predefined named colours (mostly standard HTML colours)
@see Colour, Colours::greyLevel
*/
class Colours
{
public:
static JUCE_API const Colour
//==============================================================================
transparentBlack, /**< ARGB = 0x00000000 */
transparentWhite, /**< ARGB = 0x00ffffff */
//==============================================================================
black, /**< ARGB = 0xff000000 */
white, /**< ARGB = 0xffffffff */
blue, /**< ARGB = 0xff0000ff */
grey, /**< ARGB = 0xff808080 */
green, /**< ARGB = 0xff008000 */
red, /**< ARGB = 0xffff0000 */
yellow, /**< ARGB = 0xffffff00 */
//==============================================================================
aliceblue, antiquewhite, aqua, aquamarine,
azure, beige, bisque, blanchedalmond,
blueviolet, brown, burlywood, cadetblue,
chartreuse, chocolate, coral, cornflowerblue,
cornsilk, crimson, cyan, darkblue,
darkcyan, darkgoldenrod, darkgrey, darkgreen,
darkkhaki, darkmagenta, darkolivegreen, darkorange,
darkorchid, darkred, darksalmon, darkseagreen,
darkslateblue, darkslategrey, darkturquoise, darkviolet,
deeppink, deepskyblue, dimgrey, dodgerblue,
firebrick, floralwhite, forestgreen, fuchsia,
gainsboro, ghostwhite, gold, goldenrod,
greenyellow, honeydew, hotpink, indianred,
indigo, ivory, khaki, lavender,
lavenderblush, lawngreen, lemonchiffon, lightblue,
lightcoral, lightcyan, lightgoldenrodyellow, lightgreen,
lightgrey, lightpink, lightsalmon, lightseagreen,
lightskyblue, lightslategrey, lightsteelblue, lightyellow,
lime, limegreen, linen, magenta,
maroon, mediumaquamarine, mediumblue, mediumorchid,
mediumpurple, mediumseagreen, mediumslateblue, mediumspringgreen,
mediumturquoise, mediumvioletred, midnightblue, mintcream,
mistyrose, moccasin, navajowhite, navy,
oldlace, olive, olivedrab, orange,
orangered, orchid, palegoldenrod, palegreen,
paleturquoise, palevioletred, papayawhip, peachpuff,
peru, pink, plum, powderblue,
purple, rebeccapurple, rosybrown, royalblue,
saddlebrown, salmon, sandybrown, seagreen,
seashell, sienna, silver, skyblue,
slateblue, slategrey, snow, springgreen,
steelblue, tan, teal, thistle,
tomato, turquoise, violet, wheat,
whitesmoke, yellowgreen;
/** Attempts to look up a string in the list of known colour names, and return
the appropriate colour.
A non-case-sensitive search is made of the list of predefined colours, and
if a match is found, that colour is returned. If no match is found, the
colour passed in as the defaultColour parameter is returned.
*/
static JUCE_API Colour findColourForName (const String& colourName,
Colour defaultColour);
private:
//==============================================================================
// this isn't a class you should ever instantiate - it's just here for the
// static values in it.
Colours();
JUCE_DECLARE_NON_COPYABLE (Colours)
};
} // namespace juce

+ 0
- 153
source/modules/juce_graphics/colour/juce_FillType.cpp View File

@@ -1,153 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
FillType::FillType() noexcept
: colour (0xff000000)
{
}
FillType::FillType (Colour c) noexcept
: colour (c)
{
}
FillType::FillType (const ColourGradient& gradient_)
: colour (0xff000000), gradient (new ColourGradient (gradient_))
{
}
FillType::FillType (const Image& image_, const AffineTransform& transform_) noexcept
: colour (0xff000000), image (image_), transform (transform_)
{
}
FillType::FillType (const FillType& other)
: colour (other.colour),
gradient (other.gradient.createCopy()),
image (other.image),
transform (other.transform)
{
}
FillType& FillType::operator= (const FillType& other)
{
if (this != &other)
{
colour = other.colour;
gradient = other.gradient.createCopy();
image = other.image;
transform = other.transform;
}
return *this;
}
FillType::FillType (FillType&& other) noexcept
: colour (other.colour),
gradient (other.gradient.release()),
image (static_cast<Image&&> (other.image)),
transform (other.transform)
{
}
FillType& FillType::operator= (FillType&& other) noexcept
{
jassert (this != &other); // hopefully the compiler should make this situation impossible!
colour = other.colour;
gradient = other.gradient.release();
image = static_cast<Image&&> (other.image);
transform = other.transform;
return *this;
}
FillType::~FillType() noexcept
{
}
bool FillType::operator== (const FillType& other) const
{
return colour == other.colour && image == other.image
&& transform == other.transform
&& (gradient == other.gradient
|| (gradient != nullptr && other.gradient != nullptr && *gradient == *other.gradient));
}
bool FillType::operator!= (const FillType& other) const
{
return ! operator== (other);
}
void FillType::setColour (Colour newColour) noexcept
{
gradient = nullptr;
image = Image();
colour = newColour;
}
void FillType::setGradient (const ColourGradient& newGradient)
{
if (gradient != nullptr)
{
*gradient = newGradient;
}
else
{
image = Image();
gradient = new ColourGradient (newGradient);
colour = Colours::black;
}
}
void FillType::setTiledImage (const Image& image_, const AffineTransform& transform_) noexcept
{
gradient = nullptr;
image = image_;
transform = transform_;
colour = Colours::black;
}
void FillType::setOpacity (const float newOpacity) noexcept
{
colour = colour.withAlpha (newOpacity);
}
bool FillType::isInvisible() const noexcept
{
return colour.isTransparent() || (gradient != nullptr && gradient->isInvisible());
}
FillType FillType::transformed (const AffineTransform& t) const
{
FillType f (*this);
f.transform = f.transform.followedBy (t);
return f;
}
} // namespace juce

+ 0
- 150
source/modules/juce_graphics/colour/juce_FillType.h View File

@@ -1,150 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a colour or fill pattern to use for rendering paths.
This is used by the Graphics and DrawablePath classes as a way to encapsulate
a brush type. It can either be a solid colour, a gradient, or a tiled image.
@see Graphics::setFillType, DrawablePath::setFill
*/
class JUCE_API FillType
{
public:
//==============================================================================
/** Creates a default fill type, of solid black. */
FillType() noexcept;
/** Creates a fill type of a solid colour.
@see setColour
*/
FillType (Colour colour) noexcept;
/** Creates a gradient fill type.
@see setGradient
*/
FillType (const ColourGradient& gradient);
/** Creates a tiled image fill type. The transform allows you to set the scaling, offset
and rotation of the pattern.
@see setTiledImage
*/
FillType (const Image& image, const AffineTransform& transform) noexcept;
/** Creates a copy of another FillType. */
FillType (const FillType&);
/** Makes a copy of another FillType. */
FillType& operator= (const FillType&);
/** Move constructor */
FillType (FillType&&) noexcept;
/** Move assignment operator */
FillType& operator= (FillType&&) noexcept;
/** Destructor. */
~FillType() noexcept;
//==============================================================================
/** Returns true if this is a solid colour fill, and not a gradient or image. */
bool isColour() const noexcept { return gradient == nullptr && image.isNull(); }
/** Returns true if this is a gradient fill. */
bool isGradient() const noexcept { return gradient != nullptr; }
/** Returns true if this is a tiled image pattern fill. */
bool isTiledImage() const noexcept { return image.isValid(); }
/** 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 (Colour newColour) noexcept;
/** Turns this object into a gradient fill. */
void setGradient (const ColourGradient& newGradient);
/** Turns this object into a tiled image fill type. The transform allows you to set
the scaling, offset and rotation of the pattern.
*/
void setTiledImage (const Image& image, const AffineTransform& transform) noexcept;
/** Changes the opacity that should be used.
If the fill is a solid colour, this just changes the opacity of that colour. For
gradients and image tiles, it changes the opacity that will be used for them.
*/
void setOpacity (float newOpacity) noexcept;
/** Returns the current opacity to be applied to the colour, gradient, or image.
@see setOpacity
*/
float getOpacity() const noexcept { return colour.getFloatAlpha(); }
/** Returns true if this fill type is completely transparent. */
bool isInvisible() const noexcept;
/** Returns a copy of this fill, adding the specified transform applied to the
existing transform.
*/
FillType transformed (const AffineTransform& transform) const;
//==============================================================================
/** The solid colour being used.
If the fill type is not a solid colour, the alpha channel of this colour indicates
the opacity that should be used for the fill, and the RGB channels are ignored.
*/
Colour colour;
/** Returns the gradient that should be used for filling.
This will be zero if the object is some other type of fill.
If a gradient is active, the overall opacity with which it should be applied
is indicated by the alpha channel of the colour variable.
*/
ScopedPointer<ColourGradient> gradient;
/** The image that should be used for tiling.
If an image fill is active, the overall opacity with which it should be applied
is indicated by the alpha channel of the colour variable.
*/
Image image;
/** The transform that should be applied to the image or gradient that's being drawn. */
AffineTransform transform;
//==============================================================================
bool operator== (const FillType&) const;
bool operator!= (const FillType&) const;
private:
JUCE_LEAK_DETECTOR (FillType)
};
} // namespace juce

+ 0
- 757
source/modules/juce_graphics/colour/juce_PixelFormats.h View File

@@ -1,757 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
#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 INTERNAL pixel with premultiplied alpha, and can perform compositing
operations with it.
This is used internally by the imaging classes.
@see PixelRGB
*/
class JUCE_API PixelARGB
{
public:
/** Creates a pixel without defining its colour. */
PixelARGB() noexcept {}
~PixelARGB() noexcept {}
PixelARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept
{
components.b = b;
components.g = g;
components.r = r;
components.a = a;
}
//==============================================================================
/** Returns a uint32 which represents the pixel in a platform dependent format. */
forcedinline uint32 getNativeARGB() const noexcept { return internal; }
/** Returns a uint32 which will be in argb order as if constructed with the following mask operation
((alpha << 24) | (red << 16) | (green << 8) | blue). */
forcedinline uint32 getInARGBMaskOrder() const noexcept
{
#if JUCE_ANDROID
return (uint32) ((components.a << 24) | (components.r << 16) | (components.g << 8) | (components.b << 0));
#else
return getNativeARGB();
#endif
}
/** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words,
if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/
inline uint32 getInARGBMemoryOrder() const noexcept
{
#if JUCE_BIG_ENDIAN
return getInARGBMaskOrder();
#else
return (uint32) ((components.b << 24) | (components.g << 16) | (components.r << 8) | components.a);
#endif
}
/** Return channels with an even index and insert zero bytes between them. This is useful for blending
operations. The exact channels which are returned is platform dependent. */
forcedinline uint32 getEvenBytes() const noexcept { return 0x00ff00ff & internal; }
/** Return channels with an odd index and insert zero bytes between them. This is useful for blending
operations. The exact channels which are returned is platform dependent. */
forcedinline uint32 getOddBytes() const noexcept { return 0x00ff00ff & (internal >> 8); }
//==============================================================================
forcedinline uint8 getAlpha() const noexcept { return components.a; }
forcedinline uint8 getRed() const noexcept { return components.r; }
forcedinline uint8 getGreen() const noexcept { return components.g; }
forcedinline uint8 getBlue() const noexcept { return components.b; }
#if JUCE_GCC
// 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
//==============================================================================
/** Copies another pixel colour over this one.
This doesn't blend it - this colour is simply replaced by the other one.
*/
template <class Pixel>
forcedinline void set (const Pixel& src) noexcept
{
internal = src.getNativeARGB();
}
//==============================================================================
/** Sets the pixel's colour from individual components. */
void setARGB (const uint8 a, const uint8 r, const uint8 g, const uint8 b) noexcept
{
components.b = b;
components.g = g;
components.r = r;
components.a = a;
}
//==============================================================================
/** Blends another pixel onto this one.
This takes into account the opacity of the pixel being overlaid, and blends
it accordingly.
*/
template <class Pixel>
forcedinline void blend (const Pixel& src) noexcept
{
uint32 rb = src.getEvenBytes();
uint32 ag = src.getOddBytes();
const uint32 alpha = 0x100 - (ag >> 16);
rb += maskPixelComponents (getEvenBytes() * alpha);
ag += maskPixelComponents (getOddBytes() * alpha);
internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8);
}
/** Blends another pixel onto this one.
This takes into account the opacity of the pixel being overlaid, and blends
it accordingly.
*/
forcedinline void blend (const PixelRGB src) noexcept;
/** Blends another pixel onto this one, applying an extra multiplier to its opacity.
The opacity of the pixel being overlaid is scaled by the extraAlpha factor before
being used, so this can blend semi-transparently from a PixelRGB argument.
*/
template <class Pixel>
forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept
{
uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes());
uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes());
const uint32 alpha = 0x100 - (ag >> 16);
rb += maskPixelComponents (getEvenBytes() * alpha);
ag += maskPixelComponents (getOddBytes() * alpha);
internal = clampPixelComponents (rb) | (clampPixelComponents (ag) << 8);
}
/** Blends another pixel with this one, creating a colour that is somewhere
between the two, as specified by the amount.
*/
template <class Pixel>
forcedinline void tween (const Pixel& src, const uint32 amount) noexcept
{
uint32 dEvenBytes = getEvenBytes();
dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8);
dEvenBytes &= 0x00ff00ff;
uint32 dOddBytes = getOddBytes();
dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8);
dOddBytes &= 0x00ff00ff;
dOddBytes <<= 8;
dOddBytes |= dEvenBytes;
internal = dOddBytes;
}
//==============================================================================
/** Replaces the colour's alpha value with another one. */
forcedinline void setAlpha (const uint8 newAlpha) noexcept
{
components.a = newAlpha;
}
/** Multiplies the colour's alpha value with another one. */
forcedinline void multiplyAlpha (int multiplier) noexcept
{
// increment alpha by 1, so that if multiplier == 255 (full alpha),
// this function will not change the values.
++multiplier;
internal = ((((uint32) multiplier) * getOddBytes()) & 0xff00ff00)
| (((((uint32) multiplier) * getEvenBytes()) >> 8) & 0x00ff00ff);
}
forcedinline void multiplyAlpha (const float multiplier) noexcept
{
multiplyAlpha ((int) (multiplier * 255.0f));
}
inline PixelARGB getUnpremultiplied() const noexcept { PixelARGB p (internal); p.unpremultiply(); return p; }
/** Premultiplies the pixel's RGB values by its alpha. */
forcedinline void premultiply() noexcept
{
const uint32 alpha = components.a;
if (alpha < 0xff)
{
if (alpha == 0)
{
components.b = 0;
components.g = 0;
components.r = 0;
}
else
{
components.b = (uint8) ((components.b * alpha + 0x7f) >> 8);
components.g = (uint8) ((components.g * alpha + 0x7f) >> 8);
components.r = (uint8) ((components.r * alpha + 0x7f) >> 8);
}
}
}
/** Unpremultiplies the pixel's RGB values. */
forcedinline void unpremultiply() noexcept
{
const uint32 alpha = components.a;
if (alpha < 0xff)
{
if (alpha == 0)
{
components.b = 0;
components.g = 0;
components.r = 0;
}
else
{
components.b = (uint8) jmin ((uint32) 0xffu, (components.b * 0xffu) / alpha);
components.g = (uint8) jmin ((uint32) 0xffu, (components.g * 0xffu) / alpha);
components.r = (uint8) jmin ((uint32) 0xffu, (components.r * 0xffu) / alpha);
}
}
}
forcedinline void desaturate() noexcept
{
if (components.a < 0xff && components.a > 0)
{
const int newUnpremultipliedLevel = (0xff * ((int) components.r + (int) components.g + (int) components.b) / (3 * components.a));
components.r = components.g = components.b
= (uint8) ((newUnpremultipliedLevel * components.a + 0x7f) >> 8);
}
else
{
components.r = components.g = components.b
= (uint8) (((int) components.r + (int) components.g + (int) components.b) / 3);
}
}
//==============================================================================
/** The indexes of the different components in the byte layout of this type of colour. */
#if JUCE_ANDROID
#if JUCE_BIG_ENDIAN
enum { indexA = 0, indexR = 3, indexG = 2, indexB = 1 };
#else
enum { indexA = 3, indexR = 0, indexG = 1, indexB = 2 };
#endif
#else
#if JUCE_BIG_ENDIAN
enum { indexA = 0, indexR = 1, indexG = 2, indexB = 3 };
#else
enum { indexA = 3, indexR = 2, indexG = 1, indexB = 0 };
#endif
#endif
private:
//==============================================================================
PixelARGB (const uint32 internalValue) noexcept
: internal (internalValue)
{
}
//==============================================================================
struct Components
{
#if JUCE_ANDROID
#if JUCE_BIG_ENDIAN
uint8 a, b, g, r;
#else
uint8 r, g, b, a;
#endif
#else
#if JUCE_BIG_ENDIAN
uint8 a, r, g, b;
#else
uint8 b, g, r, a;
#endif
#endif
} JUCE_PACKED;
union
{
uint32 internal;
Components components;
#if JUCE_GCC
uint8 comps[4]; // helper struct needed because gcc does not allow references to packed union members
#endif
};
}
#ifndef DOXYGEN
JUCE_PACKED
#endif
;
//==============================================================================
/**
Represents a 24-bit RGB pixel, and can perform compositing operations on it.
This is used internally by the imaging classes.
@see PixelARGB
*/
class JUCE_API PixelRGB
{
public:
/** Creates a pixel without defining its colour. */
PixelRGB() noexcept {}
~PixelRGB() noexcept {}
//==============================================================================
/** Returns a uint32 which represents the pixel in a platform dependent format which is compatible
with the native format of a PixelARGB.
@see PixelARGB::getNativeARGB */
forcedinline uint32 getNativeARGB() const noexcept
{
#if JUCE_ANDROID
return (uint32) ((0xff << 24) | r | (g << 8) | (b << 16));
#else
return (uint32) ((0xff << 24) | b | (g << 8) | (r << 16));
#endif
}
/** Returns a uint32 which will be in argb order as if constructed with the following mask operation
((alpha << 24) | (red << 16) | (green << 8) | blue). */
forcedinline uint32 getInARGBMaskOrder() const noexcept
{
#if JUCE_ANDROID
return (uint32) ((0xff << 24) | (r << 16) | (g << 8) | (b << 0));
#else
return getNativeARGB();
#endif
}
/** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words,
if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/
inline uint32 getInARGBMemoryOrder() const noexcept
{
#if JUCE_BIG_ENDIAN
return getInARGBMaskOrder();
#else
return (uint32) ((b << 24) | (g << 16) | (r << 8) | 0xff);
#endif
}
/** Return channels with an even index and insert zero bytes between them. This is useful for blending
operations. The exact channels which are returned is platform dependent but compatible with the
return value of getEvenBytes of the PixelARGB class.
@see PixelARGB::getEvenBytes */
forcedinline uint32 getEvenBytes() const noexcept
{
#if JUCE_ANDROID
return (uint32) (r | (b << 16));
#else
return (uint32) (b | (r << 16));
#endif
}
/** Return channels with an odd index and insert zero bytes between them. This is useful for blending
operations. The exact channels which are returned is platform dependent but compatible with the
return value of getOddBytes of the PixelARGB class.
@see PixelARGB::getOddBytes */
forcedinline uint32 getOddBytes() const noexcept { return (uint32)0xff0000 | g; }
//==============================================================================
forcedinline uint8 getAlpha() const noexcept { return 0xff; }
forcedinline uint8 getRed() const noexcept { return r; }
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; }
//==============================================================================
/** Copies another pixel colour over this one.
This doesn't blend it - this colour is simply replaced by the other one.
Because PixelRGB has no alpha channel, any alpha value in the source pixel
is thrown away.
*/
template <class Pixel>
forcedinline void set (const Pixel& src) noexcept
{
b = src.getBlue();
g = src.getGreen();
r = src.getRed();
}
/** Sets the pixel's colour from individual components. */
void setARGB (const uint8, const uint8 red, const uint8 green, const uint8 blue) noexcept
{
r = red;
g = green;
b = blue;
}
//==============================================================================
/** Blends another pixel onto this one.
This takes into account the opacity of the pixel being overlaid, and blends
it accordingly.
*/
template <class Pixel>
forcedinline void blend (const Pixel& src) noexcept
{
const uint32 alpha = (uint32) (0x100 - src.getAlpha());
// getEvenBytes returns 0x00rr00bb on non-android
uint32 rb = clampPixelComponents (src.getEvenBytes() + maskPixelComponents (getEvenBytes() * alpha));
// getOddBytes returns 0x00aa00gg on non-android
uint32 ag = clampPixelComponents (src.getOddBytes() + ((g * alpha) >> 8));
g = (uint8) (ag & 0xff);
#if JUCE_ANDROID
b = (uint8) (rb >> 16);
r = (uint8) (rb & 0xff);
#else
r = (uint8) (rb >> 16);
b = (uint8) (rb & 0xff);
#endif
}
forcedinline void blend (const PixelRGB src) noexcept
{
set (src);
}
/** Blends another pixel onto this one, applying an extra multiplier to its opacity.
The opacity of the pixel being overlaid is scaled by the extraAlpha factor before
being used, so this can blend semi-transparently from a PixelRGB argument.
*/
template <class Pixel>
forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept
{
uint32 ag = maskPixelComponents (extraAlpha * src.getOddBytes());
uint32 rb = maskPixelComponents (extraAlpha * src.getEvenBytes());
const uint32 alpha = 0x100 - (ag >> 16);
ag = clampPixelComponents (ag + (g * alpha >> 8));
rb = clampPixelComponents (rb + maskPixelComponents (getEvenBytes() * alpha));
g = (uint8) (ag & 0xff);
#if JUCE_ANDROID
b = (uint8) (rb >> 16);
r = (uint8) (rb & 0xff);
#else
r = (uint8) (rb >> 16);
b = (uint8) (rb & 0xff);
#endif
}
/** Blends another pixel with this one, creating a colour that is somewhere
between the two, as specified by the amount.
*/
template <class Pixel>
forcedinline void tween (const Pixel& src, const uint32 amount) noexcept
{
uint32 dEvenBytes = getEvenBytes();
dEvenBytes += (((src.getEvenBytes() - dEvenBytes) * amount) >> 8);
uint32 dOddBytes = getOddBytes();
dOddBytes += (((src.getOddBytes() - dOddBytes) * amount) >> 8);
g = (uint8) (dOddBytes & 0xff); // dOddBytes = 0x00aa00gg
#if JUCE_ANDROID
r = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00bb00rr
b = (uint8) (dEvenBytes >> 16);
#else
b = (uint8) (dEvenBytes & 0xff); // dEvenBytes = 0x00rr00bb
r = (uint8) (dEvenBytes >> 16);
#endif
}
//==============================================================================
/** This method is included for compatibility with the PixelARGB class. */
forcedinline void setAlpha (const uint8) noexcept {}
/** Multiplies the colour's alpha value with another one. */
forcedinline void multiplyAlpha (int) noexcept {}
/** Multiplies the colour's alpha value with another one. */
forcedinline void multiplyAlpha (float) noexcept {}
/** Premultiplies the pixel's RGB values by its alpha. */
forcedinline void premultiply() noexcept {}
/** Unpremultiplies the pixel's RGB values. */
forcedinline void unpremultiply() noexcept {}
forcedinline void desaturate() noexcept
{
r = g = b = (uint8) (((int) r + (int) g + (int) b) / 3);
}
//==============================================================================
/** The indexes of the different components in the byte layout of this type of colour. */
#if JUCE_MAC
enum { indexR = 0, indexG = 1, indexB = 2 };
#else
enum { indexR = 2, indexG = 1, indexB = 0 };
#endif
private:
//==============================================================================
PixelRGB (const uint32 internal) noexcept
{
#if JUCE_ANDROID
b = (uint8) (internal >> 16);
g = (uint8) (internal >> 8);
r = (uint8) (internal);
#else
r = (uint8) (internal >> 16);
g = (uint8) (internal >> 8);
b = (uint8) (internal);
#endif
}
//==============================================================================
#if JUCE_MAC
uint8 r, g, b;
#else
uint8 b, g, r;
#endif
}
#ifndef DOXYGEN
JUCE_PACKED
#endif
;
forcedinline void PixelARGB::blend (const PixelRGB src) noexcept
{
set (src);
}
//==============================================================================
/**
Represents an 8-bit single-channel pixel, and can perform compositing operations on it.
This is used internally by the imaging classes.
@see PixelARGB, PixelRGB
*/
class JUCE_API PixelAlpha
{
public:
/** Creates a pixel without defining its colour. */
PixelAlpha() noexcept {}
~PixelAlpha() noexcept {}
//==============================================================================
/** Returns a uint32 which represents the pixel in a platform dependent format which is compatible
with the native format of a PixelARGB.
@see PixelARGB::getNativeARGB */
forcedinline uint32 getNativeARGB() const noexcept { return (uint32) ((a << 24) | (a << 16) | (a << 8) | a); }
/** Returns a uint32 which will be in argb order as if constructed with the following mask operation
((alpha << 24) | (red << 16) | (green << 8) | blue). */
forcedinline uint32 getInARGBMaskOrder() const noexcept { return getNativeARGB(); }
/** Returns a uint32 which when written to memory, will be in the order a, r, g, b. In other words,
if the return-value is read as a uint8 array then the elements will be in the order of a, r, g, b*/
inline uint32 getInARGBMemoryOrder() const noexcept { return getNativeARGB(); }
/** Return channels with an even index and insert zero bytes between them. This is useful for blending
operations. The exact channels which are returned is platform dependent but compatible with the
return value of getEvenBytes of the PixelARGB class.
@see PixelARGB::getEvenBytes */
forcedinline uint32 getEvenBytes() const noexcept { return (uint32) ((a << 16) | a); }
/** Return channels with an odd index and insert zero bytes between them. This is useful for blending
operations. The exact channels which are returned is platform dependent but compatible with the
return value of getOddBytes of the PixelARGB class.
@see PixelARGB::getOddBytes */
forcedinline uint32 getOddBytes() 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; }
//==============================================================================
/** Copies another pixel colour over this one.
This doesn't blend it - this colour is simply replaced by the other one.
*/
template <class Pixel>
forcedinline void set (const Pixel& src) noexcept
{
a = src.getAlpha();
}
/** Sets the pixel's colour from individual components. */
forcedinline void setARGB (const uint8 a_, const uint8 /*r*/, const uint8 /*g*/, const uint8 /*b*/) noexcept
{
a = a_;
}
//==============================================================================
/** Blends another pixel onto this one.
This takes into account the opacity of the pixel being overlaid, and blends
it accordingly.
*/
template <class Pixel>
forcedinline void blend (const Pixel& src) noexcept
{
const int srcA = src.getAlpha();
a = (uint8) ((a * (0x100 - srcA) >> 8) + srcA);
}
/** Blends another pixel onto this one, applying an extra multiplier to its opacity.
The opacity of the pixel being overlaid is scaled by the extraAlpha factor before
being used, so this can blend semi-transparently from a PixelRGB argument.
*/
template <class Pixel>
forcedinline void blend (const Pixel& src, uint32 extraAlpha) noexcept
{
++extraAlpha;
const int srcAlpha = (int) ((extraAlpha * src.getAlpha()) >> 8);
a = (uint8) ((a * (0x100 - srcAlpha) >> 8) + srcAlpha);
}
/** Blends another pixel with this one, creating a colour that is somewhere
between the two, as specified by the amount.
*/
template <class Pixel>
forcedinline void tween (const Pixel& src, const uint32 amount) noexcept
{
a += ((src.getAlpha() - a) * amount) >> 8;
}
//==============================================================================
/** Replaces the colour's alpha value with another one. */
forcedinline void setAlpha (const uint8 newAlpha) noexcept
{
a = newAlpha;
}
/** Multiplies the colour's alpha value with another one. */
forcedinline void multiplyAlpha (int multiplier) noexcept
{
++multiplier;
a = (uint8) ((a * multiplier) >> 8);
}
forcedinline void multiplyAlpha (const float multiplier) noexcept
{
a = (uint8) (a * multiplier);
}
/** Premultiplies the pixel's RGB values by its alpha. */
forcedinline void premultiply() noexcept {}
/** Unpremultiplies the pixel's RGB values. */
forcedinline void unpremultiply() noexcept {}
forcedinline void desaturate() noexcept {}
//==============================================================================
/** The indexes of the different components in the byte layout of this type of colour. */
enum { indexA = 0 };
private:
//==============================================================================
PixelAlpha (const uint32 internal) noexcept
{
a = (uint8) (internal >> 24);
}
//==============================================================================
uint8 a;
}
#ifndef DOXYGEN
JUCE_PACKED
#endif
;
#if JUCE_MSVC
#pragma pack (pop)
#endif
} // namespace juce

+ 0
- 699
source/modules/juce_graphics/contexts/juce_GraphicsContext.cpp View File

@@ -1,699 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace
{
template <typename Type>
Rectangle<Type> coordsToRectangle (Type x, Type y, Type w, Type h) noexcept
{
#if JUCE_DEBUG
const int maxVal = 0x3fffffff;
jassert ((int) x >= -maxVal && (int) x <= maxVal
&& (int) y >= -maxVal && (int) y <= maxVal
&& (int) w >= 0 && (int) w <= maxVal
&& (int) h >= 0 && (int) h <= maxVal);
#endif
return { x, y, w, h };
}
}
//==============================================================================
LowLevelGraphicsContext::LowLevelGraphicsContext() {}
LowLevelGraphicsContext::~LowLevelGraphicsContext() {}
//==============================================================================
Graphics::Graphics (const Image& imageToDrawOnto)
: context (*imageToDrawOnto.createLowLevelContext()),
contextToDelete (&context)
{
jassert (imageToDrawOnto.isValid()); // Can't draw into a null image!
}
Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept
: context (internalContext)
{
}
Graphics::~Graphics()
{
}
//==============================================================================
void Graphics::resetToDefaultState()
{
saveStateIfPending();
context.setFill (FillType());
context.setFont (Font());
context.setInterpolationQuality (Graphics::mediumResamplingQuality);
}
bool Graphics::isVectorDevice() const
{
return context.isVectorDevice();
}
bool Graphics::reduceClipRegion (Rectangle<int> area)
{
saveStateIfPending();
return context.clipToRectangle (area);
}
bool Graphics::reduceClipRegion (int x, int y, int w, int h)
{
return reduceClipRegion (coordsToRectangle (x, y, w, h));
}
bool Graphics::reduceClipRegion (const RectangleList<int>& clipRegion)
{
saveStateIfPending();
return context.clipToRectangleList (clipRegion);
}
bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform)
{
saveStateIfPending();
context.clipToPath (path, transform);
return ! context.isClipEmpty();
}
bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform)
{
saveStateIfPending();
context.clipToImageAlpha (image, transform);
return ! context.isClipEmpty();
}
void Graphics::excludeClipRegion (Rectangle<int> rectangleToExclude)
{
saveStateIfPending();
context.excludeClipRectangle (rectangleToExclude);
}
bool Graphics::isClipEmpty() const
{
return context.isClipEmpty();
}
Rectangle<int> Graphics::getClipBounds() const
{
return context.getClipBounds();
}
void Graphics::saveState()
{
saveStateIfPending();
saveStatePending = true;
}
void Graphics::restoreState()
{
if (saveStatePending)
saveStatePending = false;
else
context.restoreState();
}
void Graphics::saveStateIfPending()
{
if (saveStatePending)
{
saveStatePending = false;
context.saveState();
}
}
void Graphics::setOrigin (Point<int> newOrigin)
{
saveStateIfPending();
context.setOrigin (newOrigin);
}
void Graphics::setOrigin (int x, int y)
{
setOrigin ({ x, y });
}
void Graphics::addTransform (const AffineTransform& transform)
{
saveStateIfPending();
context.addTransform (transform);
}
bool Graphics::clipRegionIntersects (Rectangle<int> area) const
{
return context.clipRegionIntersects (area);
}
void Graphics::beginTransparencyLayer (float layerOpacity)
{
saveStateIfPending();
context.beginTransparencyLayer (layerOpacity);
}
void Graphics::endTransparencyLayer()
{
context.endTransparencyLayer();
}
//==============================================================================
void Graphics::setColour (Colour newColour)
{
saveStateIfPending();
context.setFill (newColour);
}
void Graphics::setOpacity (const float newOpacity)
{
saveStateIfPending();
context.setOpacity (newOpacity);
}
void Graphics::setGradientFill (const ColourGradient& gradient)
{
setFillType (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);
}
void Graphics::setFillType (const FillType& newFill)
{
saveStateIfPending();
context.setFill (newFill);
}
//==============================================================================
void Graphics::setFont (const Font& newFont)
{
saveStateIfPending();
context.setFont (newFont);
}
void Graphics::setFont (const float newFontHeight)
{
setFont (context.getFont().withHeight (newFontHeight));
}
Font Graphics::getCurrentFont() const
{
return context.getFont();
}
//==============================================================================
void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY,
Justification justification) const
{
if (text.isNotEmpty())
{
// Don't pass any vertical placement flags to this method - they'll be ignored.
jassert (justification.getOnlyVerticalFlags() == 0);
auto flags = justification.getOnlyHorizontalFlags();
if (flags == Justification::right && startX < context.getClipBounds().getX())
return;
if (flags == Justification::left && startX > context.getClipBounds().getRight())
return;
GlyphArrangement arr;
arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY);
if (flags != Justification::left)
{
auto w = arr.getBoundingBox (0, -1, true).getWidth();
if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0)
w /= 2.0f;
arr.draw (*this, AffineTransform::translation (-w, 0));
}
else
{
arr.draw (*this);
}
}
}
void Graphics::drawMultiLineText (const String& text, const int startX,
const int baselineY, const int maximumLineWidth) const
{
if (text.isNotEmpty()
&& startX < context.getClipBounds().getRight())
{
GlyphArrangement arr;
arr.addJustifiedText (context.getFont(), text,
(float) startX, (float) baselineY, (float) maximumLineWidth,
Justification::left);
arr.draw (*this);
}
}
void Graphics::drawText (const String& text, Rectangle<float> area,
Justification justificationType, bool useEllipsesIfTooBig) const
{
if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer()))
{
GlyphArrangement arr;
arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f,
area.getWidth(), useEllipsesIfTooBig);
arr.justifyGlyphs (0, arr.getNumGlyphs(),
area.getX(), area.getY(), area.getWidth(), area.getHeight(),
justificationType);
arr.draw (*this);
}
}
void Graphics::drawText (const String& text, Rectangle<int> area,
Justification justificationType, bool useEllipsesIfTooBig) const
{
drawText (text, area.toFloat(), justificationType, useEllipsesIfTooBig);
}
void Graphics::drawText (const String& text, int x, int y, int width, int height,
Justification justificationType, const bool useEllipsesIfTooBig) const
{
drawText (text, coordsToRectangle (x, y, width, height), justificationType, useEllipsesIfTooBig);
}
void Graphics::drawFittedText (const String& text, Rectangle<int> area,
Justification justification,
const int maximumNumberOfLines,
const float minimumHorizontalScale) const
{
if (text.isNotEmpty() && (! area.isEmpty()) && context.clipRegionIntersects (area))
{
GlyphArrangement arr;
arr.addFittedText (context.getFont(), text,
(float) area.getX(), (float) area.getY(),
(float) area.getWidth(), (float) area.getHeight(),
justification,
maximumNumberOfLines,
minimumHorizontalScale);
arr.draw (*this);
}
}
void Graphics::drawFittedText (const String& text, int x, int y, int width, int height,
Justification justification,
const int maximumNumberOfLines,
const float minimumHorizontalScale) const
{
drawFittedText (text, coordsToRectangle (x, y, width, height),
justification, maximumNumberOfLines, minimumHorizontalScale);
}
//==============================================================================
void Graphics::fillRect (Rectangle<int> r) const
{
context.fillRect (r, false);
}
void Graphics::fillRect (Rectangle<float> r) const
{
context.fillRect (r);
}
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<float>& rectangles) const
{
context.fillRectList (rectangles);
}
void Graphics::fillRectList (const RectangleList<int>& rects) const
{
for (auto& r : rects)
context.fillRect (r, false);
}
void Graphics::fillAll() const
{
fillRect (context.getClipBounds());
}
void Graphics::fillAll (Colour colourToUse) const
{
if (! colourToUse.isTransparent())
{
auto clip = context.getClipBounds();
context.saveState();
context.setFill (colourToUse);
context.fillRect (clip, false);
context.restoreState();
}
}
//==============================================================================
void Graphics::fillPath (const Path& path) const
{
if (! (context.isClipEmpty() || path.isEmpty()))
context.fillPath (path, AffineTransform());
}
void Graphics::fillPath (const Path& path, const AffineTransform& transform) const
{
if (! (context.isClipEmpty() || path.isEmpty()))
context.fillPath (path, transform);
}
void Graphics::strokePath (const Path& path,
const PathStrokeType& strokeType,
const AffineTransform& transform) const
{
Path stroke;
strokeType.createStrokedPath (stroke, path, transform, context.getPhysicalPixelScaleFactor());
fillPath (stroke);
}
//==============================================================================
void Graphics::drawRect (float x, float y, float width, float height, float lineThickness) const
{
drawRect (coordsToRectangle (x, y, width, height), lineThickness);
}
void Graphics::drawRect (int x, int y, int width, int height, int lineThickness) const
{
drawRect (coordsToRectangle (x, y, width, height), lineThickness);
}
void Graphics::drawRect (Rectangle<int> r, int lineThickness) const
{
drawRect (r.toFloat(), (float) lineThickness);
}
void Graphics::drawRect (Rectangle<float> r, const float lineThickness) const
{
jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f);
RectangleList<float> 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 (Rectangle<float> area) const
{
Path p;
p.addEllipse (area);
fillPath (p);
}
void Graphics::fillEllipse (float x, float y, float w, float h) const
{
fillEllipse (coordsToRectangle (x, y, w, h));
}
void Graphics::drawEllipse (float x, float y, float width, float height, float lineThickness) const
{
drawEllipse (coordsToRectangle (x, y, width, height), lineThickness);
}
void Graphics::drawEllipse (Rectangle<float> area, float lineThickness) const
{
Path p;
if (area.getWidth() == area.getHeight())
{
// For a circle, we can avoid having to generate a stroke
p.addEllipse (area.expanded (lineThickness * 0.5f));
p.addEllipse (area.reduced (lineThickness * 0.5f));
p.setUsingNonZeroWinding (false);
fillPath (p);
}
else
{
p.addEllipse (area);
strokePath (p, PathStrokeType (lineThickness));
}
}
void Graphics::fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const
{
fillRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize);
}
void Graphics::fillRoundedRectangle (Rectangle<float> r, const float cornerSize) const
{
Path p;
p.addRoundedRectangle (r, cornerSize);
fillPath (p);
}
void Graphics::drawRoundedRectangle (float x, float y, float width, float height,
float cornerSize, float lineThickness) const
{
drawRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize, lineThickness);
}
void Graphics::drawRoundedRectangle (Rectangle<float> r, float cornerSize, float lineThickness) const
{
Path p;
p.addRoundedRectangle (r, cornerSize);
strokePath (p, PathStrokeType (lineThickness));
}
void Graphics::drawArrow (Line<float> line, float lineThickness, float arrowheadWidth, float arrowheadLength) const
{
Path p;
p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength);
fillPath (p);
}
void Graphics::fillCheckerBoard (Rectangle<int> area,
const int checkWidth, const int checkHeight,
Colour colour1, Colour colour2) const
{
jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less!
if (checkWidth > 0 && checkHeight > 0)
{
context.saveState();
if (colour1 == colour2)
{
context.setFill (colour1);
context.fillRect (area, false);
}
else
{
auto clipped = context.getClipBounds().getIntersection (area);
if (! clipped.isEmpty())
{
context.clipToRectangle (clipped);
const int checkNumX = (clipped.getX() - area.getX()) / checkWidth;
const int checkNumY = (clipped.getY() - area.getY()) / checkHeight;
const int startX = area.getX() + checkNumX * checkWidth;
const int startY = area.getY() + checkNumY * checkHeight;
const int right = clipped.getRight();
const int bottom = clipped.getBottom();
for (int i = 0; i < 2; ++i)
{
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<int> (x, y, checkWidth, checkHeight), false);
}
}
}
context.restoreState();
}
}
//==============================================================================
void Graphics::drawVerticalLine (const int x, float top, float bottom) const
{
if (top < bottom)
context.fillRect (Rectangle<float> ((float) x, top, 1.0f, bottom - top));
}
void Graphics::drawHorizontalLine (const int y, float left, float right) const
{
if (left < right)
context.fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
}
void Graphics::drawLine (Line<float> line) const
{
context.drawLine (line);
}
void Graphics::drawLine (float x1, float y1, float x2, float y2) const
{
context.drawLine (Line<float> (x1, y1, x2, y2));
}
void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThickness) const
{
drawLine (Line<float> (x1, y1, x2, y2), lineThickness);
}
void Graphics::drawLine (Line<float> line, const float lineThickness) const
{
Path p;
p.addLineSegment (line, lineThickness);
fillPath (p);
}
void Graphics::drawDashedLine (Line<float> line, const float* dashLengths,
int numDashLengths, float lineThickness, int n) const
{
jassert (n >= 0 && n < numDashLengths); // your start index must be valid!
const Point<double> delta ((line.getEnd() - line.getStart()).toDouble());
const double totalLen = delta.getDistanceFromOrigin();
if (totalLen >= 0.1)
{
const double onePixAlpha = 1.0 / totalLen;
for (double alpha = 0.0; alpha < 1.0;)
{
jassert (dashLengths[n] > 0); // can't have zero-length dashes!
const double lastAlpha = alpha;
alpha += dashLengths [n] * onePixAlpha;
n = (n + 1) % numDashLengths;
if ((n & 1) != 0)
{
const Line<float> segment (line.getStart() + (delta * lastAlpha).toFloat(),
line.getStart() + (delta * jmin (1.0, alpha)).toFloat());
if (lineThickness != 1.0f)
drawLine (segment, lineThickness);
else
context.drawLine (segment);
}
}
}
}
//==============================================================================
void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality)
{
saveStateIfPending();
context.setInterpolationQuality (newQuality);
}
//==============================================================================
void Graphics::drawImageAt (const Image& imageToDraw, int x, int y, bool fillAlphaChannel) const
{
drawImageTransformed (imageToDraw,
AffineTransform::translation ((float) x, (float) y),
fillAlphaChannel);
}
void Graphics::drawImage (const Image& imageToDraw, Rectangle<float> targetArea,
RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
{
if (imageToDraw.isValid())
drawImageTransformed (imageToDraw,
placementWithinTarget.getTransformToFit (imageToDraw.getBounds().toFloat(), targetArea),
fillAlphaChannelWithCurrentBrush);
}
void Graphics::drawImageWithin (const Image& imageToDraw, int dx, int dy, int dw, int dh,
RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
{
drawImage (imageToDraw, coordsToRectangle (dx, dy, dw, dh).toFloat(),
placementWithinTarget, fillAlphaChannelWithCurrentBrush);
}
void Graphics::drawImage (const Image& imageToDraw,
int dx, int dy, int dw, int dh,
int sx, int sy, int sw, int sh,
const bool fillAlphaChannelWithCurrentBrush) const
{
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 (fillAlphaChannelWithCurrentBrush)
{
context.saveState();
context.clipToImageAlpha (imageToDraw, transform);
fillAll();
context.restoreState();
}
else
{
context.drawImage (imageToDraw, transform);
}
}
}
//==============================================================================
Graphics::ScopedSaveState::ScopedSaveState (Graphics& g) : context (g)
{
context.saveState();
}
Graphics::ScopedSaveState::~ScopedSaveState()
{
context.restoreState();
}
} // namespace juce

+ 0
- 746
source/modules/juce_graphics/contexts/juce_GraphicsContext.h View File

@@ -1,746 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A graphics context, used for drawing a component or image.
When a Component needs painting, a Graphics context is passed to its
Component::paint() method, and this you then call methods within this
object to actually draw the component's content.
A Graphics can also be created from an image, to allow drawing directly onto
that image.
@see Component::paint
*/
class JUCE_API Graphics
{
public:
//==============================================================================
/** Creates a Graphics object to draw directly onto the given image.
The graphics object that is created will be set up to draw onto the image,
with the context's clipping area being the entire size of the image, and its
origin being the image's origin. To draw into a subsection of an image, use the
reduceClipRegion() and setOrigin() methods.
Obviously you shouldn't delete the image before this context is deleted.
*/
explicit Graphics (const Image& imageToDrawOnto);
/** Destructor. */
~Graphics();
//==============================================================================
/** Changes the current drawing colour.
This sets the colour that will now be used for drawing operations - it also
sets the opacity to that of the colour passed-in.
If a brush is being used when this method is called, the brush will be deselected,
and any subsequent drawing will be done with a solid colour brush instead.
@see setOpacity
*/
void setColour (Colour newColour);
/** Changes the opacity to use with the current colour.
If a solid colour is being used for drawing, this changes its opacity
to this new value (i.e. it doesn't multiply the colour's opacity by this amount).
If a gradient is being used, this will have no effect on it.
A value of 0.0 is completely transparent, 1.0 is completely opaque.
*/
void setOpacity (float newOpacity);
/** Sets the context to use a gradient for its fill pattern.
*/
void setGradientFill (const ColourGradient& gradient);
/** Sets the context to use a tiled image pattern for filling.
Make sure that you don't delete this image while it's still being used by
this context!
*/
void setTiledImageFill (const Image& imageToUse,
int anchorX, int anchorY,
float opacity);
/** Changes the current fill settings.
@see setColour, setGradientFill, setTiledImageFill
*/
void setFillType (const FillType& newFill);
//==============================================================================
/** Changes the font to use for subsequent text-drawing functions.
@see drawSingleLineText, drawMultiLineText, drawText, drawFittedText
*/
void setFont (const Font& newFont);
/** Changes the size of the currently-selected font.
This is a convenient shortcut that changes the context's current font to a
different size. The typeface won't be changed.
@see Font
*/
void setFont (float newFontHeight);
/** Returns the currently selected font. */
Font getCurrentFont() const;
/** Draws a one-line text string.
This will use the current colour (or brush) to fill the text. The font is the last
one specified by setFont().
@param text the string to draw
@param startX the position to draw the left-hand edge of the text
@param baselineY the position of the text's baseline
@param justification the horizontal flags indicate which end of the text string is
anchored at the specified point.
@see drawMultiLineText, drawText, drawFittedText, GlyphArrangement::addLineOfText
*/
void drawSingleLineText (const String& text,
int startX, int baselineY,
Justification justification = Justification::left) const;
/** Draws text across multiple lines.
This will break the text onto a new line where there's a new-line or
carriage-return character, or at a word-boundary when the text becomes wider
than the size specified by the maximumLineWidth parameter.
@see setFont, drawSingleLineText, drawFittedText, GlyphArrangement::addJustifiedText
*/
void drawMultiLineText (const String& text,
int startX, int baselineY,
int maximumLineWidth) const;
/** Draws a line of text within a specified rectangle.
The text will be positioned within the rectangle based on the justification
flags passed-in. If the string is too long to fit inside the rectangle, it will
either be truncated or will have ellipsis added to its end (if the useEllipsesIfTooBig
flag is true).
@see drawSingleLineText, drawFittedText, drawMultiLineText, GlyphArrangement::addJustifiedText
*/
void drawText (const String& text,
int x, int y, int width, int height,
Justification justificationType,
bool useEllipsesIfTooBig = true) 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,
Rectangle<int> area,
Justification justificationType,
bool useEllipsesIfTooBig = true) 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,
Rectangle<float> area,
Justification justificationType,
bool useEllipsesIfTooBig = true) 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. Pass 0 if you want it to use a default value.
@see GlyphArrangement::addFittedText
*/
void drawFittedText (const String& text,
int x, int y, int width, int height,
Justification justificationFlags,
int maximumNumberOfLines,
float minimumHorizontalScale = 0.0f) 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. Pass 0 if you want it to use a default value.
@see GlyphArrangement::addFittedText
*/
void drawFittedText (const String& text,
Rectangle<int> area,
Justification justificationFlags,
int maximumNumberOfLines,
float minimumHorizontalScale = 0.0f) const;
//==============================================================================
/** Fills the context's entire clip region with the current colour or brush.
(See also the fillAll (Colour) method which is a quick way of filling
it with a given colour).
*/
void fillAll() const;
/** Fills the context's entire clip region with a given colour.
This leaves the context's current colour and brush unchanged, it just
uses the specified colour temporarily.
*/
void fillAll (Colour colourToUse) const;
//==============================================================================
/** Fills a rectangle with the current colour or brush.
@see drawRect, fillRoundedRectangle
*/
void fillRect (Rectangle<int> rectangle) const;
/** Fills a rectangle with the current colour or brush.
@see drawRect, fillRoundedRectangle
*/
void fillRect (Rectangle<float> 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.
@see drawRect, fillRoundedRectangle
*/
void fillRect (float x, float y, float width, float height) 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<float>& 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<int>& 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 (Rectangle<float> rectangle,
float cornerSize) const;
/** Fills a rectangle with a checkerboard pattern, alternating between two colours. */
void fillCheckerBoard (Rectangle<int> area,
int checkWidth, int checkHeight,
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 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 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<int> 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<float> 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 (Rectangle<float> rectangle,
float cornerSize, float lineThickness) 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;
/** 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 (Rectangle<float> 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 (Rectangle<float> area, float lineThickness) const;
//==============================================================================
/** Draws a line between two points.
The line is 1 pixel wide and drawn with the current colour or brush.
TIP: If you're trying to draw horizontal or vertical lines, don't use this -
it's better to use fillRect() instead unless you really need an angled line.
*/
void drawLine (float startX, float startY, float endX, float endY) const;
/** Draws a line between two points with a given thickness.
TIP: If you're trying to draw horizontal or vertical lines, don't use this -
it's better to use fillRect() instead unless you really need an angled line.
@see Path::addLineSegment
*/
void drawLine (float startX, float startY, float endX, float endY, float lineThickness) const;
/** Draws a line between two points.
The line is 1 pixel wide and drawn with the current colour or brush.
TIP: If you're trying to draw horizontal or vertical lines, don't use this -
it's better to use fillRect() instead unless you really need an angled line.
*/
void drawLine (Line<float> line) const;
/** Draws a line between two points with a given thickness.
@see Path::addLineSegment
TIP: If you're trying to draw horizontal or vertical lines, don't use this -
it's better to use fillRect() instead unless you really need an angled line.
*/
void drawLine (Line<float> line, float lineThickness) const;
/** Draws a dashed line using a custom set of dash-lengths.
@param line the line to draw
@param dashLengths a series of lengths to specify the on/off lengths - e.g.
{ 4, 5, 6, 7 } will draw a line of 4 pixels, skip 5 pixels,
draw 6 pixels, skip 7 pixels, and then repeat.
@param numDashLengths the number of elements in the array (this must be an even number).
@param lineThickness the thickness of the line to draw
@param dashIndexToStartFrom the index in the dash-length array to use for the first segment
@see PathStrokeType::createDashedStroke
*/
void drawDashedLine (Line<float> line,
const float* dashLengths, int numDashLengths,
float lineThickness = 1.0f,
int dashIndexToStartFrom = 0) const;
/** Draws a vertical line of pixels at a given x position.
The x position is an integer, but the top and bottom of the line can be sub-pixel
positions, and these will be anti-aliased if necessary.
The bottom parameter must be greater than or equal to the top parameter.
*/
void drawVerticalLine (int x, float top, float bottom) const;
/** Draws a horizontal line of pixels at a given y position.
The y position is an integer, but the left and right ends of the line can be sub-pixel
positions, and these will be anti-aliased if necessary.
The right parameter must be greater than or equal to the left parameter.
*/
void drawHorizontalLine (int y, float left, float right) const;
//==============================================================================
/** Fills a path using the currently selected colour or brush. */
void fillPath (const Path& path) const;
/** Fills a path using the currently selected colour or brush, and adds a transform. */
void fillPath (const Path& path, const AffineTransform& transform) const;
/** Draws a path's outline using the currently selected colour or brush. */
void strokePath (const Path& path,
const PathStrokeType& strokeType,
const AffineTransform& transform = {}) const;
/** Draws a line with an arrowhead at its end.
@param line the line to draw
@param lineThickness the thickness of the line
@param arrowheadWidth the width of the arrow head (perpendicular to the line)
@param arrowheadLength the length of the arrow head (along the length of the line)
*/
void drawArrow (Line<float> line,
float lineThickness,
float arrowheadWidth,
float arrowheadLength) const;
//==============================================================================
/** Types of rendering quality that can be specified when drawing images.
@see Graphics::setImageResamplingQuality
*/
enum ResamplingQuality
{
lowResamplingQuality = 0, /**< Just uses a nearest-neighbour algorithm for resampling. */
mediumResamplingQuality = 1, /**< Uses bilinear interpolation for upsampling and area-averaging for downsampling. */
highResamplingQuality = 2, /**< Uses bicubic interpolation for upsampling and area-averaging for downsampling. */
};
/** Changes the quality that will be used when resampling images.
By default a Graphics object will be set to mediumRenderingQuality.
@see Graphics::drawImage, Graphics::drawImageTransformed, Graphics::drawImageWithin
*/
void setImageResamplingQuality (const ResamplingQuality newQuality);
/** Draws an image.
This will draw the whole of an image, positioning its top-left corner at the
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.
Images are composited using the context's current opacity, so if you
don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f)
(or setColour() with an opaque colour) before drawing images.
*/
void drawImageAt (const Image& imageToDraw, int topLeftX, int topLeftY,
bool fillAlphaChannelWithCurrentBrush = false) const;
/** Draws part of an image, rescaling it to fit in a given target region.
The specified area of the source image is rescaled and drawn to fill the
specifed destination rectangle.
Images are composited using the context's current opacity, so if you
don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f)
(or setColour() with an opaque colour) before drawing images.
@param imageToDraw the image to overlay
@param destX the left of the destination rectangle
@param destY the top of the destination rectangle
@param destWidth the width of the destination rectangle
@param destHeight the height of the destination rectangle
@param sourceX the left of the rectangle to copy from the source image
@param sourceY the top of the rectangle to copy from the source image
@param sourceWidth the width of the rectangle to copy from the source image
@param sourceHeight the height of the rectangle to copy from the source image
@param fillAlphaChannelWithCurrentBrush if true, then instead of drawing the source image's pixels,
the source image's alpha channel is used as a mask with
which to fill the destination using the current colour
or brush. (If the source is has no alpha channel, then
it will just fill the target with a solid rectangle)
@see setImageResamplingQuality, drawImageAt, drawImageWithin, fillAlphaMap
*/
void drawImage (const Image& imageToDraw,
int destX, int destY, int destWidth, int destHeight,
int sourceX, int sourceY, int sourceWidth, int sourceHeight,
bool fillAlphaChannelWithCurrentBrush = false) const;
/** Draws an image, having applied an affine transform to it.
This lets you throw the image around in some wacky ways, rotate it, shear,
scale it, etc.
Images are composited using the context's current opacity, so if you
don't want it to be drawn semi-transparently, be sure to call setOpacity (1.0f)
(or setColour() with an opaque colour) before drawing images.
If fillAlphaChannelWithCurrentBrush is set to true, then the image's RGB channels
are ignored and it is filled with the current brush, masked by its alpha channel.
If you want to render only a subsection of an image, use Image::getClippedImage() to
create the section that you need.
@see setImageResamplingQuality, drawImage
*/
void drawImageTransformed (const Image& imageToDraw,
const AffineTransform& transform,
bool fillAlphaChannelWithCurrentBrush = false) const;
/** Draws an image to fit within a designated rectangle.
@param imageToDraw the source image to draw
@param targetArea the target rectangle to fit it into
@param placementWithinTarget this specifies how the image should be positioned
within the target rectangle - see the RectanglePlacement
class for more details about this.
@param fillAlphaChannelWithCurrentBrush if true, then instead of drawing the image, just its
alpha channel will be used as a mask with which to
draw with the current brush or colour. This is
similar to fillAlphaMap(), and see also drawImage()
@see drawImage, drawImageTransformed, drawImageAt, RectanglePlacement
*/
void drawImage (const Image& imageToDraw, Rectangle<float> targetArea,
RectanglePlacement placementWithinTarget = RectanglePlacement::stretchToFit,
bool fillAlphaChannelWithCurrentBrush = false) const;
/** Draws an image to fit within a designated rectangle.
If the image is too big or too small for the space, it will be rescaled
to fit as nicely as it can do without affecting its aspect ratio. It will
then be placed within the target rectangle according to the justification flags
specified.
@param imageToDraw the source image to draw
@param destX top-left of the target rectangle to fit it into
@param destY top-left of the target rectangle to fit it into
@param destWidth size of the target rectangle to fit the image into
@param destHeight size of the target rectangle to fit the image into
@param placementWithinTarget this specifies how the image should be positioned
within the target rectangle - see the RectanglePlacement
class for more details about this.
@param fillAlphaChannelWithCurrentBrush if true, then instead of drawing the image, just its
alpha channel will be used as a mask with which to
draw with the current brush or colour. This is
similar to fillAlphaMap(), and see also drawImage()
@see setImageResamplingQuality, drawImage, drawImageTransformed, drawImageAt, RectanglePlacement
*/
void drawImageWithin (const Image& imageToDraw,
int destX, int destY, int destWidth, int destHeight,
RectanglePlacement placementWithinTarget,
bool fillAlphaChannelWithCurrentBrush = false) const;
//==============================================================================
/** Returns the position of the bounding box for the current clipping region.
@see getClipRegion, clipRegionIntersects
*/
Rectangle<int> getClipBounds() const;
/** Checks whether a rectangle overlaps the context's clipping region.
If this returns false, no part of the given area can be drawn onto, so this
method can be used to optimise a component's paint() method, by letting it
avoid drawing complex objects that aren't within the region being repainted.
*/
bool clipRegionIntersects (Rectangle<int> area) const;
/** Intersects the current clipping region with another region.
@returns true if the resulting clipping region is non-zero in size
@see setOrigin, clipRegionIntersects
*/
bool reduceClipRegion (int x, int y, int width, int height);
/** Intersects the current clipping region with another region.
@returns true if the resulting clipping region is non-zero in size
@see setOrigin, clipRegionIntersects
*/
bool reduceClipRegion (Rectangle<int> area);
/** Intersects the current clipping region with a rectangle list region.
@returns true if the resulting clipping region is non-zero in size
@see setOrigin, clipRegionIntersects
*/
bool reduceClipRegion (const RectangleList<int>& clipRegion);
/** Intersects the current clipping region with a path.
@returns true if the resulting clipping region is non-zero in size
@see reduceClipRegion
*/
bool reduceClipRegion (const Path& path, const AffineTransform& transform = AffineTransform());
/** Intersects the current clipping region with an image's alpha-channel.
The current clipping path is intersected with the area covered by this image's
alpha-channel, after the image has been transformed by the specified matrix.
@param image the image whose alpha-channel should be used. If the image doesn't
have an alpha-channel, it is treated as entirely opaque.
@param transform a matrix to apply to the image
@returns true if the resulting clipping region is non-zero in size
@see reduceClipRegion
*/
bool reduceClipRegion (const Image& image, const AffineTransform& transform);
/** Excludes a rectangle to stop it being drawn into. */
void excludeClipRegion (Rectangle<int> rectangleToExclude);
/** Returns true if no drawing can be done because the clip region is zero. */
bool isClipEmpty() const;
//==============================================================================
/** Saves the current graphics state on an internal stack.
To restore the state, use restoreState().
@see ScopedSaveState
*/
void saveState();
/** Restores a graphics state that was previously saved with saveState().
@see ScopedSaveState
*/
void restoreState();
/** Uses RAII to save and restore the state of a graphics context.
On construction, this calls Graphics::saveState(), and on destruction it calls
Graphics::restoreState() on the Graphics object that you supply.
*/
class ScopedSaveState
{
public:
ScopedSaveState (Graphics&);
~ScopedSaveState();
private:
Graphics& context;
JUCE_DECLARE_NON_COPYABLE (ScopedSaveState)
};
//==============================================================================
/** Begins rendering to an off-screen bitmap which will later be flattened onto the current
context with the given opacity.
The context uses an internal stack of temporary image layers to do this. When you've
finished drawing to the layer, call endTransparencyLayer() to complete the operation and
composite the finished layer. Every call to beginTransparencyLayer() MUST be matched
by a corresponding call to endTransparencyLayer()!
This call also saves the current state, and endTransparencyLayer() restores it.
*/
void beginTransparencyLayer (float layerOpacity);
/** Completes a drawing operation to a temporary semi-transparent buffer.
See beginTransparencyLayer() for more details.
*/
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<int> newOrigin);
/** Moves the position of the context's origin.
This changes the position that the context considers to be (0, 0) to
the specified position.
So if you call setOrigin (100, 100), then the position that was previously
referred to as (100, 100) will subsequently be considered to be (0, 0).
@see reduceClipRegion, addTransform
*/
void setOrigin (int newOriginX, int newOriginY);
/** Adds a transformation which will be performed on all the graphics operations that
the context subsequently performs.
After calling this, all the coordinates that are passed into the context will be
transformed by this matrix.
@see setOrigin
*/
void addTransform (const AffineTransform& transform);
/** Resets the current colour, brush, and font to default settings. */
void resetToDefaultState();
/** Returns true if this context is drawing to a vector-based device, such as a printer. */
bool isVectorDevice() const;
//==============================================================================
/** 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&) noexcept;
/** @internal */
LowLevelGraphicsContext& getInternalContext() const noexcept { return context; }
private:
//==============================================================================
LowLevelGraphicsContext& context;
ScopedPointer<LowLevelGraphicsContext> contextToDelete;
bool saveStatePending = false;
void saveStateIfPending();
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Graphics)
};
} // namespace juce

+ 0
- 101
source/modules/juce_graphics/contexts/juce_LowLevelGraphicsContext.h View File

@@ -1,101 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Interface class for graphics context objects, used internally by the Graphics class.
Users are not supposed to create instances of this class directly - do your drawing
via the Graphics object instead.
It's a base class for different types of graphics context, that may perform software-based
or OS-accelerated rendering.
E.g. the LowLevelGraphicsSoftwareRenderer renders onto an image in memory, but other
subclasses could render directly to a windows HDC, a Quartz context, or an OpenGL
context.
*/
class JUCE_API LowLevelGraphicsContext
{
protected:
//==============================================================================
LowLevelGraphicsContext();
public:
virtual ~LowLevelGraphicsContext();
/** Returns true if this device is vector-based, e.g. a printer. */
virtual bool isVectorDevice() const = 0;
//==============================================================================
/** Moves the origin to a new position.
The coordinates are relative to the current origin, and indicate the new position
of (0, 0).
*/
virtual void setOrigin (Point<int>) = 0;
virtual void addTransform (const AffineTransform&) = 0;
virtual float getPhysicalPixelScaleFactor() = 0;
virtual bool clipToRectangle (const Rectangle<int>&) = 0;
virtual bool clipToRectangleList (const RectangleList<int>&) = 0;
virtual void excludeClipRectangle (const Rectangle<int>&) = 0;
virtual void clipToPath (const Path&, const AffineTransform&) = 0;
virtual void clipToImageAlpha (const Image&, const AffineTransform&) = 0;
virtual bool clipRegionIntersects (const Rectangle<int>&) = 0;
virtual Rectangle<int> getClipBounds() const = 0;
virtual bool isClipEmpty() const = 0;
virtual void saveState() = 0;
virtual void restoreState() = 0;
virtual void beginTransparencyLayer (float opacity) = 0;
virtual void endTransparencyLayer() = 0;
//==============================================================================
virtual void setFill (const FillType&) = 0;
virtual void setOpacity (float) = 0;
virtual void setInterpolationQuality (Graphics::ResamplingQuality) = 0;
//==============================================================================
virtual void fillRect (const Rectangle<int>&, bool replaceExistingContents) = 0;
virtual void fillRect (const Rectangle<float>&) = 0;
virtual void fillRectList (const RectangleList<float>&) = 0;
virtual void fillPath (const Path&, const AffineTransform&) = 0;
virtual void drawImage (const Image&, const AffineTransform&) = 0;
virtual void drawLine (const Line<float>&) = 0;
virtual void setFont (const Font&) = 0;
virtual const Font& getFont() = 0;
virtual void drawGlyph (int glyphNumber, const AffineTransform&) = 0;
virtual bool drawTextLayout (const AttributedString&, const Rectangle<float>&) { return false; }
};
} // namespace juce

+ 0
- 540
source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.cpp View File

@@ -1,540 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
// this will throw an assertion if you try to draw something that's not
// possible in postscript
#define WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS 0
//==============================================================================
#if JUCE_DEBUG && WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS
#define notPossibleInPostscriptAssert jassertfalse
#else
#define notPossibleInPostscriptAssert
#endif
//==============================================================================
LowLevelGraphicsPostScriptRenderer::LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript,
const String& documentTitle,
const int totalWidth_,
const int totalHeight_)
: out (resultingPostScript),
totalWidth (totalWidth_),
totalHeight (totalHeight_),
needToClip (true)
{
stateStack.add (new SavedState());
stateStack.getLast()->clip = Rectangle<int> (totalWidth_, totalHeight_);
const float scale = jmin ((520.0f / totalWidth_), (750.0f / totalHeight));
out << "%!PS-Adobe-3.0 EPSF-3.0"
"\n%%BoundingBox: 0 0 600 824"
"\n%%Pages: 0"
"\n%%Creator: ROLI Ltd. JUCE"
"\n%%Title: " << documentTitle <<
"\n%%CreationDate: none"
"\n%%LanguageLevel: 2"
"\n%%EndComments"
"\n%%BeginProlog"
"\n%%BeginResource: JRes"
"\n/bd {bind def} bind def"
"\n/c {setrgbcolor} bd"
"\n/m {moveto} bd"
"\n/l {lineto} bd"
"\n/rl {rlineto} bd"
"\n/ct {curveto} bd"
"\n/cp {closepath} bd"
"\n/pr {3 index 3 index moveto 1 index 0 rlineto 0 1 index rlineto pop neg 0 rlineto pop pop closepath} bd"
"\n/doclip {initclip newpath} bd"
"\n/endclip {clip newpath} bd"
"\n%%EndResource"
"\n%%EndProlog"
"\n%%BeginSetup"
"\n%%EndSetup"
"\n%%Page: 1 1"
"\n%%BeginPageSetup"
"\n%%EndPageSetup\n\n"
<< "40 800 translate\n"
<< scale << ' ' << scale << " scale\n\n";
}
LowLevelGraphicsPostScriptRenderer::~LowLevelGraphicsPostScriptRenderer()
{
}
//==============================================================================
bool LowLevelGraphicsPostScriptRenderer::isVectorDevice() const
{
return true;
}
void LowLevelGraphicsPostScriptRenderer::setOrigin (Point<int> o)
{
if (! o.isOrigin())
{
stateStack.getLast()->xOffset += o.x;
stateStack.getLast()->yOffset += o.y;
needToClip = true;
}
}
void LowLevelGraphicsPostScriptRenderer::addTransform (const AffineTransform& /*transform*/)
{
//xxx
jassertfalse;
}
float LowLevelGraphicsPostScriptRenderer::getPhysicalPixelScaleFactor() { return 1.0f; }
bool LowLevelGraphicsPostScriptRenderer::clipToRectangle (const Rectangle<int>& r)
{
needToClip = true;
return stateStack.getLast()->clip.clipTo (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
}
bool LowLevelGraphicsPostScriptRenderer::clipToRectangleList (const RectangleList<int>& clipRegion)
{
needToClip = true;
return stateStack.getLast()->clip.clipTo (clipRegion);
}
void LowLevelGraphicsPostScriptRenderer::excludeClipRectangle (const Rectangle<int>& r)
{
needToClip = true;
stateStack.getLast()->clip.subtract (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
}
void LowLevelGraphicsPostScriptRenderer::clipToPath (const Path& path, const AffineTransform& transform)
{
writeClip();
Path p (path);
p.applyTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset));
writePath (p);
out << "clip\n";
}
void LowLevelGraphicsPostScriptRenderer::clipToImageAlpha (const Image& /*sourceImage*/, const AffineTransform& /*transform*/)
{
needToClip = true;
jassertfalse; // xxx
}
bool LowLevelGraphicsPostScriptRenderer::clipRegionIntersects (const Rectangle<int>& r)
{
return stateStack.getLast()->clip.intersectsRectangle (r.translated (stateStack.getLast()->xOffset, stateStack.getLast()->yOffset));
}
Rectangle<int> LowLevelGraphicsPostScriptRenderer::getClipBounds() const
{
return stateStack.getLast()->clip.getBounds().translated (-stateStack.getLast()->xOffset,
-stateStack.getLast()->yOffset);
}
bool LowLevelGraphicsPostScriptRenderer::isClipEmpty() const
{
return stateStack.getLast()->clip.isEmpty();
}
//==============================================================================
LowLevelGraphicsPostScriptRenderer::SavedState::SavedState()
: xOffset (0),
yOffset (0)
{
}
LowLevelGraphicsPostScriptRenderer::SavedState::~SavedState()
{
}
void LowLevelGraphicsPostScriptRenderer::saveState()
{
stateStack.add (new SavedState (*stateStack.getLast()));
}
void LowLevelGraphicsPostScriptRenderer::restoreState()
{
jassert (stateStack.size() > 0);
if (stateStack.size() > 0)
stateStack.removeLast();
}
void LowLevelGraphicsPostScriptRenderer::beginTransparencyLayer (float)
{
}
void LowLevelGraphicsPostScriptRenderer::endTransparencyLayer()
{
}
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::writeClip()
{
if (needToClip)
{
needToClip = false;
out << "doclip ";
int itemsOnLine = 0;
for (auto& i : stateStack.getLast()->clip)
{
if (++itemsOnLine == 6)
{
itemsOnLine = 0;
out << '\n';
}
out << i.getX() << ' ' << -i.getY() << ' '
<< i.getWidth() << ' ' << -i.getHeight() << " pr ";
}
out << "endclip\n";
}
}
void LowLevelGraphicsPostScriptRenderer::writeColour (Colour colour)
{
Colour c (Colours::white.overlaidWith (colour));
if (lastColour != c)
{
lastColour = c;
out << String (c.getFloatRed(), 3) << ' '
<< String (c.getFloatGreen(), 3) << ' '
<< String (c.getFloatBlue(), 3) << " c\n";
}
}
void LowLevelGraphicsPostScriptRenderer::writeXY (const float x, const float y) const
{
out << String (x, 2) << ' '
<< String (-y, 2) << ' ';
}
void LowLevelGraphicsPostScriptRenderer::writePath (const Path& path) const
{
out << "newpath ";
float lastX = 0.0f;
float lastY = 0.0f;
int itemsOnLine = 0;
Path::Iterator i (path);
while (i.next())
{
if (++itemsOnLine == 4)
{
itemsOnLine = 0;
out << '\n';
}
switch (i.elementType)
{
case Path::Iterator::startNewSubPath:
writeXY (i.x1, i.y1);
lastX = i.x1;
lastY = i.y1;
out << "m ";
break;
case Path::Iterator::lineTo:
writeXY (i.x1, i.y1);
lastX = i.x1;
lastY = i.y1;
out << "l ";
break;
case Path::Iterator::quadraticTo:
{
const float cp1x = lastX + (i.x1 - lastX) * 2.0f / 3.0f;
const float cp1y = lastY + (i.y1 - lastY) * 2.0f / 3.0f;
const float cp2x = cp1x + (i.x2 - lastX) / 3.0f;
const float cp2y = cp1y + (i.y2 - lastY) / 3.0f;
writeXY (cp1x, cp1y);
writeXY (cp2x, cp2y);
writeXY (i.x2, i.y2);
out << "ct ";
lastX = i.x2;
lastY = i.y2;
}
break;
case Path::Iterator::cubicTo:
writeXY (i.x1, i.y1);
writeXY (i.x2, i.y2);
writeXY (i.x3, i.y3);
out << "ct ";
lastX = i.x3;
lastY = i.y3;
break;
case Path::Iterator::closePath:
out << "cp ";
break;
default:
jassertfalse;
break;
}
}
out << '\n';
}
void LowLevelGraphicsPostScriptRenderer::writeTransform (const AffineTransform& trans) const
{
out << "[ "
<< trans.mat00 << ' '
<< trans.mat10 << ' '
<< trans.mat01 << ' '
<< trans.mat11 << ' '
<< trans.mat02 << ' '
<< trans.mat12 << " ] concat ";
}
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::setFill (const FillType& fillType)
{
stateStack.getLast()->fillType = fillType;
}
void LowLevelGraphicsPostScriptRenderer::setOpacity (float /*opacity*/)
{
}
void LowLevelGraphicsPostScriptRenderer::setInterpolationQuality (Graphics::ResamplingQuality /*quality*/)
{
}
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle<int>& r, const bool /*replaceExistingContents*/)
{
fillRect (r.toFloat());
}
void LowLevelGraphicsPostScriptRenderer::fillRect (const Rectangle<float>& r)
{
if (stateStack.getLast()->fillType.isColour())
{
writeClip();
writeColour (stateStack.getLast()->fillType.colour);
Rectangle<float> r2 (r.translated ((float) stateStack.getLast()->xOffset,
(float) stateStack.getLast()->yOffset));
out << r2.getX() << ' ' << -r2.getBottom() << ' ' << r2.getWidth() << ' ' << r2.getHeight() << " rectfill\n";
}
else
{
Path p;
p.addRectangle (r);
fillPath (p, AffineTransform());
}
}
void LowLevelGraphicsPostScriptRenderer::fillRectList (const RectangleList<float>& list)
{
fillPath (list.toPath(), AffineTransform());
}
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::fillPath (const Path& path, const AffineTransform& t)
{
if (stateStack.getLast()->fillType.isColour())
{
writeClip();
Path p (path);
p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset,
(float) stateStack.getLast()->yOffset));
writePath (p);
writeColour (stateStack.getLast()->fillType.colour);
out << "fill\n";
}
else if (stateStack.getLast()->fillType.isGradient())
{
// this doesn't work correctly yet - it could be improved to handle solid gradients, but
// postscript can't do semi-transparent ones.
notPossibleInPostscriptAssert; // you can disable this warning by setting the WARN_ABOUT_NON_POSTSCRIPT_OPERATIONS flag at the top of this file
writeClip();
out << "gsave ";
{
Path p (path);
p.applyTransform (t.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset));
writePath (p);
out << "clip\n";
}
const Rectangle<int> bounds (stateStack.getLast()->clip.getBounds());
// ideally this would draw lots of lines or ellipses to approximate the gradient, but for the
// time-being, this just fills it with the average colour..
writeColour (stateStack.getLast()->fillType.gradient->getColourAtPosition (0.5f));
out << bounds.getX() << ' ' << -bounds.getBottom() << ' ' << bounds.getWidth() << ' ' << bounds.getHeight() << " rectfill\n";
out << "grestore\n";
}
}
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::writeImage (const Image& im,
const int sx, const int sy,
const int maxW, const int maxH) const
{
out << "{<\n";
const int w = jmin (maxW, im.getWidth());
const int h = jmin (maxH, im.getHeight());
int charsOnLine = 0;
const Image::BitmapData srcData (im, 0, 0, w, h);
Colour pixel;
for (int y = h; --y >= 0;)
{
for (int x = 0; x < w; ++x)
{
const uint8* pixelData = srcData.getPixelPointer (x, y);
if (x >= sx && y >= sy)
{
if (im.isARGB())
{
PixelARGB p (*(const PixelARGB*) pixelData);
p.unpremultiply();
pixel = Colours::white.overlaidWith (Colour (p));
}
else if (im.isRGB())
{
pixel = Colour (*((const PixelRGB*) pixelData));
}
else
{
pixel = Colour ((uint8) 0, (uint8) 0, (uint8) 0, *pixelData);
}
}
else
{
pixel = Colours::transparentWhite;
}
const uint8 pixelValues[3] = { pixel.getRed(), pixel.getGreen(), pixel.getBlue() };
out << String::toHexString (pixelValues, 3, 0);
charsOnLine += 3;
if (charsOnLine > 100)
{
out << '\n';
charsOnLine = 0;
}
}
}
out << "\n>}\n";
}
void LowLevelGraphicsPostScriptRenderer::drawImage (const Image& sourceImage, const AffineTransform& transform)
{
const int w = sourceImage.getWidth();
const int h = sourceImage.getHeight();
writeClip();
out << "gsave ";
writeTransform (transform.translated ((float) stateStack.getLast()->xOffset, (float) stateStack.getLast()->yOffset)
.scaled (1.0f, -1.0f));
RectangleList<int> imageClip;
sourceImage.createSolidAreaMask (imageClip, 0.5f);
out << "newpath ";
int itemsOnLine = 0;
for (auto& i : imageClip)
{
if (++itemsOnLine == 6)
{
out << '\n';
itemsOnLine = 0;
}
out << i.getX() << ' ' << i.getY() << ' ' << i.getWidth() << ' ' << i.getHeight() << " pr ";
}
out << " clip newpath\n";
out << w << ' ' << h << " scale\n";
out << w << ' ' << h << " 8 [" << w << " 0 0 -" << h << ' ' << (int) 0 << ' ' << h << " ]\n";
writeImage (sourceImage, 0, 0, w, h);
out << "false 3 colorimage grestore\n";
needToClip = true;
}
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::drawLine (const Line <float>& line)
{
Path p;
p.addLineSegment (line, 1.0f);
fillPath (p, AffineTransform());
}
//==============================================================================
void LowLevelGraphicsPostScriptRenderer::setFont (const Font& newFont)
{
stateStack.getLast()->font = newFont;
}
const Font& LowLevelGraphicsPostScriptRenderer::getFont()
{
return stateStack.getLast()->font;
}
void LowLevelGraphicsPostScriptRenderer::drawGlyph (int glyphNumber, const AffineTransform& transform)
{
Path p;
Font& font = stateStack.getLast()->font;
font.getTypeface()->getOutlineForGlyph (glyphNumber, p);
fillPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight()).followedBy (transform));
}
} // namespace juce

+ 0
- 120
source/modules/juce_graphics/contexts/juce_LowLevelGraphicsPostScriptRenderer.h View File

@@ -1,120 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
An implementation of LowLevelGraphicsContext that turns the drawing operations
into a PostScript document.
*/
class JUCE_API LowLevelGraphicsPostScriptRenderer : public LowLevelGraphicsContext
{
public:
//==============================================================================
LowLevelGraphicsPostScriptRenderer (OutputStream& resultingPostScript,
const String& documentTitle,
int totalWidth,
int totalHeight);
~LowLevelGraphicsPostScriptRenderer();
//==============================================================================
bool isVectorDevice() const override;
void setOrigin (Point<int>) override;
void addTransform (const AffineTransform&) override;
float getPhysicalPixelScaleFactor() override;
bool clipToRectangle (const Rectangle<int>&) override;
bool clipToRectangleList (const RectangleList<int>&) override;
void excludeClipRectangle (const Rectangle<int>&) override;
void clipToPath (const Path&, const AffineTransform&) override;
void clipToImageAlpha (const Image&, const AffineTransform&) override;
void saveState() override;
void restoreState() override;
void beginTransparencyLayer (float) override;
void endTransparencyLayer() override;
bool clipRegionIntersects (const Rectangle<int>&) override;
Rectangle<int> getClipBounds() const override;
bool isClipEmpty() const override;
//==============================================================================
void setFill (const FillType&) override;
void setOpacity (float) override;
void setInterpolationQuality (Graphics::ResamplingQuality) override;
//==============================================================================
void fillRect (const Rectangle<int>&, bool replaceExistingContents) override;
void fillRect (const Rectangle<float>&) override;
void fillRectList (const RectangleList<float>&) override;
void fillPath (const Path&, const AffineTransform&) override;
void drawImage (const Image&, const AffineTransform&) override;
void drawLine (const Line <float>&) override;
//==============================================================================
const Font& getFont() override;
void setFont (const Font&) override;
void drawGlyph (int glyphNumber, const AffineTransform&) override;
protected:
//==============================================================================
OutputStream& out;
int totalWidth, totalHeight;
bool needToClip;
Colour lastColour;
struct SavedState
{
SavedState();
~SavedState();
RectangleList<int> clip;
int xOffset, yOffset;
FillType fillType;
Font font;
private:
SavedState& operator= (const SavedState&);
};
OwnedArray <SavedState> stateStack;
void writeClip();
void writeColour (Colour colour);
void writePath (const Path&) const;
void writeXY (float x, float y) 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)
};
} // namespace juce

+ 0
- 45
source/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp View File

@@ -1,45 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image)
: RenderingHelpers::StackBasedLowLevelGraphicsContext<RenderingHelpers::SoftwareRendererSavedState>
(new RenderingHelpers::SoftwareRendererSavedState (image, image.getBounds()))
{
}
LowLevelGraphicsSoftwareRenderer::LowLevelGraphicsSoftwareRenderer (const Image& image, Point<int> origin,
const RectangleList<int>& initialClip)
: RenderingHelpers::StackBasedLowLevelGraphicsContext<RenderingHelpers::SoftwareRendererSavedState>
(new RenderingHelpers::SoftwareRendererSavedState (image, initialClip, origin))
{
}
LowLevelGraphicsSoftwareRenderer::~LowLevelGraphicsSoftwareRenderer() {}
} // namespace juce

+ 0
- 56
source/modules/juce_graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.h View File

@@ -1,56 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A lowest-common-denominator implementation of LowLevelGraphicsContext that does all
its rendering in memory.
User code is not supposed to create instances of this class directly - do all your
rendering via the Graphics class instead.
*/
class JUCE_API LowLevelGraphicsSoftwareRenderer : public RenderingHelpers::StackBasedLowLevelGraphicsContext<RenderingHelpers::SoftwareRendererSavedState>
{
public:
//==============================================================================
/** 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<int> origin,
const RectangleList<int>& initialClip);
/** Destructor. */
~LowLevelGraphicsSoftwareRenderer();
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LowLevelGraphicsSoftwareRenderer)
};
} // namespace juce

+ 0
- 189
source/modules/juce_graphics/effects/juce_DropShadowEffect.cpp View File

@@ -1,189 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
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);
}
//==============================================================================
DropShadow::DropShadow() noexcept
: colour (0x90000000), radius (4)
{
}
DropShadow::DropShadow (Colour shadowColour, const int r, Point<int> o) noexcept
: colour (shadowColour), radius (r), offset (o)
{
jassert (radius > 0);
}
void DropShadow::drawForImage (Graphics& g, const Image& srcImage) const
{
jassert (radius > 0);
if (srcImage.isValid())
{
Image shadowImage (srcImage.convertedToFormat (Image::SingleChannel));
shadowImage.duplicateIfShared();
blurSingleChannelImage (shadowImage, radius);
g.setColour (colour);
g.drawImageAt (shadowImage, offset.x, offset.y, true);
}
}
void DropShadow::drawForPath (Graphics& g, const Path& path) const
{
jassert (radius > 0);
const Rectangle<int> 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, Rectangle<float> area,
bool isCorner, float centreX, float centreY, float edgeX, float edgeY)
{
cg.point1 = area.getRelativePoint (centreX, centreY);
cg.point2 = area.getRelativePoint (edgeX, edgeY);
cg.isRadial = isCorner;
g.setGradientFill (cg);
g.fillRect (area);
}
void DropShadow::drawForRectangle (Graphics& g, const Rectangle<int>& 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 float radiusInset = (radius + 1) / 2.0f;
const float expandedRadius = radius + radiusInset;
const Rectangle<float> area (targetArea.toFloat().reduced (radiusInset) + offset.toFloat());
Rectangle<float> r (area.expanded (expandedRadius));
Rectangle<float> top (r.removeFromTop (expandedRadius));
Rectangle<float> 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);
}
} // namespace juce

+ 0
- 110
source/modules/juce_graphics/effects/juce_DropShadowEffect.h View File

@@ -1,110 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
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<int> 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<int>& 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<int> offset;
};
//==============================================================================
/**
An effect filter that adds a drop-shadow behind the image's content.
(This will only work on images/components that aren't opaque, of course).
When added to a component, this effect will draw a soft-edged
shadow based on what gets drawn inside it. The shadow will also
be applied to the component's children.
For speed, this doesn't use a proper gaussian blur, but cheats by
using a simple bilinear filter. If you need a really high-quality
shadow, check out ImageConvolutionKernel::createGaussianBlur()
@see Component::setComponentEffect
*/
class JUCE_API DropShadowEffect : public ImageEffectFilter
{
public:
//==============================================================================
/** Creates a default drop-shadow effect.
To customise the shadow's appearance, use the setShadowProperties() method.
*/
DropShadowEffect();
/** Destructor. */
~DropShadowEffect();
//==============================================================================
/** Sets up parameters affecting the shadow's appearance. */
void setShadowProperties (const DropShadow& newShadow);
//==============================================================================
/** @internal */
void applyEffect (Image& sourceImage, Graphics& destContext, float scaleFactor, float alpha) override;
private:
//==============================================================================
DropShadow shadow;
JUCE_LEAK_DETECTOR (DropShadowEffect)
};
} // namespace juce

+ 0
- 58
source/modules/juce_graphics/effects/juce_GlowEffect.cpp View File

@@ -1,58 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
GlowEffect::GlowEffect() {}
GlowEffect::~GlowEffect() {}
void GlowEffect::setGlowProperties (float newRadius, Colour newColour, Point<int> pos)
{
radius = newRadius;
colour = newColour;
offset = pos;
}
void GlowEffect::applyEffect (Image& image, Graphics& g, float scaleFactor, float alpha)
{
Image temp (image.getFormat(), image.getWidth(), image.getHeight(), true);
ImageConvolutionKernel blurKernel (roundToInt (radius * scaleFactor * 2.0f));
blurKernel.createGaussianBlur (radius);
blurKernel.rescaleAllValues (radius);
blurKernel.applyToImage (temp, image, image.getBounds());
g.setColour (colour.withMultipliedAlpha (alpha));
g.drawImageAt (temp, offset.x, offset.y, true);
g.setOpacity (alpha);
g.drawImageAt (image, offset.x, offset.y, false);
}
} // namespace juce

+ 0
- 75
source/modules/juce_graphics/effects/juce_GlowEffect.h View File

@@ -1,75 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A component effect that adds a coloured blur around the component's contents.
(This will only work on non-opaque components).
@see Component::setComponentEffect, DropShadowEffect
*/
class JUCE_API GlowEffect : public ImageEffectFilter
{
public:
//==============================================================================
/** Creates a default 'glow' effect.
To customise its appearance, use the setGlowProperties() method.
*/
GlowEffect();
/** Destructor. */
~GlowEffect();
//==============================================================================
/** Sets the glow's radius and colour.
The radius is how large the blur should be, and the colour is
used to render it (for a less intense glow, lower the colour's
opacity).
*/
void setGlowProperties (float newRadius,
Colour newColour,
Point<int> offset = {});
//==============================================================================
/** @internal */
void applyEffect (Image&, Graphics&, float scaleFactor, float alpha) override;
private:
//==============================================================================
float radius = 2.0f;
Colour colour { Colours::white };
Point<int> offset;
JUCE_LEAK_DETECTOR (GlowEffect)
};
} // namespace juce

+ 0
- 70
source/modules/juce_graphics/effects/juce_ImageEffectFilter.h View File

@@ -1,70 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A graphical effect filter that can be applied to components.
An ImageEffectFilter can be applied to the image that a component
paints before it hits the screen.
This is used for adding effects like shadows, blurs, etc.
@see Component::setComponentEffect
*/
class JUCE_API ImageEffectFilter
{
public:
//==============================================================================
/** Overridden to render the effect.
The implementation of this method must use the image that is passed in
as its source, and should render its output to the graphics context passed in.
@param sourceImage the image that the source component has just rendered with
its paint() method. The image may or may not have an alpha
channel, depending on whether the component is opaque.
@param destContext the graphics context to use to draw the resultant image.
@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. */
virtual ~ImageEffectFilter() {}
};
} // namespace juce

+ 0
- 356
source/modules/juce_graphics/fonts/juce_AttributedString.cpp View File

@@ -1,356 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace
{
int getLength (const Array<AttributedString::Attribute>& atts) noexcept
{
return atts.size() != 0 ? atts.getReference (atts.size() - 1).range.getEnd() : 0;
}
void splitAttributeRanges (Array<AttributedString::Attribute>& atts, int position)
{
for (int i = atts.size(); --i >= 0;)
{
const AttributedString::Attribute& att = atts.getReference (i);
const int offset = position - att.range.getStart();
if (offset >= 0)
{
if (offset > 0 && position < att.range.getEnd())
{
atts.insert (i + 1, att);
atts.getReference (i).range.setEnd (position);
atts.getReference (i + 1).range.setStart (position);
}
break;
}
}
}
Range<int> splitAttributeRanges (Array<AttributedString::Attribute>& atts, Range<int> newRange)
{
newRange = newRange.getIntersectionWith (Range<int> (0, getLength (atts)));
if (! newRange.isEmpty())
{
splitAttributeRanges (atts, newRange.getStart());
splitAttributeRanges (atts, newRange.getEnd());
}
return newRange;
}
void mergeAdjacentRanges (Array<AttributedString::Attribute>& atts)
{
for (int i = atts.size() - 1; --i >= 0;)
{
AttributedString::Attribute& a1 = atts.getReference (i);
AttributedString::Attribute& a2 = atts.getReference (i + 1);
if (a1.colour == a2.colour && a1.font == a2.font)
{
a1.range.setEnd (a2.range.getEnd());
atts.remove (i + 1);
if (i < atts.size() - 1)
++i;
}
}
}
void appendRange (Array<AttributedString::Attribute>& atts,
int length, const Font* f, const Colour* c)
{
if (atts.size() == 0)
{
atts.add (AttributedString::Attribute (Range<int> (0, length),
f != nullptr ? *f : Font(),
c != nullptr ? *c : Colour (0xff000000)));
}
else
{
const int start = getLength (atts);
atts.add (AttributedString::Attribute (Range<int> (start, start + length),
f != nullptr ? *f : atts.getReference (atts.size() - 1).font,
c != nullptr ? *c : atts.getReference (atts.size() - 1).colour));
mergeAdjacentRanges (atts);
}
}
void applyFontAndColour (Array<AttributedString::Attribute>& atts,
Range<int> range, const Font* f, const Colour* c)
{
range = splitAttributeRanges (atts, range);
for (int i = 0; i < atts.size(); ++i)
{
AttributedString::Attribute& att = atts.getReference (i);
if (range.getStart() < att.range.getEnd())
{
if (range.getEnd() <= att.range.getStart())
break;
if (c != nullptr) att.colour = *c;
if (f != nullptr) att.font = *f;
}
}
mergeAdjacentRanges (atts);
}
void truncate (Array<AttributedString::Attribute>& atts, int newLength)
{
splitAttributeRanges (atts, newLength);
for (int i = atts.size(); --i >= 0;)
if (atts.getReference (i).range.getStart() >= newLength)
atts.remove (i);
}
}
//==============================================================================
AttributedString::Attribute::Attribute() noexcept : colour (0xff000000) {}
AttributedString::Attribute::~Attribute() noexcept {}
AttributedString::Attribute::Attribute (Attribute&& other) noexcept
: range (other.range),
font (static_cast<Font&&> (other.font)),
colour (other.colour)
{
}
AttributedString::Attribute& AttributedString::Attribute::operator= (Attribute&& other) noexcept
{
range = other.range;
font = static_cast<Font&&> (other.font);
colour = other.colour;
return *this;
}
AttributedString::Attribute::Attribute (const Attribute& other) noexcept
: range (other.range),
font (other.font),
colour (other.colour)
{
}
AttributedString::Attribute& AttributedString::Attribute::operator= (const Attribute& other) noexcept
{
range = other.range;
font = other.font;
colour = other.colour;
return *this;
}
AttributedString::Attribute::Attribute (Range<int> r, const Font& f, Colour c) noexcept
: range (r), font (f), colour (c)
{
}
//==============================================================================
AttributedString::AttributedString()
: lineSpacing (0.0f),
justification (Justification::left),
wordWrap (AttributedString::byWord),
readingDirection (AttributedString::natural)
{
}
AttributedString::AttributedString (const String& newString)
: lineSpacing (0.0f),
justification (Justification::left),
wordWrap (AttributedString::byWord),
readingDirection (AttributedString::natural)
{
setText (newString);
}
AttributedString::AttributedString (const AttributedString& other)
: text (other.text),
lineSpacing (other.lineSpacing),
justification (other.justification),
wordWrap (other.wordWrap),
readingDirection (other.readingDirection),
attributes (other.attributes)
{
}
AttributedString& AttributedString::operator= (const AttributedString& other)
{
if (this != &other)
{
text = other.text;
lineSpacing = other.lineSpacing;
justification = other.justification;
wordWrap = other.wordWrap;
readingDirection = other.readingDirection;
attributes = other.attributes;
}
return *this;
}
AttributedString::AttributedString (AttributedString&& other) noexcept
: text (static_cast<String&&> (other.text)),
lineSpacing (other.lineSpacing),
justification (other.justification),
wordWrap (other.wordWrap),
readingDirection (other.readingDirection),
attributes (static_cast<Array<Attribute>&&> (other.attributes))
{
}
AttributedString& AttributedString::operator= (AttributedString&& other) noexcept
{
text = static_cast<String&&> (other.text);
lineSpacing = other.lineSpacing;
justification = other.justification;
wordWrap = other.wordWrap;
readingDirection = other.readingDirection;
attributes = static_cast<Array<Attribute>&&> (other.attributes);
return *this;
}
AttributedString::~AttributedString() noexcept {}
void AttributedString::setText (const String& newText)
{
const int newLength = newText.length();
const int oldLength = getLength (attributes);
if (newLength > oldLength)
appendRange (attributes, newLength - oldLength, nullptr, nullptr);
else if (newLength < oldLength)
truncate (attributes, newLength);
text = newText;
}
void AttributedString::append (const String& textToAppend)
{
text += textToAppend;
appendRange (attributes, textToAppend.length(), nullptr, nullptr);
}
void AttributedString::append (const String& textToAppend, const Font& font)
{
text += textToAppend;
appendRange (attributes, textToAppend.length(), &font, nullptr);
}
void AttributedString::append (const String& textToAppend, Colour colour)
{
text += textToAppend;
appendRange (attributes, textToAppend.length(), nullptr, &colour);
}
void AttributedString::append (const String& textToAppend, const Font& font, Colour colour)
{
text += textToAppend;
appendRange (attributes, textToAppend.length(), &font, &colour);
}
void AttributedString::append (const AttributedString& other)
{
const int originalLength = getLength (attributes);
const int originalNumAtts = attributes.size();
text += other.text;
attributes.addArray (other.attributes);
for (int i = originalNumAtts; i < attributes.size(); ++i)
attributes.getReference (i).range += originalLength;
mergeAdjacentRanges (attributes);
}
void AttributedString::clear()
{
text.clear();
attributes.clear();
}
void AttributedString::setJustification (Justification newJustification) noexcept
{
justification = newJustification;
}
void AttributedString::setWordWrap (WordWrap newWordWrap) noexcept
{
wordWrap = newWordWrap;
}
void AttributedString::setReadingDirection (ReadingDirection newReadingDirection) noexcept
{
readingDirection = newReadingDirection;
}
void AttributedString::setLineSpacing (const float newLineSpacing) noexcept
{
lineSpacing = newLineSpacing;
}
void AttributedString::setColour (Range<int> range, Colour colour)
{
applyFontAndColour (attributes, range, nullptr, &colour);
}
void AttributedString::setFont (Range<int> range, const Font& font)
{
applyFontAndColour (attributes, range, &font, nullptr);
}
void AttributedString::setColour (Colour colour)
{
setColour (Range<int> (0, getLength (attributes)), colour);
}
void AttributedString::setFont (const Font& font)
{
setFont (Range<int> (0, getLength (attributes)), font);
}
void AttributedString::draw (Graphics& g, const Rectangle<float>& area) const
{
if (text.isNotEmpty() && g.clipRegionIntersects (area.getSmallestIntegerContainer()))
{
jassert (text.length() == getLength (attributes));
if (! g.getInternalContext().drawTextLayout (*this, area))
{
TextLayout layout;
layout.createLayout (*this, area.getWidth());
layout.draw (g, area);
}
}
}
} // namespace juce

+ 0
- 207
source/modules/juce_graphics/fonts/juce_AttributedString.h View File

@@ -1,207 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A text string with a set of colour/font settings that are associated with sub-ranges
of the text.
An attributed string lets you create a string with varied fonts, colours, word-wrapping,
layout, etc., and draw it using AttributedString::draw().
@see TextLayout
*/
class JUCE_API AttributedString
{
public:
/** Creates an empty attributed string. */
AttributedString();
/** Creates an attributed string with the given text. */
explicit AttributedString (const String& text);
AttributedString (const AttributedString&);
AttributedString& operator= (const AttributedString&);
AttributedString (AttributedString&&) noexcept;
AttributedString& operator= (AttributedString&&) noexcept;
/** Destructor. */
~AttributedString() noexcept;
//==============================================================================
/** Returns the complete text of this attributed string. */
const String& getText() const noexcept { return text; }
/** Replaces all the text.
This will change the text, but won't affect any of the colour or font attributes
that have been added.
*/
void setText (const String& newText);
/** Appends some text (with a default font and colour). */
void append (const String& textToAppend);
/** 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, Colour colour);
/** Appends some text, with a specified font and 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,
word-wrap mode, etc.
*/
void clear();
//==============================================================================
/** Draws this string within the given area.
The layout of the string within the rectangle is controlled by the justification
value passed to setJustification().
*/
void draw (Graphics& g, const Rectangle<float>& area) const;
//==============================================================================
/** Returns the justification that should be used for laying-out the text.
This may include both vertical and horizontal flags.
*/
Justification getJustification() const noexcept { return justification; }
/** Sets the justification that should be used for laying-out the text.
This may include both vertical and horizontal flags.
*/
void setJustification (Justification newJustification) noexcept;
//==============================================================================
/** Types of word-wrap behaviour.
@see getWordWrap, setWordWrap
*/
enum WordWrap
{
none, /**< No word-wrapping: lines extend indefinitely. */
byWord, /**< Lines are wrapped on a word boundary. */
byChar, /**< Lines are wrapped on a character boundary. */
};
/** Returns the word-wrapping behaviour. */
WordWrap getWordWrap() const noexcept { return wordWrap; }
/** Sets the word-wrapping behaviour. */
void setWordWrap (WordWrap newWordWrap) noexcept;
//==============================================================================
/** Types of reading direction that can be used.
@see getReadingDirection, setReadingDirection
*/
enum ReadingDirection
{
natural,
leftToRight,
rightToLeft,
};
/** Returns the reading direction for the text. */
ReadingDirection getReadingDirection() const noexcept { return readingDirection; }
/** Sets the reading direction that should be used for the text. */
void setReadingDirection (ReadingDirection newReadingDirection) noexcept;
//==============================================================================
/** Returns the extra line-spacing distance. */
float getLineSpacing() const noexcept { return lineSpacing; }
/** Sets an extra line-spacing distance. */
void setLineSpacing (float newLineSpacing) noexcept;
//==============================================================================
/** An attribute that has been applied to a range of characters in an AttributedString. */
class JUCE_API Attribute
{
public:
Attribute() noexcept;
~Attribute() noexcept;
Attribute (const Attribute&) noexcept;
Attribute& operator= (const Attribute&) noexcept;
Attribute (Attribute&&) noexcept;
Attribute& operator= (Attribute&&) noexcept;
/** Creates an attribute that specifies the font and colour for a range of characters. */
Attribute (Range<int> range, const Font& font, Colour colour) noexcept;
/** The range of characters to which this attribute will be applied. */
Range<int> range;
/** The font for this range of characters. */
Font font;
/** The colour for this range of characters. */
Colour colour;
private:
JUCE_LEAK_DETECTOR (Attribute)
};
/** Returns the number of attributes that have been added to this string. */
int getNumAttributes() const noexcept { return attributes.size(); }
/** Returns one of the string's attributes.
The index provided must be less than getNumAttributes(), and >= 0.
*/
const Attribute& getAttribute (int index) const noexcept { return attributes.getReference (index); }
//==============================================================================
/** Adds a colour attribute for the specified range. */
void setColour (Range<int> range, Colour colour);
/** Removes all existing colour attributes, and applies this colour to the whole string. */
void setColour (Colour colour);
/** Adds a font attribute for the specified range. */
void setFont (Range<int> range, const Font& font);
/** Removes all existing font attributes, and applies this font to the whole string. */
void setFont (const Font& font);
private:
String text;
float lineSpacing;
Justification justification;
WordWrap wordWrap;
ReadingDirection readingDirection;
Array<Attribute> attributes;
JUCE_LEAK_DETECTOR (AttributedString)
};
} // namespace juce

+ 0
- 413
source/modules/juce_graphics/fonts/juce_CustomTypeface.cpp View File

@@ -1,413 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
class CustomTypeface::GlyphInfo
{
public:
GlyphInfo (const juce_wchar c, const Path& p, const float w) noexcept
: character (c), path (p), width (w)
{
}
struct KerningPair
{
juce_wchar character2;
float kerningAmount;
};
void addKerningPair (const juce_wchar subsequentCharacter,
const float extraKerningAmount) noexcept
{
KerningPair kp;
kp.character2 = subsequentCharacter;
kp.kerningAmount = extraKerningAmount;
kerningPairs.add (kp);
}
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;
}
const juce_wchar character;
const Path path;
float width;
Array <KerningPair> kerningPairs;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GlyphInfo)
};
//==============================================================================
namespace CustomTypefaceHelpers
{
static juce_wchar readChar (InputStream& in)
{
uint32 n = (uint32) (uint16) in.readShort();
if (n >= 0xd800 && n <= 0xdfff)
{
const uint32 nextWord = (uint32) (uint16) in.readShort();
jassert (nextWord >= 0xdc00); // illegal unicode character!
n = 0x10000 + (((n - 0xd800) << 10) | (nextWord - 0xdc00));
}
return (juce_wchar) n;
}
static void writeChar (OutputStream& out, juce_wchar charToWrite)
{
if (charToWrite >= 0x10000)
{
charToWrite -= 0x10000;
out.writeShort ((short) (uint16) (0xd800 + (charToWrite >> 10)));
out.writeShort ((short) (uint16) (0xdc00 + (charToWrite & 0x3ff)));
}
else
{
out.writeShort ((short) (uint16) charToWrite);
}
}
}
//==============================================================================
CustomTypeface::CustomTypeface()
: Typeface (String(), String())
{
clear();
}
CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream)
: Typeface (String(), String())
{
clear();
GZIPDecompressorInputStream gzin (serialisedTypefaceStream);
BufferedInputStream in (gzin, 32768);
name = in.readString();
const bool isBold = in.readBool();
const bool isItalic = in.readBool();
style = FontStyleHelpers::getStyleName (isBold, isItalic);
ascent = in.readFloat();
defaultCharacter = CustomTypefaceHelpers::readChar (in);
int numChars = in.readInt();
for (int i = 0; i < numChars; ++i)
{
const juce_wchar c = CustomTypefaceHelpers::readChar (in);
const float width = in.readFloat();
Path p;
p.loadPathFromStream (in);
addGlyph (c, p, width);
}
const int numKerningPairs = in.readInt();
for (int i = 0; i < numKerningPairs; ++i)
{
const juce_wchar char1 = CustomTypefaceHelpers::readChar (in);
const juce_wchar char2 = CustomTypefaceHelpers::readChar (in);
addKerningPair (char1, char2, in.readFloat());
}
}
CustomTypeface::~CustomTypeface()
{
}
//==============================================================================
void CustomTypeface::clear()
{
defaultCharacter = 0;
ascent = 1.0f;
style = "Regular";
zeromem (lookupTable, sizeof (lookupTable));
glyphs.clear();
}
void CustomTypeface::setCharacteristics (const String& newName, const float newAscent, const bool isBold,
const bool isItalic, const juce_wchar newDefaultCharacter) noexcept
{
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
{
// Check that you're not trying to add the same character twice..
jassert (findGlyph (character, false) == nullptr);
if (isPositiveAndBelow ((int) character, numElementsInArray (lookupTable)))
lookupTable [character] = (short) glyphs.size();
glyphs.add (new GlyphInfo (character, path, width));
}
void CustomTypeface::addKerningPair (const juce_wchar char1, const juce_wchar char2, const float extraAmount) noexcept
{
if (extraAmount != 0.0f)
{
if (auto* g = findGlyph (char1, true))
g->addKerningPair (char2, extraAmount);
else
jassertfalse; // can only add kerning pairs for characters that exist!
}
}
CustomTypeface::GlyphInfo* CustomTypeface::findGlyph (const juce_wchar character, const bool loadIfNeeded) noexcept
{
if (isPositiveAndBelow ((int) character, numElementsInArray (lookupTable)) && lookupTable [character] > 0)
return glyphs [(int) lookupTable [(int) character]];
for (int i = 0; i < glyphs.size(); ++i)
{
GlyphInfo* const g = glyphs.getUnchecked(i);
if (g->character == character)
return g;
}
if (loadIfNeeded && loadGlyphIfPossible (character))
return findGlyph (character, false);
return nullptr;
}
bool CustomTypeface::loadGlyphIfPossible (const juce_wchar /*characterNeeded*/)
{
return false;
}
void CustomTypeface::addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept
{
setCharacteristics (name, style, typefaceToCopy.getAscent(), defaultCharacter);
for (int i = 0; i < numCharacters; ++i)
{
const juce_wchar c = (juce_wchar) (characterStartIndex + static_cast<juce_wchar> (i));
Array <int> glyphIndexes;
Array <float> offsets;
typefaceToCopy.getGlyphPositions (String::charToString (c), glyphIndexes, offsets);
const int glyphIndex = glyphIndexes.getFirst();
if (glyphIndex >= 0 && glyphIndexes.size() > 0)
{
const float glyphWidth = offsets[1];
Path p;
typefaceToCopy.getOutlineForGlyph (glyphIndex, p);
addGlyph (c, p, glyphWidth);
for (int j = glyphs.size() - 1; --j >= 0;)
{
const juce_wchar char2 = glyphs.getUnchecked (j)->character;
glyphIndexes.clearQuick();
offsets.clearQuick();
typefaceToCopy.getGlyphPositions (String::charToString (c) + String::charToString (char2), glyphIndexes, offsets);
if (offsets.size() > 1)
addKerningPair (c, char2, offsets[1] - glyphWidth);
}
}
}
}
bool CustomTypeface::writeToStream (OutputStream& outputStream)
{
GZIPCompressorOutputStream out (&outputStream);
out.writeString (name);
out.writeBool (FontStyleHelpers::isBold (style));
out.writeBool (FontStyleHelpers::isItalic (style));
out.writeFloat (ascent);
CustomTypefaceHelpers::writeChar (out, defaultCharacter);
out.writeInt (glyphs.size());
int numKerningPairs = 0;
for (int i = 0; i < glyphs.size(); ++i)
{
const GlyphInfo* const g = glyphs.getUnchecked (i);
CustomTypefaceHelpers::writeChar (out, g->character);
out.writeFloat (g->width);
g->path.writePathToStream (out);
numKerningPairs += g->kerningPairs.size();
}
out.writeInt (numKerningPairs);
for (int i = 0; i < glyphs.size(); ++i)
{
const GlyphInfo* const g = glyphs.getUnchecked (i);
for (int j = 0; j < g->kerningPairs.size(); ++j)
{
const GlyphInfo::KerningPair& p = g->kerningPairs.getReference (j);
CustomTypefaceHelpers::writeChar (out, g->character);
CustomTypefaceHelpers::writeChar (out, p.character2);
out.writeFloat (p.kerningAmount);
}
}
return true;
}
//==============================================================================
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;
for (String::CharPointerType t (text.getCharPointer()); ! t.isEmpty();)
{
const juce_wchar c = t.getAndAdvance();
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));
}
}
return x;
}
void CustomTypeface::getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array<float>& xOffsets)
{
xOffsets.add (0);
float x = 0;
for (String::CharPointerType t (text.getCharPointer()); ! t.isEmpty();)
{
float width = 0.0f;
int glyphChar = 0;
const juce_wchar c = t.getAndAdvance();
if (const GlyphInfo* const glyph = findGlyph (c, true))
{
width = glyph->getHorizontalSpacing (*t);
glyphChar = (int) glyph->character;
}
else
{
const Typeface::Ptr fallbackTypeface (getFallbackTypeface());
if (fallbackTypeface != nullptr && fallbackTypeface != this)
{
Array <int> subGlyphs;
Array <float> subOffsets;
fallbackTypeface->getGlyphPositions (String::charToString (c), subGlyphs, subOffsets);
if (subGlyphs.size() > 0)
{
glyphChar = subGlyphs.getFirst();
width = subOffsets[1];
}
}
}
x += width;
resultGlyphs.add (glyphChar);
xOffsets.add (x);
}
}
bool CustomTypeface::getOutlineForGlyph (int glyphNumber, Path& path)
{
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, float fontHeight)
{
if (const GlyphInfo* const glyph = findGlyph ((juce_wchar) glyphNumber, true))
{
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, fontHeight);
}
return nullptr;
}
} // namespace juce

+ 0
- 165
source/modules/juce_graphics/fonts/juce_CustomTypeface.h View File

@@ -1,165 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A typeface that can be populated with custom glyphs.
You can create a CustomTypeface if you need one that contains your own glyphs,
or if you need to load a typeface from a Juce-formatted binary stream.
If you want to create a copy of a native face, you can use addGlyphsFromOtherTypeface()
to copy glyphs into this face.
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 Projucer,
and then call Typeface::createSystemTypefaceFor() to load it from memory.
@see Typeface, Font
*/
class JUCE_API CustomTypeface : public Typeface
{
public:
//==============================================================================
/** Creates a new, empty typeface. */
CustomTypeface();
/** Loads a typeface from a previously saved stream.
The stream must have been created by writeToStream().
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);
/** Destructor. */
~CustomTypeface();
//==============================================================================
/** Resets this typeface, deleting all its glyphs and settings. */
void clear();
/** Sets the vital statistics for the typeface.
@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& 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
origin is the anchor point of the character on its baseline.
The width is the nominal width of the character, and any extra kerning values that
are specified will be added to this width.
*/
void addGlyph (juce_wchar character, const Path& path, float width) noexcept;
/** Specifies an extra kerning amount to be used between a pair of characters.
The amount will be added to the nominal width of the first character when laying out a string.
*/
void addKerningPair (juce_wchar char1, juce_wchar char2, float extraAmount) noexcept;
/** Adds a range of glyphs from another typeface.
This will attempt to pull in the paths and kerning information from another typeface and
add it to this one.
*/
void addGlyphsFromOtherTypeface (Typeface& typefaceToCopy, juce_wchar characterStartIndex, int numCharacters) noexcept;
/** 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 override;
float getDescent() const override;
float getHeightToPointsFactor() const override;
float getStringWidth (const String&) override;
void getGlyphPositions (const String&, Array <int>& glyphs, Array<float>& xOffsets) override;
bool getOutlineForGlyph (int glyphNumber, Path&) override;
EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform&, float fontHeight) override;
protected:
//==============================================================================
juce_wchar defaultCharacter;
float ascent;
//==============================================================================
/** If a subclass overrides this, it can load glyphs into the font on-demand.
When methods such as getGlyphPositions() or getOutlineForGlyph() are asked for a
particular character and there's no corresponding glyph, they'll call this
method so that a subclass can try to add that glyph, returning true if it
manages to do so.
*/
virtual bool loadGlyphIfPossible (juce_wchar characterNeeded);
private:
//==============================================================================
class GlyphInfo;
friend struct ContainerDeletePolicy<GlyphInfo>;
OwnedArray<GlyphInfo> glyphs;
short lookupTable [128];
GlyphInfo* findGlyph (const juce_wchar character, bool loadIfNeeded) noexcept;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomTypeface)
};
} // namespace juce

+ 0
- 722
source/modules/juce_graphics/fonts/juce_Font.cpp View File

@@ -1,722 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
namespace FontValues
{
static float limitFontHeight (const float height) noexcept
{
return jlimit (0.1f, 10000.0f, height);
}
const float defaultFontHeight = 14.0f;
float minimumHorizontalScale = 0.7f;
String fallbackFont;
String fallbackFontStyle;
}
typedef Typeface::Ptr (*GetTypefaceForFont) (const Font&);
GetTypefaceForFont juce_getTypefaceForFont = nullptr;
float Font::getDefaultMinimumHorizontalScaleFactor() noexcept { return FontValues::minimumHorizontalScale; }
void Font::setDefaultMinimumHorizontalScaleFactor (float newValue) noexcept { FontValues::minimumHorizontalScale = newValue; }
//==============================================================================
class TypefaceCache : private DeletedAtShutdown
{
public:
TypefaceCache()
{
setSize (10);
}
~TypefaceCache()
{
clearSingletonInstance();
}
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 ScopedReadLock slr (lock);
auto faceName = font.getTypefaceName();
auto faceStyle = font.getTypefaceStyle();
jassert (faceName.isNotEmpty());
for (int i = faces.size(); --i >= 0;)
{
CachedFace& face = faces.getReference(i);
if (face.typefaceName == faceName
&& face.typefaceStyle == faceStyle
&& face.typeface != nullptr
&& face.typeface->isSuitableForFont (font))
{
face.lastUsageCount = ++counter;
return face.typeface;
}
}
const ScopedWriteLock slw (lock);
int replaceIndex = 0;
auto bestLastUsageCount = std::numeric_limits<size_t>::max();
for (int i = faces.size(); --i >= 0;)
{
auto lu = faces.getReference(i).lastUsageCount;
if (bestLastUsageCount > lu)
{
bestLastUsageCount = lu;
replaceIndex = i;
}
}
auto& face = faces.getReference (replaceIndex);
face.typefaceName = faceName;
face.typefaceStyle = faceStyle;
face.lastUsageCount = ++counter;
if (juce_getTypefaceForFont == nullptr)
face.typeface = Font::getDefaultTypefaceForFont (font);
else
face.typeface = juce_getTypefaceForFont (font);
jassert (face.typeface != nullptr); // the look and feel must return a typeface!
if (defaultFace == nullptr && font == Font())
defaultFace = face.typeface;
return face.typeface;
}
Typeface::Ptr defaultFace;
private:
struct CachedFace
{
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. "<Sans-Serif>" 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, typefaceStyle;
size_t lastUsageCount;
Typeface::Ptr typeface;
};
ReadWriteLock lock;
Array<CachedFace> faces;
size_t counter = 0;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TypefaceCache)
};
juce_ImplementSingleton (TypefaceCache)
void Typeface::setTypefaceCacheSize (int numFontsToCache)
{
TypefaceCache::getInstance()->setSize (numFontsToCache);
}
void (*clearOpenGLGlyphCache)() = nullptr;
void Typeface::clearTypefaceCache()
{
TypefaceCache::getInstance()->clear();
RenderingHelpers::SoftwareRendererSavedState::clearGlyphCache();
if (clearOpenGLGlyphCache != nullptr)
clearOpenGLGlyphCache();
}
//==============================================================================
class Font::SharedFontInternal : public ReferenceCountedObject
{
public:
SharedFontInternal() noexcept
: typeface (TypefaceCache::getInstance()->defaultFace),
typefaceName (Font::getDefaultSansSerifFontName()),
typefaceStyle (Font::getDefaultStyle()),
height (FontValues::defaultFontHeight)
{
}
SharedFontInternal (int styleFlags, float fontHeight) noexcept
: typefaceName (Font::getDefaultSansSerifFontName()),
typefaceStyle (FontStyleHelpers::getStyleName (styleFlags)),
height (fontHeight),
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),
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)
{
if (typefaceName.isEmpty())
typefaceName = Font::getDefaultSansSerifFontName();
}
explicit SharedFontInternal (const Typeface::Ptr& face) noexcept
: typeface (face),
typefaceName (face->getName()),
typefaceStyle (face->getStyle()),
height (FontValues::defaultFontHeight)
{
jassert (typefaceName.isNotEmpty());
}
SharedFontInternal (const SharedFontInternal& other) noexcept
: ReferenceCountedObject(),
typeface (other.typeface),
typefaceName (other.typefaceName),
typefaceStyle (other.typefaceStyle),
height (other.height),
horizontalScale (other.horizontalScale),
kerning (other.kerning),
ascent (other.ascent),
underline (other.underline)
{
}
bool operator== (const SharedFontInternal& other) const noexcept
{
return height == other.height
&& underline == other.underline
&& horizontalScale == other.horizontalScale
&& kerning == other.kerning
&& typefaceName == other.typefaceName
&& typefaceStyle == other.typefaceStyle;
}
Typeface::Ptr typeface;
String typefaceName, typefaceStyle;
float height, horizontalScale = 1.0f, kerning = 0, ascent = 0;
bool underline = false;
};
//==============================================================================
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 String& typefaceName, float fontHeight, int styleFlags)
: font (new SharedFontInternal (typefaceName, styleFlags, FontValues::limitFontHeight (fontHeight)))
{
}
Font::Font (const String& typefaceName, const String& typefaceStyle, float fontHeight)
: font (new SharedFontInternal (typefaceName, typefaceStyle, FontValues::limitFontHeight (fontHeight)))
{
}
Font& Font::operator= (const Font& other) noexcept
{
font = other.font;
return *this;
}
Font::Font (Font&& other) noexcept
: font (static_cast<ReferenceCountedObjectPtr<SharedFontInternal>&&> (other.font))
{
}
Font& Font::operator= (Font&& other) noexcept
{
font = static_cast<ReferenceCountedObjectPtr<SharedFontInternal>&&> (other.font);
return *this;
}
Font::~Font() noexcept
{
}
bool Font::operator== (const Font& other) const noexcept
{
return font == other.font
|| *font == *other.font;
}
bool Font::operator!= (const Font& other) const noexcept
{
return ! operator== (other);
}
void Font::dupeInternalIfShared()
{
if (font->getReferenceCount() > 1)
font = new SharedFontInternal (*font);
}
void Font::checkTypefaceSuitability()
{
if (font->typeface != nullptr && ! font->typeface->isSuitableForFont (*this))
font->typeface = nullptr;
}
//==============================================================================
struct FontPlaceholderNames
{
String sans { "<Sans-Serif>" },
serif { "<Serif>" },
mono { "<Monospaced>" },
regular { "<Regular>" };
};
const FontPlaceholderNames& getFontPlaceholderNames()
{
static FontPlaceholderNames names;
return names;
}
#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::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::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;
font->ascent = 0;
}
}
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;
}
//==============================================================================
const String& Font::getFallbackFontName()
{
return FontValues::fallbackFont;
}
void Font::setFallbackFontName (const String& name)
{
FontValues::fallbackFont = name;
#if JUCE_MAC || JUCE_IOS
jassertfalse; // Note that use of a fallback font isn't currently implemented in OSX..
#endif
}
const String& Font::getFallbackFontStyle()
{
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);
f.setHeight (newHeight);
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);
if (font->height != newHeight)
{
dupeInternalIfShared();
font->height = newHeight;
checkTypefaceSuitability();
}
}
void Font::setHeightWithoutChangingWidth (float newHeight)
{
newHeight = FontValues::limitFontHeight (newHeight);
if (font->height != newHeight)
{
dupeInternalIfShared();
font->horizontalScale *= (font->height / newHeight);
font->height = newHeight;
checkTypefaceSuitability();
}
}
int Font::getStyleFlags() const noexcept
{
int styleFlags = font->underline ? underlined : plain;
if (isBold()) styleFlags |= bold;
if (isItalic()) styleFlags |= italic;
return styleFlags;
}
Font Font::withStyle (const int newFlags) const
{
Font f (*this);
f.setStyleFlags (newFlags);
return f;
}
void Font::setStyleFlags (const int newFlags)
{
if (getStyleFlags() != newFlags)
{
dupeInternalIfShared();
font->typeface = nullptr;
font->typefaceStyle = FontStyleHelpers::getStyleName (newFlags);
font->underline = (newFlags & underlined) != 0;
font->ascent = 0;
}
}
void Font::setSizeAndStyle (float newHeight,
const int newStyleFlags,
const float newHorizontalScale,
const float newKerningAmount)
{
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();
}
setStyleFlags (newStyleFlags);
}
void Font::setSizeAndStyle (float newHeight,
const String& newStyle,
const float newHorizontalScale,
const float newKerningAmount)
{
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
{
Font f (*this);
f.setHorizontalScale (newHorizontalScale);
return f;
}
void Font::setHorizontalScale (const float scaleFactor)
{
dupeInternalIfShared();
font->horizontalScale = scaleFactor;
checkTypefaceSuitability();
}
float Font::getHorizontalScale() const noexcept
{
return font->horizontalScale;
}
float Font::getExtraKerningFactor() const noexcept
{
return font->kerning;
}
Font Font::withExtraKerningFactor (const float extraKerning) const
{
Font f (*this);
f.setExtraKerningFactor (extraKerning);
return f;
}
void Font::setExtraKerningFactor (const float extraKerning)
{
dupeInternalIfShared();
font->kerning = extraKerning;
checkTypefaceSuitability();
}
Font Font::boldened() const { return withStyle (getStyleFlags() | bold); }
Font Font::italicised() const { return withStyle (getStyleFlags() | italic); }
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)
{
auto flags = getStyleFlags();
setStyleFlags (shouldBeBold ? (flags | bold)
: (flags & ~bold));
}
void Font::setItalic (const bool shouldBeItalic)
{
auto flags = getStyleFlags();
setStyleFlags (shouldBeItalic ? (flags | italic)
: (flags & ~italic));
}
void Font::setUnderline (const bool shouldBeUnderlined)
{
dupeInternalIfShared();
font->underline = shouldBeUnderlined;
checkTypefaceSuitability();
}
float Font::getAscent() const
{
if (font->ascent == 0.0f)
font->ascent = getTypeface()->getAscent();
return font->height * font->ascent;
}
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
{
return (int) std::ceil (getStringWidthFloat (text));
}
float Font::getStringWidthFloat (const String& text) const
{
// This call isn't thread-safe when there's a message thread running
jassert (MessageManager::getInstanceWithoutCreating() == nullptr
|| MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
auto w = getTypeface()->getStringWidth (text);
if (font->kerning != 0.0f)
w += font->kerning * text.length();
return w * font->height * font->horizontalScale;
}
void Font::getGlyphPositions (const String& text, Array<int>& glyphs, Array<float>& xOffsets) const
{
// This call isn't thread-safe when there's a message thread running
jassert (MessageManager::getInstanceWithoutCreating() == nullptr
|| MessageManager::getInstanceWithoutCreating()->currentThreadHasLockedMessageManager());
getTypeface()->getGlyphPositions (text, glyphs, xOffsets);
if (auto num = xOffsets.size())
{
auto scale = font->height * font->horizontalScale;
auto* x = xOffsets.getRawDataPointer();
if (font->kerning != 0.0f)
{
for (int i = 0; i < num; ++i)
x[i] = (x[i] + i * font->kerning) * scale;
}
else
{
for (int i = 0; i < num; ++i)
x[i] *= scale;
}
}
}
void Font::findFonts (Array<Font>& destArray)
{
for (auto& name : findAllTypefaceNames())
{
auto styles = findAllTypefaceStyles (name);
String style ("Regular");
if (! styles.contains (style, true))
style = styles[0];
destArray.add (Font (name, style, FontValues::defaultFontHeight));
}
}
//==============================================================================
String Font::toString() const
{
String s;
if (getTypefaceName() != getDefaultSansSerifFontName())
s << getTypefaceName() << "; ";
s << String (getHeight(), 1);
if (getTypefaceStyle() != getDefaultStyle())
s << ' ' << getTypefaceStyle();
return s;
}
Font Font::fromString (const String& fontDescription)
{
const int separator = fontDescription.indexOfChar (';');
String name;
if (separator > 0)
name = fontDescription.substring (0, separator).trim();
if (name.isEmpty())
name = getDefaultSansSerifFontName();
String sizeAndStyle (fontDescription.substring (separator + 1).trimStart());
float height = sizeAndStyle.getFloatValue();
if (height <= 0)
height = 10.0f;
const String style (sizeAndStyle.fromFirstOccurrenceOf (" ", false, false));
return Font (name, style, height);
}
} // namespace juce

+ 0
- 478
source/modules/juce_graphics/fonts/juce_Font.h View File

@@ -1,478 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a particular font, including its size, style, etc.
Apart from the typeface to be used, a Font object also dictates whether
the font is bold, italic, underlined, how big it is, and its kerning and
horizontal scale factor.
@see Typeface
*/
class JUCE_API Font
{
public:
//==============================================================================
/** A combination of these values is used by the constructor to specify the
style of font to use.
*/
enum FontStyleFlags
{
plain = 0, /**< indicates a plain, non-bold, non-italic version of the font. @see setStyleFlags */
bold = 1, /**< boldens the font. @see setStyleFlags */
italic = 2, /**< finds an italic version of the font. @see setStyleFlags */
underlined = 4 /**< underlines the font. @see setStyleFlags */
};
//==============================================================================
/** Creates a sans-serif font in a given size.
@param fontHeight the height in pixels (can be fractional)
@param styleFlags the style to use - this can be a combination of the
Font::bold, Font::italic and Font::underlined, or
just Font::plain for the normal style.
@see FontStyleFlags, getDefaultSansSerifFontName
*/
Font (float fontHeight, int styleFlags = plain);
/** Creates a font with a given typeface and parameters.
@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
just Font::plain for the normal style.
@see FontStyleFlags, getDefaultSansSerifFontName
*/
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;
/** Creates a font for a typeface. */
Font (const Typeface::Ptr& typeface);
/** Creates a basic sans-serif font at a default height.
You should use one of the other constructors for creating a font that you're planning
on drawing with - this constructor is here to help initialise objects before changing
the font's settings later.
*/
Font();
/** Move constructor */
Font (Font&& other) noexcept;
/** Move assignment operator */
Font& operator= (Font&& other) noexcept;
/** Copies this font from another one. */
Font& operator= (const Font& other) noexcept;
bool operator== (const Font& other) const noexcept;
bool operator!= (const Font& other) const noexcept;
/** Destructor. */
~Font() noexcept;
//==============================================================================
/** 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 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 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 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 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 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 font family of this font.
@see setTypefaceName, getDefaultSerifFontName, getDefaultMonospacedFontName
*/
static const String& getDefaultSansSerifFontName();
/** 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 font family of this font.
@see setTypefaceName, getDefaultSansSerifFontName, getDefaultMonospacedFontName
*/
static const String& getDefaultSerifFontName();
/** 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 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 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
*/
void setHeight (float newHeight);
/** Changes the font's height without changing its width.
This alters the horizontal scale to compensate for the change in height.
*/
void setHeightWithoutChangingWidth (float newHeight);
/** 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 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
enum, to describe whether the font is bold, italic, etc.
@see FontStyleFlags, withStyle
*/
int getStyleFlags() const noexcept;
/** Returns a copy of this font with the given set of style flags.
@param styleFlags a bitwise-or'ed combination of values from the FontStyleFlags enum.
@see FontStyleFlags, getStyleFlags
*/
Font withStyle (int styleFlags) const;
/** Changes the font's style.
@param newFlags a bitwise-or'ed combination of values from the FontStyleFlags enum.
@see FontStyleFlags, withStyle
*/
void setStyleFlags (int newFlags);
//==============================================================================
/** Makes the font bold or non-bold. */
void setBold (bool shouldBeBold);
/** Returns a copy of this font with the bold attribute set.
If the font does not have a bold version, this will return the default font.
*/
Font boldened() const;
/** Returns true if the font is bold. */
bool isBold() const noexcept;
/** Makes the font italic or non-italic. */
void setItalic (bool shouldBeItalic);
/** Returns a copy of this font with the italic attribute set. */
Font italicised() const;
/** Returns true if the font is italic. */
bool isItalic() const noexcept;
/** Makes the font underlined or non-underlined. */
void setUnderline (bool shouldBeUnderlined);
/** Returns true if the font is underlined. */
bool isUnderlined() const noexcept;
//==============================================================================
/** Returns the font's horizontal scale.
A value of 1.0 is the normal scale, less than this will be narrower, greater
than 1.0 will be stretched out.
@see withHorizontalScale
*/
float getHorizontalScale() const noexcept;
/** Returns a copy of this font with a new horizontal scale.
@param scaleFactor a value of 1.0 is the normal scale, less than this will be
narrower, greater than 1.0 will be stretched out.
@see getHorizontalScale
*/
Font withHorizontalScale (float scaleFactor) const;
/** Changes the font's horizontal scale factor.
@param scaleFactor a value of 1.0 is the normal scale, less than this will be
narrower, greater than 1.0 will be stretched out.
*/
void setHorizontalScale (float scaleFactor);
/** Returns the minimum horizontal scale to which fonts may be squashed when trying to
create a layout.
@see setDefaultMinimumHorizontalScaleFactor
*/
static float getDefaultMinimumHorizontalScaleFactor() noexcept;
/** Sets the minimum horizontal scale to which fonts may be squashed when trying to
create a text layout.
@see getDefaultMinimumHorizontalScaleFactor
*/
static void setDefaultMinimumHorizontalScaleFactor (float newMinimumScaleFactor) noexcept;
/** Returns the font's kerning.
This is the extra space added between adjacent characters, as a proportion
of the font's height.
A value of zero is normal spacing, positive values will spread the letters
out more, and negative values make them closer together.
*/
float getExtraKerningFactor() const noexcept;
/** Returns a copy of this font with a new kerning factor.
@param extraKerning a multiple of the font's height that will be added
to space between the characters. So a value of zero is
normal spacing, positive values spread the letters out,
negative values make them closer together.
*/
Font withExtraKerningFactor (float extraKerning) const;
/** Changes the font's kerning.
@param extraKerning a multiple of the font's height that will be added
to space between the characters. So a value of zero is
normal spacing, positive values spread the letters out,
negative values make them closer together.
*/
void setExtraKerningFactor (float extraKerning);
//==============================================================================
/** Changes all the font's characteristics with one call. */
void setSizeAndStyle (float newHeight,
int newStyleFlags,
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;
/** Returns the series of glyph numbers and their x offsets needed to represent a string.
An extra x offset is added at the end of the run, to indicate where the right hand
edge of the last character is.
*/
void getGlyphPositions (const String& text, Array <int>& glyphs, Array <float>& xOffsets) const;
//==============================================================================
/** Returns the typeface used by this font.
Note that the object returned may go out of scope if this font is deleted
or has its style changed.
*/
Typeface* getTypeface() const;
/** Creates an array of Font objects to represent all the fonts on the system.
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<Font>& results);
/** 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 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 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) 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
style. To recreate the font from this string, use fromString().
*/
String toString() const;
/** Recreates a font from its stringified encoding.
This method takes a string that was created by toString(), and recreates the
original font.
*/
static Font fromString (const String& fontDescription);
private:
//==============================================================================
class SharedFontInternal;
ReferenceCountedObjectPtr<SharedFontInternal> font;
void dupeInternalIfShared();
void checkTypefaceSuitability();
float getHeightToPointsFactor() const;
JUCE_LEAK_DETECTOR (Font)
};
} // namespace juce

+ 0
- 820
source/modules/juce_graphics/fonts/juce_GlyphArrangement.cpp View File

@@ -1,820 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
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_),
x (x_), y (y_), w (w_), whitespace (whitespace_)
{
}
PositionedGlyph::PositionedGlyph (const PositionedGlyph& other)
: font (other.font), character (other.character), glyph (other.glyph),
x (other.x), y (other.y), w (other.w), whitespace (other.whitespace)
{
}
PositionedGlyph::PositionedGlyph (PositionedGlyph&& other) noexcept
: font (static_cast<Font&&> (other.font)),
character (other.character), glyph (other.glyph),
x (other.x), y (other.y), w (other.w), whitespace (other.whitespace)
{
}
PositionedGlyph& PositionedGlyph::operator= (PositionedGlyph&& other) noexcept
{
font = static_cast<Font&&> (other.font);
character = other.character;
glyph = other.glyph;
x = other.x;
y = other.y;
w = other.w;
whitespace = other.whitespace;
return *this;
}
PositionedGlyph::~PositionedGlyph() {}
PositionedGlyph& PositionedGlyph::operator= (const PositionedGlyph& other)
{
font = other.font;
character = other.character;
glyph = other.glyph;
x = other.x;
y = other.y;
w = other.w;
whitespace = other.whitespace;
return *this;
}
static inline void drawGlyphWithFont (Graphics& g, int glyph, const Font& font, const AffineTransform& t)
{
LowLevelGraphicsContext& context = g.getInternalContext();
context.setFont (font);
context.drawGlyph (glyph, t);
}
void PositionedGlyph::draw (Graphics& g) const
{
if (! isWhitespace())
drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y));
}
void PositionedGlyph::draw (Graphics& g, const AffineTransform& transform) const
{
if (! isWhitespace())
drawGlyphWithFont (g, glyph, font, AffineTransform::translation (x, y).followedBy (transform));
}
void PositionedGlyph::createPath (Path& path) const
{
if (! isWhitespace())
{
if (auto* t = font.getTypeface())
{
Path p;
t->getOutlineForGlyph (glyph, p);
path.addPath (p, AffineTransform::scale (font.getHeight() * font.getHorizontalScale(), font.getHeight())
.translated (x, y));
}
}
}
bool PositionedGlyph::hitTest (float px, float py) const
{
if (getBounds().contains (px, py) && ! isWhitespace())
{
if (auto* t = font.getTypeface())
{
Path p;
t->getOutlineForGlyph (glyph, p);
AffineTransform::translation (-x, -y)
.scaled (1.0f / (font.getHeight() * font.getHorizontalScale()), 1.0f / font.getHeight())
.transformPoint (px, py);
return p.contains (px, py);
}
}
return false;
}
void PositionedGlyph::moveBy (const float deltaX,
const float deltaY)
{
x += deltaX;
y += deltaY;
}
//==============================================================================
GlyphArrangement::GlyphArrangement()
{
glyphs.ensureStorageAllocated (128);
}
GlyphArrangement::GlyphArrangement (const GlyphArrangement& other)
: glyphs (other.glyphs)
{
}
GlyphArrangement& GlyphArrangement::operator= (const GlyphArrangement& other)
{
glyphs = other.glyphs;
return *this;
}
GlyphArrangement::~GlyphArrangement()
{
}
//==============================================================================
void GlyphArrangement::clear()
{
glyphs.clear();
}
PositionedGlyph& GlyphArrangement::getGlyph (const int index) const noexcept
{
return glyphs.getReference (index);
}
//==============================================================================
void GlyphArrangement::addGlyphArrangement (const GlyphArrangement& other)
{
glyphs.addArray (other.glyphs);
}
void GlyphArrangement::addGlyph (const PositionedGlyph& glyph)
{
glyphs.add (glyph);
}
void GlyphArrangement::removeRangeOfGlyphs (int startIndex, const int num)
{
glyphs.removeRange (startIndex, num < 0 ? glyphs.size() : num);
}
//==============================================================================
void GlyphArrangement::addLineOfText (const Font& font,
const String& text,
const float xOffset,
const float yOffset)
{
addCurtailedLineOfText (font, text, xOffset, yOffset, 1.0e10f, false);
}
void GlyphArrangement::addCurtailedLineOfText (const Font& font,
const String& text,
const float xOffset,
const float yOffset,
const float maxWidthPixels,
const bool useEllipsis)
{
if (text.isNotEmpty())
{
Array<int> newGlyphs;
Array<float> xOffsets;
font.getGlyphPositions (text, newGlyphs, xOffsets);
const int textLen = newGlyphs.size();
glyphs.ensureStorageAllocated (glyphs.size() + textLen);
auto t = text.getCharPointer();
for (int i = 0; i < textLen; ++i)
{
const float nextX = xOffsets.getUnchecked (i + 1);
if (nextX > maxWidthPixels + 1.0f)
{
// curtail the string if it's too wide..
if (useEllipsis && textLen > 3 && glyphs.size() >= 3)
insertEllipsis (font, xOffset + maxWidthPixels, 0, glyphs.size());
break;
}
const float thisX = xOffsets.getUnchecked (i);
const bool isWhitespace = t.isWhitespace();
glyphs.add (PositionedGlyph (font, t.getAndAdvance(),
newGlyphs.getUnchecked(i),
xOffset + thisX, yOffset,
nextX - thisX, isWhitespace));
}
}
}
int GlyphArrangement::insertEllipsis (const Font& font, const float maxXPos,
const int startIndex, int endIndex)
{
int numDeleted = 0;
if (glyphs.size() > 0)
{
Array<int> dotGlyphs;
Array<float> dotXs;
font.getGlyphPositions ("..", dotGlyphs, dotXs);
const float dx = dotXs[1];
float xOffset = 0.0f, yOffset = 0.0f;
while (endIndex > startIndex)
{
auto& pg = glyphs.getReference (--endIndex);
xOffset = pg.x;
yOffset = pg.y;
glyphs.remove (endIndex);
++numDeleted;
if (xOffset + dx * 3 <= maxXPos)
break;
}
for (int i = 3; --i >= 0;)
{
glyphs.insert (endIndex++, PositionedGlyph (font, '.', dotGlyphs.getFirst(),
xOffset, yOffset, dx, false));
--numDeleted;
xOffset += dx;
if (xOffset > maxXPos)
break;
}
}
return numDeleted;
}
void GlyphArrangement::addJustifiedText (const Font& font,
const String& text,
float x, float y,
const float maxLineWidth,
Justification horizontalLayout)
{
int lineStartIndex = glyphs.size();
addLineOfText (font, text, x, y);
const float originalY = y;
while (lineStartIndex < glyphs.size())
{
int i = lineStartIndex;
if (glyphs.getReference(i).getCharacter() != '\n'
&& glyphs.getReference(i).getCharacter() != '\r')
++i;
const float lineMaxX = glyphs.getReference (lineStartIndex).getLeft() + maxLineWidth;
int lastWordBreakIndex = -1;
while (i < glyphs.size())
{
auto& pg = glyphs.getReference (i);
const juce_wchar c = pg.getCharacter();
if (c == '\r' || c == '\n')
{
++i;
if (c == '\r' && i < glyphs.size()
&& glyphs.getReference(i).getCharacter() == '\n')
++i;
break;
}
if (pg.isWhitespace())
{
lastWordBreakIndex = i + 1;
}
else if (pg.getRight() - 0.0001f >= lineMaxX)
{
if (lastWordBreakIndex >= 0)
i = lastWordBreakIndex;
break;
}
++i;
}
const float currentLineStartX = glyphs.getReference (lineStartIndex).getLeft();
float currentLineEndX = currentLineStartX;
for (int j = i; --j >= lineStartIndex;)
{
if (! glyphs.getReference (j).isWhitespace())
{
currentLineEndX = glyphs.getReference (j).getRight();
break;
}
}
float deltaX = 0.0f;
if (horizontalLayout.testFlags (Justification::horizontallyJustified))
spreadOutLine (lineStartIndex, i - lineStartIndex, maxLineWidth);
else if (horizontalLayout.testFlags (Justification::horizontallyCentred))
deltaX = (maxLineWidth - (currentLineEndX - currentLineStartX)) * 0.5f;
else if (horizontalLayout.testFlags (Justification::right))
deltaX = maxLineWidth - (currentLineEndX - currentLineStartX);
moveRangeOfGlyphs (lineStartIndex, i - lineStartIndex,
x + deltaX - currentLineStartX, y - originalY);
lineStartIndex = i;
y += font.getHeight();
}
}
void GlyphArrangement::addFittedText (const Font& f,
const String& text,
const float x, const float y,
const float width, const float height,
Justification layout,
int maximumLines,
float minimumHorizontalScale)
{
if (minimumHorizontalScale == 0.0f)
minimumHorizontalScale = Font::getDefaultMinimumHorizontalScaleFactor();
// doesn't make much sense if this is outside a sensible range of 0.5 to 1.0
jassert (minimumHorizontalScale > 0 && minimumHorizontalScale <= 1.0f);
if (text.containsAnyOf ("\r\n"))
{
addLinesWithLineBreaks (text, f, x, y, width, height, layout);
}
else
{
const int startIndex = glyphs.size();
const String trimmed (text.trim());
addLineOfText (f, trimmed, x, y);
const int numGlyphs = glyphs.size() - startIndex;
if (numGlyphs > 0)
{
const float lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
- glyphs.getReference (startIndex).getLeft();
if (lineWidth > 0)
{
if (lineWidth * minimumHorizontalScale < width)
{
if (lineWidth > width)
stretchRangeOfGlyphs (startIndex, numGlyphs, width / lineWidth);
justifyGlyphs (startIndex, numGlyphs, x, y, width, height, layout);
}
else if (maximumLines <= 1)
{
fitLineIntoSpace (startIndex, numGlyphs, x, y, width, height,
f, layout, minimumHorizontalScale);
}
else
{
splitLines (trimmed, f, startIndex, x, y, width, height,
maximumLines, lineWidth, layout, minimumHorizontalScale);
}
}
}
}
}
//==============================================================================
void GlyphArrangement::moveRangeOfGlyphs (int startIndex, int num, const float dx, const float dy)
{
jassert (startIndex >= 0);
if (dx != 0.0f || dy != 0.0f)
{
if (num < 0 || startIndex + num > glyphs.size())
num = glyphs.size() - startIndex;
while (--num >= 0)
glyphs.getReference (startIndex++).moveBy (dx, dy);
}
}
void GlyphArrangement::addLinesWithLineBreaks (const String& text, const Font& f,
float x, float y, float width, float height, Justification layout)
{
GlyphArrangement ga;
ga.addJustifiedText (f, text, x, y, width, layout);
auto bb = ga.getBoundingBox (0, -1, false);
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());
ga.moveRangeOfGlyphs (0, -1, 0.0f, dy);
glyphs.addArray (ga.glyphs);
}
int GlyphArrangement::fitLineIntoSpace (int start, int numGlyphs, float x, float y, float w, float h, const Font& font,
Justification justification, float minimumHorizontalScale)
{
int numDeleted = 0;
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.getReference (start + numGlyphs - 1).getRight() - lineStartX - 0.5f;
}
if (lineWidth > w)
{
numDeleted = insertEllipsis (font, lineStartX + w, start, start + numGlyphs);
numGlyphs -= numDeleted;
}
}
justifyGlyphs (start, numGlyphs, x, y, w, h, justification);
return numDeleted;
}
void GlyphArrangement::stretchRangeOfGlyphs (int startIndex, int num,
const float horizontalScaleFactor)
{
jassert (startIndex >= 0);
if (num < 0 || startIndex + num > glyphs.size())
num = glyphs.size() - startIndex;
if (num > 0)
{
const float xAnchor = glyphs.getReference (startIndex).getLeft();
while (--num >= 0)
{
auto& pg = glyphs.getReference (startIndex++);
pg.x = xAnchor + (pg.x - xAnchor) * horizontalScaleFactor;
pg.font.setHorizontalScale (pg.font.getHorizontalScale() * horizontalScaleFactor);
pg.w *= horizontalScaleFactor;
}
}
}
Rectangle<float> GlyphArrangement::getBoundingBox (int startIndex, int num, const bool includeWhitespace) const
{
jassert (startIndex >= 0);
if (num < 0 || startIndex + num > glyphs.size())
num = glyphs.size() - startIndex;
Rectangle<float> result;
while (--num >= 0)
{
auto& pg = glyphs.getReference (startIndex++);
if (includeWhitespace || ! pg.isWhitespace())
result = result.getUnion (pg.getBounds());
}
return result;
}
void GlyphArrangement::justifyGlyphs (const int startIndex, const int num,
const float x, const float y, const float width, const float height,
Justification justification)
{
jassert (num >= 0 && startIndex >= 0);
if (glyphs.size() > 0 && num > 0)
{
auto bb = getBoundingBox (startIndex, num, ! justification.testFlags (Justification::horizontallyJustified
| Justification::horizontallyCentred));
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::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.getReference (startIndex).getBaselineY();
int i;
for (i = 0; i < num; ++i)
{
const float glyphY = glyphs.getReference (startIndex + i).getBaselineY();
if (glyphY != baseY)
{
spreadOutLine (startIndex + lineStart, i - lineStart, width);
lineStart = i;
baseY = glyphY;
}
}
if (i > lineStart)
spreadOutLine (startIndex + lineStart, i - lineStart, width);
}
}
}
void GlyphArrangement::spreadOutLine (const int start, const int num, const float targetWidth)
{
if (start + num < glyphs.size()
&& 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.getReference (start + i).isWhitespace())
{
++spacesAtEnd;
++numSpaces;
}
else
{
spacesAtEnd = 0;
}
}
numSpaces -= spacesAtEnd;
if (numSpaces > 0)
{
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;
float deltaX = 0.0f;
for (int i = 0; i < num; ++i)
{
glyphs.getReference (start + i).moveBy (deltaX, 0.0f);
if (glyphs.getReference (start + i).isWhitespace())
deltaX += extraPaddingBetweenWords;
}
}
}
}
void GlyphArrangement::splitLines (const String& text, Font font, int startIndex,
float x, float y, float width, float height, int maximumLines,
float lineWidth, Justification layout, float minimumHorizontalScale)
{
const int length = text.length();
const int originalStartIndex = startIndex;
int numLines = 1;
if (length <= 12 && ! text.containsAnyOf (" -\t\r\n"))
maximumLines = 1;
maximumLines = jmin (maximumLines, length);
while (numLines < maximumLines)
{
++numLines;
const float newFontHeight = height / (float) numLines;
if (newFontHeight < font.getHeight())
{
font.setHeight (jmax (8.0f, newFontHeight));
removeRangeOfGlyphs (startIndex, -1);
addLineOfText (font, text, x, y);
lineWidth = glyphs.getReference (glyphs.size() - 1).getRight()
- glyphs.getReference (startIndex).getLeft();
}
// Try to estimate the point at which there are enough lines to fit the text,
// allowing for unevenness in the lengths due to differently sized words.
const float lineLengthUnevennessAllowance = 80.0f;
if (numLines > (lineWidth + lineLengthUnevennessAllowance) / width || newFontHeight < 8.0f)
break;
}
if (numLines < 1)
numLines = 1;
float lineY = y;
float widthPerLine = lineWidth / numLines;
while (lineY < y + height)
{
int i = startIndex;
const float lineStartX = glyphs.getReference (startIndex).getLeft();
const float lineBottomY = lineY + font.getHeight();
if (lineBottomY >= y + height)
{
widthPerLine = width;
i = glyphs.size();
}
else
{
while (i < glyphs.size())
{
lineWidth = (glyphs.getReference (i).getRight() - lineStartX);
if (lineWidth > widthPerLine)
{
// got to a point where the line's too long, so skip forward to find a
// good place to break it..
const int searchStartIndex = i;
while (i < glyphs.size())
{
if ((glyphs.getReference (i).getRight() - lineStartX) * minimumHorizontalScale < width)
{
if (glyphs.getReference (i).isWhitespace()
|| glyphs.getReference (i).getCharacter() == '-')
{
++i;
break;
}
}
else
{
// can't find a suitable break, so try looking backwards..
i = searchStartIndex;
for (int back = 1; back < jmin (7, i - startIndex - 1); ++back)
{
if (glyphs.getReference (i - back).isWhitespace()
|| glyphs.getReference (i - back).getCharacter() == '-')
{
i -= back - 1;
break;
}
}
break;
}
++i;
}
break;
}
++i;
}
int wsStart = i;
while (wsStart > 0 && glyphs.getReference (wsStart - 1).isWhitespace())
--wsStart;
int wsEnd = i;
while (wsEnd < glyphs.size() && glyphs.getReference (wsEnd).isWhitespace())
++wsEnd;
removeRangeOfGlyphs (wsStart, wsEnd - wsStart);
i = jmax (wsStart, startIndex + 1);
}
i -= fitLineIntoSpace (startIndex, i - startIndex,
x, lineY, width, font.getHeight(), font,
layout.getOnlyHorizontalFlags() | Justification::verticallyCentred,
minimumHorizontalScale);
startIndex = i;
lineY = lineBottomY;
if (startIndex >= glyphs.size())
break;
}
justifyGlyphs (originalStartIndex, glyphs.size() - originalStartIndex,
x, y, width, height, layout.getFlags() & ~Justification::horizontallyJustified);
}
//==============================================================================
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
{
draw (g, AffineTransform());
}
void GlyphArrangement::draw (const Graphics& g, const AffineTransform& transform) const
{
auto& context = g.getInternalContext();
Font lastFont (context.getFont());
bool needToRestore = false;
for (int i = 0; i < glyphs.size(); ++i)
{
auto& pg = glyphs.getReference(i);
if (pg.font.isUnderlined())
drawGlyphUnderline (g, pg, i, transform);
if (! pg.isWhitespace())
{
if (lastFont != pg.font)
{
lastFont = pg.font;
if (! needToRestore)
{
needToRestore = true;
context.saveState();
}
context.setFont (lastFont);
}
context.drawGlyph (pg.glyph, AffineTransform::translation (pg.x, pg.y).followedBy (transform));
}
}
if (needToRestore)
context.restoreState();
}
void GlyphArrangement::createPath (Path& path) const
{
for (auto& g : glyphs)
g.createPath (path);
}
int GlyphArrangement::findGlyphIndexAt (const float x, const float y) const
{
for (int i = 0; i < glyphs.size(); ++i)
if (glyphs.getReference (i).hitTest (x, y))
return i;
return -1;
}
} // namespace juce

+ 0
- 327
source/modules/juce_graphics/fonts/juce_GlyphArrangement.h View File

@@ -1,327 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A glyph from a particular font, with a particular size, style,
typeface and position.
You should rarely need to use this class directly - for most purposes, the
GlyphArrangement class will do what you need for text layout.
@see GlyphArrangement, Font
*/
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&);
PositionedGlyph& operator= (const PositionedGlyph&);
/** Move constructor */
PositionedGlyph (PositionedGlyph&&) noexcept;
/** Move assignment operator */
PositionedGlyph& operator= (PositionedGlyph&&) noexcept;
~PositionedGlyph();
/** Returns the character the glyph represents. */
juce_wchar getCharacter() const noexcept { return character; }
/** Checks whether the glyph is actually empty. */
bool isWhitespace() const noexcept { return whitespace; }
/** Returns the position of the glyph's left-hand edge. */
float getLeft() const noexcept { return x; }
/** Returns the position of the glyph's right-hand edge. */
float getRight() const noexcept { return x + w; }
/** Returns the y position of the glyph's baseline. */
float getBaselineY() const noexcept { return y; }
/** Returns the y position of the top of the glyph. */
float getTop() const { return y - font.getAscent(); }
/** Returns the y position of the bottom of the glyph. */
float getBottom() const { return y + font.getDescent(); }
/** Returns the bounds of the glyph. */
Rectangle<float> getBounds() const { return Rectangle<float> (x, getTop(), w, font.getHeight()); }
//==============================================================================
/** Shifts the glyph's position by a relative amount. */
void moveBy (float deltaX, float deltaY);
//==============================================================================
/** Draws the glyph into a graphics context.
(Note that this may change the context's currently selected font).
*/
void draw (Graphics& g) const;
/** Draws the glyph into a graphics context, with an extra transform applied to it.
(Note that this may change the context's currently selected font).
*/
void draw (Graphics& g, const AffineTransform& transform) const;
/** Returns the path for this glyph.
@param path the glyph's outline will be appended to this path
*/
void createPath (Path& path) const;
/** Checks to see if a point lies within this glyph. */
bool hitTest (float x, float y) const;
private:
//==============================================================================
friend class GlyphArrangement;
Font font;
juce_wchar character;
int glyph;
float x, y, w;
bool whitespace;
JUCE_LEAK_DETECTOR (PositionedGlyph)
};
//==============================================================================
/**
A set of glyphs, each with a position.
You can create a GlyphArrangement, text to it and then draw it onto a
graphics context. It's used internally by the text methods in the
Graphics class, but can be used directly if more control is needed.
@see Font, PositionedGlyph
*/
class JUCE_API GlyphArrangement
{
public:
//==============================================================================
/** Creates an empty arrangement. */
GlyphArrangement();
/** Takes a copy of another arrangement. */
GlyphArrangement (const GlyphArrangement&);
/** Copies another arrangement onto this one.
To add another arrangement without clearing this one, use addGlyphArrangement().
*/
GlyphArrangement& operator= (const GlyphArrangement&);
/** Destructor. */
~GlyphArrangement();
//==============================================================================
/** Returns the total number of glyphs in the arrangement. */
int getNumGlyphs() const noexcept { return glyphs.size(); }
/** Returns one of the glyphs from the arrangement.
@param index the glyph's index, from 0 to (getNumGlyphs() - 1). Be
careful not to pass an out-of-range index here, as it
doesn't do any bounds-checking.
*/
PositionedGlyph& getGlyph (int index) const noexcept;
//==============================================================================
/** Clears all text from the arrangement and resets it. */
void clear();
/** Appends a line of text to the arrangement.
This will add the text as a single line, where x is the left-hand edge of the
first character, and y is the position for the text's baseline.
If the text contains new-lines or carriage-returns, this will ignore them - use
addJustifiedText() to add multi-line arrangements.
*/
void addLineOfText (const Font& font,
const String& text,
float x, float y);
/** Adds a line of text, truncating it if it's wider than a specified size.
This is the same as addLineOfText(), but if the line's width exceeds the value
specified in maxWidthPixels, it will be truncated using either ellipsis (i.e. dots: "..."),
if useEllipsis is true, or if this is false, it will just drop any subsequent characters.
*/
void addCurtailedLineOfText (const Font& font,
const String& text,
float x, float y,
float maxWidthPixels,
bool useEllipsis);
/** Adds some multi-line text, breaking lines at word-boundaries if they are too wide.
This will add text to the arrangement, breaking it into new lines either where there
is a new-line or carriage-return character in the text, or where a line's width
exceeds the value set in maxLineWidth.
Each line that is added will be laid out using the flags set in horizontalLayout, so
the lines can be left- or right-justified, or centred horizontally in the space
between x and (x + maxLineWidth).
The y 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,
Justification horizontalLayout);
/** Tries to fit some text within 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. Pass 0 if you want it to use the default value.
@see Graphics::drawFittedText
*/
void addFittedText (const Font& font,
const String& text,
float x, float y, float width, float height,
Justification layout,
int maximumLinesToUse,
float minimumHorizontalScale = 0.0f);
/** Appends another glyph arrangement to this one. */
void addGlyphArrangement (const GlyphArrangement&);
/** Appends a custom glyph to the arrangement. */
void addGlyph (const PositionedGlyph&);
//==============================================================================
/** Draws this glyph arrangement to a graphics context.
This uses cached bitmaps so is much faster than the draw (Graphics&, const AffineTransform&)
method, which renders the glyphs as filled vectors.
*/
void draw (const Graphics&) 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&, 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 coordinate.
@returns the index of the glyph, or -1 if none were found.
*/
int findGlyphIndexAt (float x, float y) const;
//==============================================================================
/** Finds the smallest rectangle that will enclose a subset of the glyphs.
@param startIndex the first glyph to test
@param numGlyphs the number of glyphs to include; if this is < 0, all glyphs after
startIndex will be included
@param includeWhitespace if true, the extent of any whitespace characters will also
be taken into account
*/
Rectangle<float> getBoundingBox (int startIndex, int numGlyphs, bool includeWhitespace) const;
/** Shifts a set of glyphs by a given amount.
@param startIndex the first glyph to transform
@param numGlyphs the number of glyphs to move; if this is < 0, all glyphs after
startIndex will be used
@param deltaX the amount to add to their x-positions
@param deltaY the amount to add to their y-positions
*/
void moveRangeOfGlyphs (int startIndex, int numGlyphs,
float deltaX, float deltaY);
/** Removes a set of glyphs from the arrangement.
@param startIndex the first glyph to remove
@param numGlyphs the number of glyphs to remove; if this is < 0, all glyphs after
startIndex will be deleted
*/
void removeRangeOfGlyphs (int startIndex, int numGlyphs);
/** Expands or compresses a set of glyphs horizontally.
@param startIndex the first glyph to transform
@param numGlyphs the number of glyphs to stretch; if this is < 0, all glyphs after
startIndex will be used
@param horizontalScaleFactor how much to scale their horizontal width by
*/
void stretchRangeOfGlyphs (int startIndex, int numGlyphs,
float horizontalScaleFactor);
/** Justifies a set of glyphs within a given space.
This moves the glyphs as a block so that the whole thing is located within the
given rectangle with the specified layout.
If the Justification::horizontallyJustified flag is specified, each line will
be stretched out to fill the specified width.
*/
void justifyGlyphs (int startIndex, int numGlyphs,
float x, float y, float width, float height,
Justification justification);
private:
//==============================================================================
Array<PositionedGlyph> 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&,
Justification, float minimumHorizontalScale);
void spreadOutLine (int start, int numGlyphs, float targetWidth);
void splitLines (const String&, Font, int start, float x, float y, float w, float h, int maxLines,
float lineWidth, Justification, float minimumHorizontalScale);
void addLinesWithLineBreaks (const String&, const Font&, float x, float y, float width, float height, Justification);
void drawGlyphUnderline (const Graphics&, const PositionedGlyph&, int, const AffineTransform&) const;
JUCE_LEAK_DETECTOR (GlyphArrangement)
};
} // namespace juce

+ 0
- 592
source/modules/juce_graphics/fonts/juce_TextLayout.cpp View File

@@ -1,592 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
TextLayout::Glyph::Glyph (const int glyph, Point<float> anch, float w) noexcept
: glyphCode (glyph), anchor (anch), width (w)
{
}
TextLayout::Glyph::Glyph (const Glyph& other) noexcept
: glyphCode (other.glyphCode), anchor (other.anchor), width (other.width)
{
}
TextLayout::Glyph& TextLayout::Glyph::operator= (const Glyph& other) noexcept
{
glyphCode = other.glyphCode;
anchor = other.anchor;
width = other.width;
return *this;
}
TextLayout::Glyph::~Glyph() noexcept {}
//==============================================================================
TextLayout::Run::Run() noexcept
: colour (0xff000000)
{
}
TextLayout::Run::Run (Range<int> range, int numGlyphsToPreallocate)
: colour (0xff000000), stringRange (range)
{
glyphs.ensureStorageAllocated (numGlyphsToPreallocate);
}
TextLayout::Run::Run (const Run& other)
: font (other.font),
colour (other.colour),
glyphs (other.glyphs),
stringRange (other.stringRange)
{
}
TextLayout::Run::~Run() noexcept {}
//==============================================================================
TextLayout::Line::Line() noexcept
: ascent (0.0f), descent (0.0f), leading (0.0f)
{
}
TextLayout::Line::Line (Range<int> range, Point<float> o, float asc, float desc,
float lead, int numRunsToPreallocate)
: stringRange (range), lineOrigin (o),
ascent (asc), descent (desc), leading (lead)
{
runs.ensureStorageAllocated (numRunsToPreallocate);
}
TextLayout::Line::Line (const Line& other)
: stringRange (other.stringRange), lineOrigin (other.lineOrigin),
ascent (other.ascent), descent (other.descent), leading (other.leading)
{
runs.addCopiesOf (other.runs);
}
TextLayout::Line::~Line() noexcept
{
}
Range<float> TextLayout::Line::getLineBoundsX() const noexcept
{
Range<float> range;
bool isFirst = true;
for (auto* run : runs)
{
for (auto& glyph : run->glyphs)
{
Range<float> runRange (glyph.anchor.x, glyph.anchor.x + glyph.width);
if (isFirst)
{
isFirst = false;
range = runRange;
}
else
{
range = range.getUnionWith (runRange);
}
}
}
return range + lineOrigin.x;
}
Range<float> TextLayout::Line::getLineBoundsY() const noexcept
{
return { lineOrigin.y - ascent,
lineOrigin.y + descent };
}
Rectangle<float> TextLayout::Line::getLineBounds() const noexcept
{
auto x = getLineBoundsX();
auto y = getLineBoundsY();
return { x.getStart(), y.getStart(), x.getLength(), y.getLength() };
}
//==============================================================================
TextLayout::TextLayout()
: width (0), height (0), justification (Justification::topLeft)
{
}
TextLayout::TextLayout (const TextLayout& other)
: width (other.width), height (other.height),
justification (other.justification)
{
lines.addCopiesOf (other.lines);
}
TextLayout::TextLayout (TextLayout&& other) noexcept
: lines (static_cast<OwnedArray<Line>&&> (other.lines)),
width (other.width), height (other.height),
justification (other.justification)
{
}
TextLayout& TextLayout::operator= (TextLayout&& other) noexcept
{
lines = static_cast<OwnedArray<Line>&&> (other.lines);
width = other.width;
height = other.height;
justification = other.justification;
return *this;
}
TextLayout& TextLayout::operator= (const TextLayout& other)
{
width = other.width;
height = other.height;
justification = other.justification;
lines.clear();
lines.addCopiesOf (other.lines);
return *this;
}
TextLayout::~TextLayout()
{
}
TextLayout::Line& TextLayout::getLine (const int index) const
{
return *lines.getUnchecked (index);
}
void TextLayout::ensureStorageAllocated (int numLinesNeeded)
{
lines.ensureStorageAllocated (numLinesNeeded);
}
void TextLayout::addLine (Line* line)
{
lines.add (line);
}
void TextLayout::draw (Graphics& g, Rectangle<float> area) const
{
auto origin = justification.appliedToRectangle (Rectangle<float> (width, getHeight()), area).getPosition();
auto& context = g.getInternalContext();
for (auto* line : lines)
{
auto lineOrigin = origin + line->lineOrigin;
for (auto* run : line->runs)
{
context.setFont (run->font);
context.setFill (run->colour);
for (auto& glyph : run->glyphs)
context.drawGlyph (glyph.glyphCode, AffineTransform::translation (lineOrigin.x + glyph.anchor.x,
lineOrigin.y + glyph.anchor.y));
if (run->font.isUnderlined())
{
Range<float> runExtent;
for (auto& glyph : run->glyphs)
{
Range<float> glyphRange (glyph.anchor.x, glyph.anchor.x + glyph.width);
runExtent = runExtent.isEmpty() ? glyphRange
: runExtent.getUnionWith (glyphRange);
}
auto lineThickness = run->font.getDescent() * 0.3f;
context.fillRect ({ runExtent.getStart() + lineOrigin.x, lineOrigin.y + lineThickness * 2.0f,
runExtent.getLength(), lineThickness });
}
}
}
}
void TextLayout::createLayout (const AttributedString& text, float maxWidth)
{
createLayout (text, maxWidth, 1.0e7f);
}
void TextLayout::createLayout (const AttributedString& text, float maxWidth, float maxHeight)
{
lines.clear();
width = maxWidth;
height = maxHeight;
justification = text.getJustification();
if (! createNativeLayout (text))
createStandardLayout (text);
recalculateSize();
}
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth)
{
createLayoutWithBalancedLineLengths (text, maxWidth, 1.0e7f);
}
void TextLayout::createLayoutWithBalancedLineLengths (const AttributedString& text, float maxWidth, float maxHeight)
{
auto minimumWidth = maxWidth / 2.0f;
auto bestWidth = maxWidth;
float bestLineProportion = 0.0f;
while (maxWidth > minimumWidth)
{
createLayout (text, maxWidth, maxHeight);
if (getNumLines() < 2)
return;
auto line1 = lines.getUnchecked (lines.size() - 1)->getLineBoundsX().getLength();
auto line2 = lines.getUnchecked (lines.size() - 2)->getLineBoundsX().getLength();
auto shortest = jmin (line1, line2);
auto longest = jmax (line1, line2);
auto prop = shortest > 0 ? longest / shortest : 1.0f;
if (prop > 0.9f && prop < 1.1f)
return;
if (prop > bestLineProportion)
{
bestLineProportion = prop;
bestWidth = maxWidth;
}
maxWidth -= 10.0f;
}
if (bestWidth != maxWidth)
createLayout (text, bestWidth, maxHeight);
}
//==============================================================================
namespace TextLayoutHelpers
{
struct Token
{
Token (const String& t, const Font& f, Colour c, bool whitespace)
: text (t), font (f), colour (c),
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<float> area;
int line;
float lineHeight;
const bool isWhitespace, isNewLine;
Token& operator= (const Token&) = delete;
};
struct TokenList
{
TokenList() noexcept {}
void createLayout (const AttributedString& text, TextLayout& layout)
{
layout.ensureStorageAllocated (totalLines);
addTextRuns (text);
layoutRuns (layout.getWidth(), text.getLineSpacing(), text.getWordWrap());
int charPosition = 0;
int lineStartPosition = 0;
int runStartPosition = 0;
ScopedPointer<TextLayout::Line> currentLine;
ScopedPointer<TextLayout::Run> currentRun;
bool needToSetLineOrigin = true;
for (int i = 0; i < tokens.size(); ++i)
{
auto& t = *tokens.getUnchecked (i);
Array<int> newGlyphs;
Array<float> xOffsets;
t.font.getGlyphPositions (getTrimmedEndIfNotAllWhitespace (t.text), newGlyphs, xOffsets);
if (currentRun == nullptr) currentRun = new TextLayout::Run();
if (currentLine == nullptr) currentLine = new TextLayout::Line();
if (newGlyphs.size() > 0)
{
currentRun->glyphs.ensureStorageAllocated (currentRun->glyphs.size() + newGlyphs.size());
auto tokenOrigin = t.area.getPosition().translated (0, t.font.getAscent());
if (needToSetLineOrigin)
{
needToSetLineOrigin = false;
currentLine->lineOrigin = tokenOrigin;
}
auto glyphOffset = tokenOrigin - currentLine->lineOrigin;
for (int j = 0; j < newGlyphs.size(); ++j)
{
auto 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)
++charPosition;
if (auto* nextToken = tokens[i + 1])
{
if (t.font != nextToken->font || t.colour != nextToken->colour)
{
addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition);
runStartPosition = charPosition;
}
if (t.line != nextToken->line)
{
if (currentRun == nullptr)
currentRun = new TextLayout::Run();
addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition);
currentLine->stringRange = Range<int> (lineStartPosition, charPosition);
if (! needToSetLineOrigin)
layout.addLine (currentLine.release());
runStartPosition = charPosition;
lineStartPosition = charPosition;
needToSetLineOrigin = true;
}
}
else
{
addRun (*currentLine, currentRun.release(), t, runStartPosition, charPosition);
currentLine->stringRange = Range<int> (lineStartPosition, charPosition);
if (! needToSetLineOrigin)
layout.addLine (currentLine.release());
needToSetLineOrigin = true;
}
}
if ((text.getJustification().getFlags() & (Justification::right | Justification::horizontallyCentred)) != 0)
{
auto totalW = layout.getWidth();
bool isCentred = (text.getJustification().getFlags() & Justification::horizontallyCentred) != 0;
for (int i = 0; i < layout.getNumLines(); ++i)
{
auto dx = totalW - layout.getLine(i).getLineBoundsX().getLength();
if (isCentred)
dx /= 2.0f;
layout.getLine(i).lineOrigin.x += dx;
}
}
}
private:
static void addRun (TextLayout::Line& glyphLine, TextLayout::Run* glyphRun,
const Token& t, const int start, const int end)
{
glyphRun->stringRange = { 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);
}
static int getCharacterType (const juce_wchar c) noexcept
{
if (c == '\r' || c == '\n')
return 0;
return CharacterFunctions::isWhitespace (c) ? 2 : 1;
}
void appendText (const String& stringText, const Font& font, Colour colour)
{
auto t = stringText.getCharPointer();
String currentString;
int lastCharType = 0;
for (;;)
{
auto c = t.getAndAdvance();
if (c == 0)
break;
auto charType = getCharacterType (c);
if (charType == 0 || charType != lastCharType)
{
if (currentString.isNotEmpty())
tokens.add (new Token (currentString, font, colour,
lastCharType == 2 || lastCharType == 0));
currentString = String::charToString (c);
if (c == '\r' && *t == '\n')
currentString += t.getAndAdvance();
}
else
{
currentString += c;
}
lastCharType = charType;
}
if (currentString.isNotEmpty())
tokens.add (new Token (currentString, font, colour, lastCharType == 2));
}
void layoutRuns (const float maxWidth, const float extraLineSpacing, const AttributedString::WordWrap wordWrap)
{
float x = 0, y = 0, h = 0;
int i;
for (i = 0; i < tokens.size(); ++i)
{
auto& t = *tokens.getUnchecked(i);
t.area.setPosition (x, y);
t.line = totalLines;
x += t.area.getWidth();
h = jmax (h, t.area.getHeight() + extraLineSpacing);
auto* nextTok = tokens[i + 1];
if (nextTok == nullptr)
break;
const bool tokenTooLarge = (x + nextTok->area.getWidth() > maxWidth);
if (t.isNewLine || ((! nextTok->isWhitespace) && (tokenTooLarge && wordWrap != AttributedString::none)))
{
setLastLineHeight (i + 1, h);
x = 0;
y += h;
h = 0;
++totalLines;
}
}
setLastLineHeight (jmin (i + 1, tokens.size()), h);
++totalLines;
}
void setLastLineHeight (int i, const float height) noexcept
{
while (--i >= 0)
{
auto& tok = *tokens.getUnchecked (i);
if (tok.line == totalLines)
tok.lineHeight = height;
else
break;
}
}
void addTextRuns (const AttributedString& text)
{
auto numAttributes = text.getNumAttributes();
tokens.ensureStorageAllocated (jmax (64, numAttributes));
for (int i = 0; i < numAttributes; ++i)
{
auto& attr = text.getAttribute (i);
appendText (text.getText().substring (attr.range.getStart(), attr.range.getEnd()),
attr.font, attr.colour);
}
}
static String getTrimmedEndIfNotAllWhitespace (const String& s)
{
auto trimmed = s.trimEnd();
if (trimmed.isEmpty() && s.isNotEmpty())
trimmed = s.replaceCharacters ("\r\n\t", " ");
return trimmed;
}
OwnedArray<Token> tokens;
int totalLines = 0;
JUCE_DECLARE_NON_COPYABLE (TokenList)
};
}
//==============================================================================
void TextLayout::createStandardLayout (const AttributedString& text)
{
TextLayoutHelpers::TokenList l;
l.createLayout (text, *this);
}
void TextLayout::recalculateSize()
{
if (! lines.isEmpty())
{
auto bounds = lines.getFirst()->getLineBounds();
for (auto* line : lines)
bounds = bounds.getUnion (line->getLineBounds());
for (auto* line : lines)
line->lineOrigin.x -= bounds.getX();
width = bounds.getWidth();
height = bounds.getHeight();
}
else
{
width = 0;
height = 0;
}
}
} // namespace juce

+ 0
- 195
source/modules/juce_graphics/fonts/juce_TextLayout.h View File

@@ -1,195 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A Pre-formatted piece of text, which may contain multiple fonts and colours.
A TextLayout is created from an AttributedString, and once created can be
quickly drawn into a Graphics context.
@see AttributedString
*/
class JUCE_API TextLayout
{
public:
/** Creates an empty layout.
Having created a TextLayout, you can populate it using createLayout() or
createLayoutWithBalancedLineLengths().
*/
TextLayout();
TextLayout (const TextLayout&);
TextLayout& operator= (const TextLayout&);
TextLayout (TextLayout&&) noexcept;
TextLayout& operator= (TextLayout&&) noexcept;
/** Destructor. */
~TextLayout();
//==============================================================================
/** Creates a layout from the given attributed string.
This will replace any data that is currently stored in the layout.
*/
void createLayout (const AttributedString&, float maxWidth);
/** Creates a layout from the given attributed string, given some size constraints.
This will replace any data that is currently stored in the layout.
*/
void createLayout (const AttributedString&, float maxWidth, float maxHeight);
/** Creates a layout, attempting to choose a width which results in lines
of a similar length.
This will be slower than the normal createLayout method, but produces a
tidier result.
*/
void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth);
/** Creates a layout, attempting to choose a width which results in lines
of a similar length.
This will be slower than the normal createLayout method, but produces a
tidier result.
*/
void createLayoutWithBalancedLineLengths (const AttributedString&, float maxWidth, float maxHeight);
/** Draws the layout within the specified area.
The position of the text within the rectangle is controlled by the justification
flags set in the original AttributedString that was used to create this layout.
*/
void draw (Graphics&, Rectangle<float> area) const;
//==============================================================================
/** A positioned glyph. */
class JUCE_API Glyph
{
public:
Glyph (int glyphCode, Point<float> anchor, float width) noexcept;
Glyph (const Glyph&) noexcept;
Glyph& operator= (const Glyph&) noexcept;
~Glyph() noexcept;
/** The code number of this glyph. */
int glyphCode;
/** The glyph's anchor point - this is relative to the line's origin.
@see TextLayout::Line::lineOrigin
*/
Point<float> anchor;
float width;
private:
JUCE_LEAK_DETECTOR (Glyph)
};
//==============================================================================
/** A sequence of glyphs with a common font and colour. */
class JUCE_API Run
{
public:
Run() noexcept;
Run (const Run&);
Run (Range<int> stringRange, int numGlyphsToPreallocate);
~Run() noexcept;
Font font; /**< The run's font. */
Colour colour; /**< The run's colour. */
Array<Glyph> glyphs; /**< The glyphs in this run. */
Range<int> stringRange; /**< The character range that this run represents in the
original string that was used to create it. */
private:
Run& operator= (const Run&);
JUCE_LEAK_DETECTOR (Run)
};
//==============================================================================
/** A line containing a sequence of glyph-runs. */
class JUCE_API Line
{
public:
Line() noexcept;
Line (const Line&);
Line (Range<int> stringRange, Point<float> lineOrigin,
float ascent, float descent, float leading, int numRunsToPreallocate);
~Line() noexcept;
/** Returns the X position range which contains all the glyphs in this line. */
Range<float> getLineBoundsX() const noexcept;
/** Returns the Y position range which contains all the glyphs in this line. */
Range<float> getLineBoundsY() const noexcept;
/** Returns the smallest rectangle which contains all the glyphs in this line. */
Rectangle<float> getLineBounds() const noexcept;
OwnedArray<Run> runs; /**< The glyph-runs in this line. */
Range<int> stringRange; /**< The character range that this line represents in the
original string that was used to create it. */
Point<float> lineOrigin; /**< The line's baseline origin. */
float ascent, descent, leading;
private:
Line& operator= (const Line&);
JUCE_LEAK_DETECTOR (Line)
};
//==============================================================================
/** Returns the maximum width of the content. */
float getWidth() const noexcept { return width; }
/** Returns the maximum height of the content. */
float getHeight() const noexcept { return height; }
/** Returns the number of lines in the layout. */
int getNumLines() const noexcept { return lines.size(); }
/** Returns one of the lines. */
Line& getLine (int index) const;
/** Adds a line to the layout. The layout will take ownership of this line object
and will delete it when it is no longer needed. */
void addLine (Line*);
/** Pre-allocates space for the specified number of lines. */
void ensureStorageAllocated (int numLinesNeeded);
private:
OwnedArray<Line> lines;
float width, height;
Justification justification;
void createStandardLayout (const AttributedString&);
bool createNativeLayout (const AttributedString&);
void recalculateSize();
JUCE_LEAK_DETECTOR (TextLayout)
};
} // namespace juce

+ 0
- 267
source/modules/juce_graphics/fonts/juce_Typeface.cpp View File

@@ -1,267 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
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)
{
}
Typeface::~Typeface()
{
}
Typeface::Ptr Typeface::getFallbackTypeface()
{
const Font fallbackFont (Font::getFallbackFontName(), Font::getFallbackFontStyle(), 10.0f);
return fallbackFont.getTypeface();
}
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<float> y;
DefaultElementComparator<float> sorter;
for (int i = 0; i < ga.getNumGlyphs(); ++i)
{
Path p;
ga.getGlyph (i).createPath (p);
Rectangle<float> 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);
}
}
} // namespace juce

+ 0
- 161
source/modules/juce_graphics/fonts/juce_Typeface.h View File

@@ -1,161 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
A typeface represents a size-independent font.
This base class is abstract, but calling createSystemTypefaceFor() will return
a platform-specific subclass that can be used.
The CustomTypeface subclass allow you to build your own typeface, and to
load and save it in the Juce typeface format.
Normally you should never need to deal directly with Typeface objects - the Font
class does everything you typically need for rendering text.
@see CustomTypeface, Font
*/
class JUCE_API Typeface : public ReferenceCountedObject
{
public:
//==============================================================================
/** A handy typedef for a pointer to a typeface. */
typedef ReferenceCountedObjectPtr<Typeface> Ptr;
//==============================================================================
/** 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();
/** Returns true if this typeface can be used to render the specified font.
When called, the font will already have been checked to make sure that its name and
style flags match the typeface.
*/
virtual bool isSuitableForFont (const Font&) const { return true; }
/** Returns the ascent of the font, as a proportion of its height.
The height is considered to always be normalised as 1.0, so this will be a
value less that 1.0, indicating the proportion of the font that lies above
its baseline.
*/
virtual float getAscent() const = 0;
/** Returns the descent of the font, as a proportion of its height.
The height is considered to always be normalised as 1.0, so this will be a
value less that 1.0, indicating the proportion of the font that lies below
its baseline.
*/
virtual float getDescent() const = 0;
/** 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 <int>& glyphs, Array<float>& 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, float fontHeight);
/** Returns true if the typeface uses hinting. */
virtual bool isHinted() const { return false; }
//==============================================================================
/** 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, style;
Typeface (const String& name, const String& style) noexcept;
static Ptr getFallbackTypeface();
private:
struct HintingParams;
friend struct ContainerDeletePolicy<HintingParams>;
ScopedPointer<HintingParams> hintingParams;
CriticalSection hintingLock;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Typeface)
};
} // namespace juce

+ 0
- 269
source/modules/juce_graphics/geometry/juce_AffineTransform.cpp View File

@@ -1,269 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
AffineTransform::AffineTransform() noexcept
: mat00 (1.0f), mat01 (0), mat02 (0),
mat10 (0), mat11 (1.0f), mat12 (0)
{
}
AffineTransform::AffineTransform (const AffineTransform& other) noexcept
: mat00 (other.mat00), mat01 (other.mat01), mat02 (other.mat02),
mat10 (other.mat10), mat11 (other.mat11), mat12 (other.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)
{
}
AffineTransform& AffineTransform::operator= (const AffineTransform& other) noexcept
{
mat00 = other.mat00;
mat01 = other.mat01;
mat02 = other.mat02;
mat10 = other.mat10;
mat11 = other.mat11;
mat12 = other.mat12;
return *this;
}
bool AffineTransform::operator== (const AffineTransform& other) const noexcept
{
return mat00 == other.mat00
&& mat01 == other.mat01
&& mat02 == other.mat02
&& mat10 == other.mat10
&& mat11 == other.mat11
&& mat12 == other.mat12;
}
bool AffineTransform::operator!= (const AffineTransform& other) const noexcept
{
return ! operator== (other);
}
//==============================================================================
bool AffineTransform::isIdentity() const noexcept
{
return (mat01 == 0.0f)
&& (mat02 == 0.0f)
&& (mat10 == 0.0f)
&& (mat12 == 0.0f)
&& (mat00 == 1.0f)
&& (mat11 == 1.0f);
}
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
const AffineTransform AffineTransform::identity;
#endif
//==============================================================================
AffineTransform AffineTransform::followedBy (const AffineTransform& other) const noexcept
{
return AffineTransform (other.mat00 * mat00 + other.mat01 * mat10,
other.mat00 * mat01 + other.mat01 * mat11,
other.mat00 * mat02 + other.mat01 * mat12 + other.mat02,
other.mat10 * mat00 + other.mat11 * mat10,
other.mat10 * mat01 + other.mat11 * mat11,
other.mat10 * mat02 + other.mat11 * mat12 + other.mat12);
}
AffineTransform AffineTransform::translated (const float dx, const float dy) const noexcept
{
return AffineTransform (mat00, mat01, mat02 + dx,
mat10, mat11, mat12 + dy);
}
AffineTransform AffineTransform::translation (const float dx, const float dy) noexcept
{
return AffineTransform (1.0f, 0, dx,
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);
const float sinRad = std::sin (rad);
return AffineTransform (cosRad * mat00 + -sinRad * mat10,
cosRad * mat01 + -sinRad * mat11,
cosRad * mat02 + -sinRad * mat12,
sinRad * mat00 + cosRad * mat10,
sinRad * mat01 + cosRad * mat11,
sinRad * mat02 + cosRad * mat12);
}
AffineTransform AffineTransform::rotation (const float rad) noexcept
{
const float cosRad = std::cos (rad);
const float sinRad = std::sin (rad);
return AffineTransform (cosRad, -sinRad, 0,
sinRad, cosRad, 0);
}
AffineTransform AffineTransform::rotation (const float rad, const float pivotX, const float pivotY) noexcept
{
const float cosRad = std::cos (rad);
const float sinRad = std::sin (rad);
return AffineTransform (cosRad, -sinRad, -cosRad * pivotX + sinRad * pivotY + pivotX,
sinRad, cosRad, -sinRad * pivotX + -cosRad * pivotY + pivotY);
}
AffineTransform AffineTransform::rotated (const float angle, const float pivotX, const float pivotY) const noexcept
{
return followedBy (rotation (angle, pivotX, pivotY));
}
AffineTransform AffineTransform::scaled (const float factorX, const float factorY) const noexcept
{
return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02,
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);
}
AffineTransform AffineTransform::scale (const float factor) noexcept
{
return AffineTransform (factor, 0, 0, 0, factor, 0);
}
AffineTransform AffineTransform::scaled (const float factorX, const float factorY,
const float pivotX, const float pivotY) const noexcept
{
return AffineTransform (factorX * mat00, factorX * mat01, factorX * mat02 + pivotX * (1.0f - factorX),
factorY * mat10, factorY * mat11, factorY * mat12 + pivotY * (1.0f - factorY));
}
AffineTransform AffineTransform::scale (const float factorX, const float factorY,
const float pivotX, const float pivotY) noexcept
{
return AffineTransform (factorX, 0, pivotX * (1.0f - factorX),
0, factorY, pivotY * (1.0f - factorY));
}
AffineTransform AffineTransform::shear (float shearX, float shearY) noexcept
{
return AffineTransform (1.0f, shearX, 0,
shearY, 1.0f, 0);
}
AffineTransform AffineTransform::sheared (const float shearX, const float shearY) const noexcept
{
return AffineTransform (mat00 + shearX * mat10,
mat01 + shearX * mat11,
mat02 + shearX * mat12,
mat10 + shearY * mat00,
mat11 + shearY * mat01,
mat12 + shearY * mat02);
}
AffineTransform AffineTransform::verticalFlip (const float height) noexcept
{
return AffineTransform (1.0f, 0, 0, 0, -1.0f, height);
}
AffineTransform AffineTransform::inverted() const noexcept
{
double determinant = (mat00 * mat11 - mat10 * mat01);
if (determinant != 0.0)
{
determinant = 1.0 / determinant;
const float dst00 = (float) ( mat11 * determinant);
const float dst10 = (float) (-mat10 * determinant);
const float dst01 = (float) (-mat01 * determinant);
const float dst11 = (float) ( mat00 * determinant);
return AffineTransform (dst00, dst01, -mat02 * dst00 - mat12 * dst01,
dst10, dst11, -mat02 * dst10 - mat12 * dst11);
}
// singularity..
return *this;
}
bool AffineTransform::isSingularity() const noexcept
{
return (mat00 * mat11 - mat10 * mat01) == 0.0f;
}
AffineTransform AffineTransform::fromTargetPoints (const float x00, const float y00,
const float x10, const float y10,
const float x01, const float y01) noexcept
{
return AffineTransform (x10 - x00, x01 - x00, x00,
y10 - y00, y01 - y00, y00);
}
AffineTransform AffineTransform::fromTargetPoints (const float sx1, const float sy1, const float tx1, const float ty1,
const float sx2, const float sy2, const float tx2, const float ty2,
const float sx3, const float sy3, const float tx3, const float ty3) noexcept
{
return fromTargetPoints (sx1, sy1, sx2, sy2, sx3, sy3)
.inverted()
.followedBy (fromTargetPoints (tx1, ty1, tx2, ty2, tx3, ty3));
}
bool AffineTransform::isOnlyTranslation() const noexcept
{
return (mat01 == 0.0f)
&& (mat10 == 0.0f)
&& (mat00 == 1.0f)
&& (mat11 == 1.0f);
}
float AffineTransform::getScaleFactor() const noexcept
{
return (std::abs (mat00) + std::abs (mat11)) / 2.0f;
}
} // namespace juce

+ 0
- 279
source/modules/juce_graphics/geometry/juce_AffineTransform.h View File

@@ -1,279 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Represents a 2D affine-transformation matrix.
An affine transformation is a transformation such as a rotation, scale, shear,
resize or translation.
These are used for various 2D transformation tasks, e.g. with Path objects.
@see Path, Point, Line
*/
class JUCE_API AffineTransform
{
public:
//==============================================================================
/** Creates an identity transform. */
AffineTransform() noexcept;
/** Creates a copy of another transform. */
AffineTransform (const AffineTransform& other) noexcept;
/** Creates a transform from a set of raw matrix values.
The resulting matrix is:
(mat00 mat01 mat02)
(mat10 mat11 mat12)
( 0 0 1 )
*/
AffineTransform (float mat00, float mat01, float mat02,
float mat10, float mat11, float mat12) noexcept;
/** Copies from another AffineTransform object */
AffineTransform& operator= (const AffineTransform& other) noexcept;
/** Compares two transforms. */
bool operator== (const AffineTransform& other) const noexcept;
/** Compares two transforms. */
bool operator!= (const AffineTransform& other) const noexcept;
#if JUCE_ALLOW_STATIC_NULL_VARIABLES
/** A ready-to-use identity transform.
Note that you should always avoid using a static variable like this, and
prefer AffineTransform() or {} if you need a default-constructed instance.
*/
static const AffineTransform identity;
#endif
//==============================================================================
/** Transforms a 2D coordinate using this matrix. */
template <typename ValueType>
void transformPoint (ValueType& x, ValueType& y) const noexcept
{
const ValueType oldX = x;
x = static_cast<ValueType> (mat00 * oldX + mat01 * y + mat02);
y = static_cast<ValueType> (mat10 * oldX + mat11 * y + mat12);
}
/** 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).
*/
template <typename ValueType>
void transformPoints (ValueType& x1, ValueType& y1,
ValueType& x2, ValueType& y2) const noexcept
{
const ValueType oldX1 = x1, oldX2 = x2;
x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
}
/** 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).
*/
template <typename ValueType>
void transformPoints (ValueType& x1, ValueType& y1,
ValueType& x2, ValueType& y2,
ValueType& x3, ValueType& y3) const noexcept
{
const ValueType oldX1 = x1, oldX2 = x2, oldX3 = x3;
x1 = static_cast<ValueType> (mat00 * oldX1 + mat01 * y1 + mat02);
y1 = static_cast<ValueType> (mat10 * oldX1 + mat11 * y1 + mat12);
x2 = static_cast<ValueType> (mat00 * oldX2 + mat01 * y2 + mat02);
y2 = static_cast<ValueType> (mat10 * oldX2 + mat11 * y2 + mat12);
x3 = static_cast<ValueType> (mat00 * oldX3 + mat01 * y3 + mat02);
y3 = static_cast<ValueType> (mat10 * oldX3 + mat11 * y3 + mat12);
}
//==============================================================================
/** Returns a new transform which is the same as this one followed by a translation. */
AffineTransform translated (float deltaX,
float deltaY) const noexcept;
/** Returns a new transform which is the same as this one followed by a translation. */
template <typename PointType>
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 <typename PointType>
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
the origin (0, 0).
*/
AffineTransform rotated (float angleInRadians) const noexcept;
/** 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 coordinates passed in.
*/
AffineTransform rotated (float angleInRadians,
float pivotX,
float pivotY) const noexcept;
/** Returns a new transform which is a rotation about (0, 0). */
static AffineTransform rotation (float angleInRadians) noexcept;
/** Returns a new transform which is a rotation about a given point. */
static AffineTransform rotation (float angleInRadians,
float pivotX,
float pivotY) 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 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.
*/
AffineTransform scaled (float factorX, float factorY,
float pivotX, float pivotY) const noexcept;
/** Returns a new transform which is a re-scale about the origin. */
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;
/** Returns a transform which is the same as this one followed by a shear.
The shear is centred around the origin (0, 0).
*/
AffineTransform sheared (float shearX, float shearY) const noexcept;
/** Returns a shear transform, centred around the origin (0, 0). */
static AffineTransform shear (float shearX, float shearY) noexcept;
/** 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;
/** Returns a matrix which is the inverse operation of this one.
Some matrices don't have an inverse - in this case, the method will just return
an identity transform.
*/
AffineTransform inverted() const noexcept;
/** Returns the transform that will map three known points onto three coordinates
that are supplied.
This returns the transform that will transform (0, 0) into (x00, y00),
(1, 0) to (x10, y10), and (0, 1) to (x01, y01).
*/
static AffineTransform fromTargetPoints (float x00, float y00,
float x10, float y10,
float x01, float y01) noexcept;
/** Returns the transform that will map three specified points onto three target points. */
static AffineTransform fromTargetPoints (float sourceX1, float sourceY1, float targetX1, float targetY1,
float sourceX2, float sourceY2, float targetX2, float targetY2,
float sourceX3, float sourceY3, float targetX3, float targetY3) noexcept;
//==============================================================================
/** Returns the result of concatenating another transformation after this one. */
AffineTransform followedBy (const AffineTransform& other) const noexcept;
/** Returns true if this transform has no effect on points. */
bool isIdentity() const noexcept;
/** Returns true if this transform maps to a singularity - i.e. if it has no inverse. */
bool isSingularity() const noexcept;
/** Returns true if the transform only translates, and doesn't scale or rotate the
points. */
bool isOnlyTranslation() const noexcept;
/** If this transform is only a translation, this returns the X offset.
@see isOnlyTranslation
*/
float getTranslationX() const noexcept { return mat02; }
/** If this transform is only a translation, this returns the X offset.
@see isOnlyTranslation
*/
float getTranslationY() const noexcept { return mat12; }
/** Returns the approximate scale factor by which lengths will be transformed.
Obviously a length may be scaled by entirely different amounts depending on its
direction, so this is only appropriate as a rough guide.
*/
float getScaleFactor() const noexcept;
//==============================================================================
/* The transform matrix is:
(mat00 mat01 mat02)
(mat10 mat11 mat12)
( 0 0 1 )
*/
float mat00, mat01, mat02;
float mat10, mat11, mat12;
};
} // namespace juce

+ 0
- 153
source/modules/juce_graphics/geometry/juce_BorderSize.h View File

@@ -1,153 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
//==============================================================================
/**
Specifies a set of gaps to be left around the sides of a rectangle.
This is basically the size of the spaces at the top, bottom, left and right of
a rectangle. It's used by various component classes to specify borders.
@see Rectangle
*/
template <typename ValueType>
class BorderSize
{
public:
//==============================================================================
/** Creates a null border.
All sizes are left as 0.
*/
BorderSize() noexcept
: top(), left(), bottom(), right()
{
}
/** Creates a copy of another border. */
BorderSize (const BorderSize& other) noexcept
: top (other.top), left (other.left), bottom (other.bottom), right (other.right)
{
}
/** Creates a border with the given gaps. */
BorderSize (ValueType topGap, ValueType leftGap, ValueType bottomGap, ValueType rightGap) noexcept
: top (topGap), left (leftGap), bottom (bottomGap), right (rightGap)
{
}
/** Creates a border with the given gap on all sides. */
explicit BorderSize (ValueType allGaps) noexcept
: top (allGaps), left (allGaps), bottom (allGaps), right (allGaps)
{
}
//==============================================================================
/** Returns the gap that should be left at the top of the region. */
ValueType getTop() const noexcept { return top; }
/** Returns the gap that should be left at the top of the region. */
ValueType getLeft() const noexcept { return left; }
/** Returns the gap that should be left at the top of the region. */
ValueType getBottom() const noexcept { return bottom; }
/** Returns the gap that should be left at the top of the region. */
ValueType getRight() const noexcept { return right; }
/** Returns the sum of the top and bottom gaps. */
ValueType getTopAndBottom() const noexcept { return top + bottom; }
/** Returns the sum of the left and right gaps. */
ValueType getLeftAndRight() const noexcept { return left + right; }
/** Returns true if this border has no thickness along any edge. */
bool isEmpty() const noexcept { return left + right + top + bottom == ValueType(); }
//==============================================================================
/** Changes the top gap. */
void setTop (ValueType newTopGap) noexcept { top = newTopGap; }
/** Changes the left gap. */
void setLeft (ValueType newLeftGap) noexcept { left = newLeftGap; }
/** Changes the bottom gap. */
void setBottom (ValueType newBottomGap) noexcept { bottom = newBottomGap; }
/** Changes the right gap. */
void setRight (ValueType newRightGap) noexcept { right = newRightGap; }
//==============================================================================
/** Returns a rectangle with these borders removed from it. */
Rectangle<ValueType> subtractedFrom (const Rectangle<ValueType>& original) const noexcept
{
return Rectangle<ValueType> (original.getX() + left,
original.getY() + top,
original.getWidth() - (left + right),
original.getHeight() - (top + bottom));
}
/** Removes this border from a given rectangle. */
void subtractFrom (Rectangle<ValueType>& rectangle) const noexcept
{
rectangle = subtractedFrom (rectangle);
}
/** Returns a rectangle with these borders added around it. */
Rectangle<ValueType> addedTo (const Rectangle<ValueType>& original) const noexcept
{
return Rectangle<ValueType> (original.getX() - left,
original.getY() - top,
original.getWidth() + (left + right),
original.getHeight() + (top + bottom));
}
/** Adds this border around a given rectangle. */
void addTo (Rectangle<ValueType>& rectangle) const noexcept
{
rectangle = addedTo (rectangle);
}
//==============================================================================
bool operator== (const BorderSize& other) const noexcept
{
return top == other.top && left == other.left && bottom == other.bottom && right == other.right;
}
bool operator!= (const BorderSize& other) const noexcept
{
return ! operator== (other);
}
private:
//==============================================================================
ValueType top, left, bottom, right;
};
} // namespace juce

+ 0
- 838
source/modules/juce_graphics/geometry/juce_EdgeTable.cpp View File

@@ -1,838 +0,0 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
const int juce_edgeTableDefaultEdgesPerLine = 32;
//==============================================================================
EdgeTable::EdgeTable (const Rectangle<int>& area,
const Path& path, const AffineTransform& transform)
: bounds (area),
maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1),
needToCheckEmptiness (true)
{
allocate();
int* t = table;
for (int i = bounds.getHeight(); --i >= 0;)
{
*t = 0;
t += lineStrideElements;
}
const int leftLimit = bounds.getX() << 8;
const int topLimit = bounds.getY() << 8;
const int rightLimit = bounds.getRight() << 8;
const int heightLimit = bounds.getHeight() << 8;
PathFlatteningIterator iter (path, transform);
while (iter.next())
{
int y1 = roundToInt (iter.y1 * 256.0f);
int y2 = roundToInt (iter.y2 * 256.0f);
if (y1 != y2)
{
y1 -= topLimit;
y2 -= topLimit;
const int startY = y1;
int direction = -1;
if (y1 > y2)
{
std::swap (y1, y2);
direction = 1;
}
if (y1 < 0)
y1 = 0;
if (y2 > heightLimit)
y2 = heightLimit;
if (y1 < y2)
{
const double startX = 256.0f * iter.x1;
const double multiplier = (iter.x2 - iter.x1) / (iter.y2 - iter.y1);
const int stepSize = jlimit (1, 256, 256 / (1 + (int) std::abs (multiplier)));
do
{
const int step = jmin (stepSize, y2 - y1, 256 - (y1 & 255));
int x = roundToInt (startX + multiplier * ((y1 + (step >> 1)) - startY));
if (x < leftLimit)
x = leftLimit;
else if (x >= rightLimit)
x = rightLimit - 1;
addEdgePoint (x, y1 >> 8, direction * step);
y1 += step;
}
while (y1 < y2);
}
}
}
sanitiseLevels (path.isUsingNonZeroWinding());
}
EdgeTable::EdgeTable (const Rectangle<int>& rectangleToAdd)
: bounds (rectangleToAdd),
maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1),
needToCheckEmptiness (true)
{
allocate();
table[0] = 0;
const int x1 = rectangleToAdd.getX() << 8;
const int x2 = rectangleToAdd.getRight() << 8;
int* t = table;
for (int i = rectangleToAdd.getHeight(); --i >= 0;)
{
t[0] = 2;
t[1] = x1;
t[2] = 255;
t[3] = x2;
t[4] = 0;
t += lineStrideElements;
}
}
EdgeTable::EdgeTable (const RectangleList<int>& rectanglesToAdd)
: bounds (rectanglesToAdd.getBounds()),
maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
lineStrideElements (juce_edgeTableDefaultEdgesPerLine * 2 + 1),
needToCheckEmptiness (true)
{
allocate();
clearLineSizes();
for (auto& r : rectanglesToAdd)
{
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<float>& rectanglesToAdd)
: bounds (rectanglesToAdd.getBounds().getSmallestIntegerContainer()),
maxEdgesPerLine (rectanglesToAdd.getNumRectangles() * 2),
lineStrideElements (rectanglesToAdd.getNumRectangles() * 4 + 1),
needToCheckEmptiness (true)
{
bounds.setHeight (bounds.getHeight() + 1);
allocate();
clearLineSizes();
for (auto& r : rectanglesToAdd)
{
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)
{
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);
}
}
sanitiseLevels (true);
}
EdgeTable::EdgeTable (const Rectangle<float>& rectangleToAdd)
: bounds (Rectangle<int> ((int) std::floor (rectangleToAdd.getX()),
roundToInt (rectangleToAdd.getY() * 256.0f) >> 8,
2 + (int) rectangleToAdd.getWidth(),
2 + (int) rectangleToAdd.getHeight())),
maxEdgesPerLine (juce_edgeTableDefaultEdgesPerLine),
lineStrideElements ((juce_edgeTableDefaultEdgesPerLine << 1) + 1),
needToCheckEmptiness (true)
{
jassert (! rectangleToAdd.isEmpty());
allocate();
table[0] = 0;
const int x1 = roundToInt (rectangleToAdd.getX() * 256.0f);
const int x2 = roundToInt (rectangleToAdd.getRight() * 256.0f);
int y1 = roundToInt (rectangleToAdd.getY() * 256.0f) - (bounds.getY() << 8);
jassert (y1 < 256);
int y2 = roundToInt (rectangleToAdd.getBottom() * 256.0f) - (bounds.getY() << 8);
if (x2 <= x1 || y2 <= y1)
{
bounds.setHeight (0);
return;
}
int lineY = 0;
int* t = table;
if ((y1 >> 8) == (y2 >> 8))
{
t[0] = 2;
t[1] = x1;
t[2] = y2 - y1;
t[3] = x2;
t[4] = 0;
++lineY;
t += lineStrideElements;
}
else
{
t[0] = 2;
t[1] = x1;
t[2] = 255 - (y1 & 255);
t[3] = x2;
t[4] = 0;
++lineY;
t += lineStrideElements;
while (lineY < (y2 >> 8))
{
t[0] = 2;
t[1] = x1;
t[2] = 255;
t[3] = x2;
t[4] = 0;
++lineY;
t += lineStrideElements;
}
jassert (lineY < bounds.getHeight());
t[0] = 2;
t[1] = x1;
t[2] = y2 & 255;
t[3] = x2;
t[4] = 0;
++lineY;
t += lineStrideElements;
}
while (lineY < bounds.getHeight())
{
t[0] = 0;
t += lineStrideElements;
++lineY;
}
}
EdgeTable::EdgeTable (const EdgeTable& other)
{
operator= (other);
}
EdgeTable& EdgeTable::operator= (const EdgeTable& other)
{
bounds = other.bounds;
maxEdgesPerLine = other.maxEdgesPerLine;
lineStrideElements = other.lineStrideElements;
needToCheckEmptiness = other.needToCheckEmptiness;
allocate();
copyEdgeTableData (table, lineStrideElements, other.table, lineStrideElements, bounds.getHeight());
return *this;
}
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, (size_t) (src[0] * 2 + 1) * sizeof (int));
src += srcLineStride;
dest += destLineStride;
}
}
void EdgeTable::sanitiseLevels (const bool useNonZeroWinding) noexcept
{
// Convert the table from relative windings to absolute levels..
int* lineStart = table;
for (int y = bounds.getHeight(); --y >= 0;)
{
int num = lineStart[0];
if (num > 0)
{
LineItem* items = reinterpret_cast<LineItem*> (lineStart + 1);
LineItem* const itemsEnd = items + num;
// sort the X coords
std::sort (items, itemsEnd);
const LineItem* src = items;
int correctedNum = num;
int level = 0;
while (src < itemsEnd)
{
level += src->level;
const int x = src->x;
++src;
while (src < itemsEnd && src->x == x)
{
level += src->level;
++src;
--correctedNum;
}
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
}
lineStart += lineStrideElements;
}
}
void EdgeTable::remapTableForNumEdges (const int newNumEdgesPerLine)
{
if (newNumEdgesPerLine != maxEdgesPerLine)
{
maxEdgesPerLine = newNumEdgesPerLine;
jassert (bounds.getHeight() > 0);
const int newLineStrideElements = maxEdgesPerLine * 2 + 1;
HeapBlock<int> newTable (getEdgeTableAllocationSize (newLineStrideElements, bounds.getHeight()));
copyEdgeTableData (newTable, newLineStrideElements, table, lineStrideElements, bounds.getHeight());
table.swapWith (newTable);
lineStrideElements = newLineStrideElements;
}
}
void EdgeTable::optimiseTable()
{
int maxLineElements = 0;
for (int i = bounds.getHeight(); --i >= 0;)
maxLineElements = jmax (maxLineElements, table [i * lineStrideElements]);
remapTableForNumEdges (maxLineElements);
}
void EdgeTable::addEdgePoint (const int x, const int y, const int winding)
{
jassert (y >= 0 && y < bounds.getHeight());
int* line = table + lineStrideElements * y;
const int numPoints = line[0];
if (numPoints >= maxEdgesPerLine)
{
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;
}
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
{
bounds.translate ((int) std::floor (dx), dy);
int* lineStart = table;
const int intDx = (int) (dx * 256.0f);
for (int i = bounds.getHeight(); --i >= 0;)
{
int* line = lineStart;
lineStart += lineStrideElements;
int num = *line++;
while (--num >= 0)
{
*line += intDx;
line += 2;
}
}
}
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<LineItem*> (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* srcLine = table + lineStrideElements * y;
int srcNum1 = *srcLine;
if (srcNum1 == 0)
return;
int srcNum2 = *otherLine;
if (srcNum2 == 0)
{
*srcLine = 0;
return;
}
const int right = bounds.getRight() << 8;
// 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 (srcNum2 == 2 && otherLine[2] >= 255)
{
clipEdgeTableLineToRange (srcLine, otherLine[1], jmin (right, otherLine[3]));
return;
}
bool isUsingTempSpace = false;
const int* src1 = srcLine + 1;
int x1 = *src1++;
const int* src2 = otherLine + 1;
int x2 = *src2++;
int destIndex = 0, destTotal = 0;
int level1 = 0, level2 = 0;
int lastX = std::numeric_limits<int>::min(), lastLevel = 0;
while (srcNum1 > 0 && srcNum2 > 0)
{
int nextX;
if (x1 <= x2)
{
if (x1 == x2)
{
level2 = *src2++;
x2 = *src2++;
--srcNum2;
}
nextX = x1;
level1 = *src1++;
x1 = *src1++;
--srcNum1;
}
else
{
nextX = x2;
level2 = *src2++;
x2 = *src2++;
--srcNum2;
}
if (nextX > lastX)
{
if (nextX >= right)
break;
lastX = nextX;
const int nextLevel = (level1 * (level2 + 1)) >> 8;
jassert (isPositiveAndBelow (nextLevel, 256));
if (nextLevel != lastLevel)
{
if (destTotal >= maxEdgesPerLine)
{
srcLine[0] = destTotal;
if (isUsingTempSpace)
{
const size_t tempSize = (size_t) srcNum1 * 2 * sizeof (int);
int* const oldTemp = static_cast<int*> (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;
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;
}
}
}
if (lastLevel > 0)
{
if (destTotal >= maxEdgesPerLine)
{
srcLine[0] = destTotal;
remapTableForNumEdges (jmax (256, destTotal * 2));
srcLine = table + lineStrideElements * y;
}
++destTotal;
srcLine[++destIndex] = right;
srcLine[++destIndex] = 0;
}
srcLine[0] = destTotal;
}
void EdgeTable::clipEdgeTableLineToRange (int* dest, const int x1, const int x2) noexcept
{
int* lastItem = dest + (dest[0] * 2 - 1);
if (x2 < lastItem[0])
{
if (x2 <= dest[1])
{
dest[0] = 0;
return;
}
while (x2 < lastItem[-2])
{
--(dest[0]);
lastItem -= 2;
}
lastItem[0] = x2;
lastItem[1] = 0;
}
if (x1 > dest[1])
{
while (lastItem[0] > x1)
lastItem -= 2;
const int itemsRemoved = (int) (lastItem - (dest + 1)) / 2;
if (itemsRemoved > 0)
{
dest[0] -= itemsRemoved;
memmove (dest + 1, lastItem, (size_t) dest[0] * (sizeof (int) * 2));
}
dest[1] = x1;
}
}
//==============================================================================
void EdgeTable::clipToRectangle (const Rectangle<int>& r)
{
const Rectangle<int> clipped (r.getIntersection (bounds));
if (clipped.isEmpty())
{
needToCheckEmptiness = false;
bounds.setHeight (0);
}
else
{
const int top = clipped.getY() - bounds.getY();
const int bottom = clipped.getBottom() - bounds.getY();
if (bottom < bounds.getHeight())
bounds.setHeight (bottom);
for (int i = top; --i >= 0;)
table [lineStrideElements * i] = 0;
if (clipped.getX() > bounds.getX() || clipped.getRight() < bounds.getRight())
{
const int x1 = clipped.getX() << 8;
const int x2 = jmin (bounds.getRight(), clipped.getRight()) << 8;
int* line = table + lineStrideElements * top;
for (int i = bottom - top; --i >= 0;)
{
if (line[0] != 0)
clipEdgeTableLineToRange (line, x1, x2);
line += lineStrideElements;
}
}
needToCheckEmptiness = true;
}
}
void EdgeTable::excludeRectangle (const Rectangle<int>& r)
{
const Rectangle<int> clipped (r.getIntersection (bounds));
if (! clipped.isEmpty())
{
const int top = clipped.getY() - bounds.getY();
const int bottom = clipped.getBottom() - bounds.getY();
const int rectLine[] = { 4, std::numeric_limits<int>::min(), 255,
clipped.getX() << 8, 0,
clipped.getRight() << 8, 255,
std::numeric_limits<int>::max(), 0 };
for (int i = top; i < bottom; ++i)
intersectWithEdgeTableLine (i, rectLine);
needToCheckEmptiness = true;
}
}
void EdgeTable::clipToEdgeTable (const EdgeTable& other)
{
const Rectangle<int> clipped (other.bounds.getIntersection (bounds));
if (clipped.isEmpty())
{
needToCheckEmptiness = false;
bounds.setHeight (0);
}
else
{
const int top = clipped.getY() - bounds.getY();
const int bottom = clipped.getBottom() - bounds.getY();
if (bottom < bounds.getHeight())
bounds.setHeight (bottom);
if (clipped.getRight() < bounds.getRight())
bounds.setRight (clipped.getRight());
for (int i = 0; i < top; ++i)
table [lineStrideElements * i] = 0;
const int* otherLine = other.table + other.lineStrideElements * (clipped.getY() - other.bounds.getY());
for (int i = top; i < bottom; ++i)
{
intersectWithEdgeTableLine (i, otherLine);
otherLine += other.lineStrideElements;
}
needToCheckEmptiness = true;
}
}
void EdgeTable::clipLineToMask (int x, int y, const uint8* mask, int maskStride, int numPixels)
{
y -= bounds.getY();
if (y < 0 || y >= bounds.getHeight())
return;
needToCheckEmptiness = true;
if (numPixels <= 0)
{
table [lineStrideElements * y] = 0;
return;
}
int* tempLine = static_cast<int*> (alloca ((size_t) (numPixels * 2 + 4) * sizeof (int)));
int destIndex = 0, lastLevel = 0;
while (--numPixels >= 0)
{
const int alpha = *mask;
mask += maskStride;
if (alpha != lastLevel)
{
tempLine[++destIndex] = (x << 8);
tempLine[++destIndex] = alpha;
lastLevel = alpha;
}
++x;
}
if (lastLevel > 0)
{
tempLine[++destIndex] = (x << 8);
tempLine[++destIndex] = 0;
}
tempLine[0] = destIndex >> 1;
intersectWithEdgeTableLine (y, tempLine);
}
bool EdgeTable::isEmpty() noexcept
{
if (needToCheckEmptiness)
{
needToCheckEmptiness = false;
int* t = table;
for (int i = bounds.getHeight(); --i >= 0;)
{
if (t[0] > 1)
return false;
t += lineStrideElements;
}
bounds.setHeight (0);
}
return bounds.getHeight() == 0;
}
} // namespace juce

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save