Browse Source

oops - forgot to add a couple of new files to the last check-in..

tags/2021-05-28
Julian Storer 15 years ago
parent
commit
3c32eda726
2 changed files with 1132 additions and 0 deletions
  1. +619
    -0
      src/native/linux/juce_linux_JackAudio.cpp
  2. +513
    -0
      src/native/mac/juce_mac_CoreGraphicsContext.mm

+ 619
- 0
src/native/linux/juce_linux_JackAudio.cpp View File

@@ -0,0 +1,619 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-9 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
// (This file gets included by juce_linux_NativeCode.cpp, rather than being
// compiled on its own).
#ifdef JUCE_INCLUDED_FILE
#if JUCE_JACK
//==============================================================================
static void* juce_libjack_handle = 0;
void* juce_load_jack_function (const char* const name)
{
if (juce_libjack_handle == 0)
return 0;
return dlsym (juce_libjack_handle, name);
}
//==============================================================================
#define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \
typedef return_type (*fn_name##_ptr_t)argument_types; \
return_type fn_name argument_types { \
static fn_name##_ptr_t fn = 0; \
if (fn == 0) { fn = (fn_name##_ptr_t)juce_load_jack_function(#fn_name); } \
if (fn) return (*fn)arguments; \
else return 0; \
}
#define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \
typedef void (*fn_name##_ptr_t)argument_types; \
void fn_name argument_types { \
static fn_name##_ptr_t fn = 0; \
if (fn == 0) { fn = (fn_name##_ptr_t)juce_load_jack_function(#fn_name); } \
if (fn) (*fn)arguments; \
}
//==============================================================================
JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status), (client_name, options, status));
JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client));
JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client));
JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client));
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client));
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client));
JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg));
JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes));
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port));
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size));
JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func));
JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg));
JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags));
JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port));
JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port));
JUCE_DECL_JACK_FUNCTION (int, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg));
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id));
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port));
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name));
#if JUCE_DEBUG
#define JACK_LOGGING_ENABLED 1
#endif
#if JACK_LOGGING_ENABLED
static void jack_Log (const String& s)
{
puts (s);
}
static void dumpJackErrorMessage (const jack_status_t status) throw()
{
if (status & JackServerFailed || status & JackServerError)
jack_Log ("Unable to connect to JACK server");
if (status & JackVersionError)
jack_Log ("Client's protocol version does not match");
if (status & JackInvalidOption)
jack_Log ("The operation contained an invalid or unsupported option");
if (status & JackNameNotUnique)
jack_Log ("The desired client name was not unique");
if (status & JackNoSuchClient)
jack_Log ("Requested client does not exist");
if (status & JackInitFailure)
jack_Log ("Unable to initialize client");
}
#else
#define dumpJackErrorMessage(a) {}
#define jack_Log(...) {}
#endif
//==============================================================================
#ifndef JUCE_JACK_CLIENT_NAME
#define JUCE_JACK_CLIENT_NAME "JuceJack"
#endif
//==============================================================================
class JackAudioIODevice : public AudioIODevice
{
public:
JackAudioIODevice (const String& deviceName,
const String& inputId_,
const String& outputId_)
: AudioIODevice (deviceName, T("JACK")),
inputId (inputId_),
outputId (outputId_),
isOpen_ (false),
callback (0),
inChans (0),
outChans (0),
totalNumberOfInputChannels (0),
totalNumberOfOutputChannels (0)
{
jassert (deviceName.isNotEmpty());
jack_status_t status;
client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status);
if (client == 0)
{
dumpJackErrorMessage (status);
}
else
{
juce::jack_set_error_function (errorCallback);
// open input ports
const StringArray inputChannels (getInputChannelNames());
for (int i = 0; i < inputChannels.size(); i++)
{
String inputName;
inputName << "in_" << (++totalNumberOfInputChannels);
inputPorts.add (juce::jack_port_register (client, (const char*) inputName,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0));
}
// open output ports
const StringArray outputChannels (getOutputChannelNames());
for (int i = 0; i < outputChannels.size (); i++)
{
String outputName;
outputName << "out_" << (++totalNumberOfOutputChannels);
outputPorts.add (juce::jack_port_register (client, (const char*) outputName,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0));
}
inChans = (float**) juce_calloc (sizeof (float*) * (totalNumberOfInputChannels + 2));
outChans = (float**) juce_calloc (sizeof (float*) * (totalNumberOfOutputChannels + 2));
}
}
~JackAudioIODevice()
{
close();
if (client != 0)
{
juce::jack_client_close (client);
client = 0;
}
juce_free (inChans);
juce_free (outChans);
}
const StringArray getChannelNames (bool forInput) const
{
StringArray names;
const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */
forInput ? JackPortIsInput : JackPortIsOutput);
if (ports != 0)
{
int j = 0;
while (ports[j] != 0)
{
const String portName (ports [j++]);
if (portName.upToFirstOccurrenceOf (T(":"), false, false) == getName())
names.add (portName.fromFirstOccurrenceOf (T(":"), false, false));
}
free (ports);
}
return names;
}
const StringArray getOutputChannelNames() { return getChannelNames (false); }
const StringArray getInputChannelNames() { return getChannelNames (true); }
int getNumSampleRates() { return client != 0 ? 1 : 0; }
double getSampleRate (int index) { return client != 0 ? juce::jack_get_sample_rate (client) : 0; }
int getNumBufferSizesAvailable() { return client != 0 ? 1 : 0; }
int getBufferSizeSamples (int index) { return getDefaultBufferSize(); }
int getDefaultBufferSize() { return client != 0 ? juce::jack_get_buffer_size (client) : 0; }
const String open (const BitArray& inputChannels, const BitArray& outputChannels,
double sampleRate, int bufferSizeSamples)
{
if (client == 0)
{
lastError = T("No JACK client running");
return lastError;
}
lastError = String::empty;
close();
juce::jack_set_process_callback (client, processCallback, this);
juce::jack_on_shutdown (client, shutdownCallback, this);
juce::jack_activate (client);
isOpen_ = true;
if (! inputChannels.isEmpty())
{
const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsOutput);
if (ports != 0)
{
const int numInputChannels = inputChannels.getHighestBit () + 1;
for (int i = 0; i < numInputChannels; ++i)
{
const String portName (ports[i]);
if (inputChannels[i] && portName.upToFirstOccurrenceOf (T(":"), false, false) == getName())
{
int error = juce::jack_connect (client, ports[i], juce::jack_port_name ((jack_port_t*) inputPorts[i]));
if (error != 0)
jack_Log ("Cannot connect input port " + String (i) + " (" + String (ports[i]) + "), error " + String (error));
}
}
free (ports);
}
}
if (! outputChannels.isEmpty())
{
const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsInput);
if (ports != 0)
{
const int numOutputChannels = outputChannels.getHighestBit () + 1;
for (int i = 0; i < numOutputChannels; ++i)
{
const String portName (ports[i]);
if (outputChannels[i] && portName.upToFirstOccurrenceOf (T(":"), false, false) == getName())
{
int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i]), ports[i]);
if (error != 0)
jack_Log ("Cannot connect output port " + String (i) + " (" + String (ports[i]) + "), error " + String (error));
}
}
free (ports);
}
}
return lastError;
}
void close()
{
stop();
if (client != 0)
{
juce::jack_deactivate (client);
juce::jack_set_process_callback (client, processCallback, 0);
juce::jack_on_shutdown (client, shutdownCallback, 0);
}
isOpen_ = false;
}
void start (AudioIODeviceCallback* newCallback)
{
if (isOpen_ && newCallback != callback)
{
if (newCallback != 0)
newCallback->audioDeviceAboutToStart (this);
AudioIODeviceCallback* const oldCallback = callback;
{
const ScopedLock sl (callbackLock);
callback = newCallback;
}
if (oldCallback != 0)
oldCallback->audioDeviceStopped();
}
}
void stop()
{
start (0);
}
bool isOpen() { return isOpen_; }
bool isPlaying() { return callback != 0; }
int getCurrentBufferSizeSamples() { return getBufferSizeSamples (0); }
double getCurrentSampleRate() { return getSampleRate (0); }
int getCurrentBitDepth() { return 32; }
const String getLastError() { return lastError; }
const BitArray getActiveOutputChannels() const
{
BitArray outputBits;
for (int i = 0; i < outputPorts.size(); i++)
if (juce::jack_port_connected ((jack_port_t*) outputPorts [i]))
outputBits.setBit (i);
return outputBits;
}
const BitArray getActiveInputChannels() const
{
BitArray inputBits;
for (int i = 0; i < inputPorts.size(); i++)
if (juce::jack_port_connected ((jack_port_t*) inputPorts [i]))
inputBits.setBit (i);
return inputBits;
}
int getOutputLatencyInSamples()
{
int latency = 0;
for (int i = 0; i < outputPorts.size(); i++)
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i]));
return latency;
}
int getInputLatencyInSamples()
{
int latency = 0;
for (int i = 0; i < inputPorts.size(); i++)
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i]));
return latency;
}
String inputId, outputId;
private:
void process (const int numSamples)
{
int i, numActiveInChans = 0, numActiveOutChans = 0;
for (i = 0; i < totalNumberOfInputChannels; ++i)
{
jack_default_audio_sample_t* in
= (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples);
if (in != 0)
inChans [numActiveInChans++] = (float*) in;
}
for (i = 0; i < totalNumberOfOutputChannels; ++i)
{
jack_default_audio_sample_t* out
= (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples);
if (out != 0)
outChans [numActiveOutChans++] = (float*) out;
}
const ScopedLock sl (callbackLock);
if (callback != 0)
{
callback->audioDeviceIOCallback ((const float**) inChans, numActiveInChans,
outChans, numActiveOutChans, numSamples);
}
else
{
for (i = 0; i < numActiveOutChans; ++i)
zeromem (outChans[i], sizeof (float) * numSamples);
}
}
static int processCallback (jack_nframes_t nframes, void* callbackArgument)
{
if (callbackArgument != 0)
((JackAudioIODevice*) callbackArgument)->process (nframes);
return 0;
}
static void threadInitCallback (void* callbackArgument)
{
jack_Log ("JackAudioIODevice::initialise");
}
static void shutdownCallback (void* callbackArgument)
{
jack_Log ("JackAudioIODevice::shutdown");
JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument;
if (device != 0)
{
device->client = 0;
device->close();
}
}
static void errorCallback (const char* msg)
{
jack_Log ("JackAudioIODevice::errorCallback " + String (msg));
}
bool isOpen_;
jack_client_t* client;
String lastError;
AudioIODeviceCallback* callback;
CriticalSection callbackLock;
float** inChans;
float** outChans;
int totalNumberOfInputChannels;
int totalNumberOfOutputChannels;
VoidArray inputPorts, outputPorts;
};
//==============================================================================
class JackAudioIODeviceType : public AudioIODeviceType
{
public:
//==============================================================================
JackAudioIODeviceType()
: AudioIODeviceType (T("JACK")),
hasScanned (false)
{
}
~JackAudioIODeviceType()
{
}
//==============================================================================
void scanForDevices()
{
hasScanned = true;
inputNames.clear();
inputIds.clear();
outputNames.clear();
outputIds.clear();
if (juce_libjack_handle == 0)
{
juce_libjack_handle = dlopen ("libjack.so", RTLD_LAZY);
if (juce_libjack_handle == 0)
return;
}
// open a dummy client
jack_status_t status;
jack_client_t* client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status);
if (client == 0)
{
dumpJackErrorMessage (status);
}
else
{
// scan for output devices
const char** ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsOutput);
if (ports != 0)
{
int j = 0;
while (ports[j] != 0)
{
String clientName (ports[j]);
clientName = clientName.upToFirstOccurrenceOf (T(":"), false, false);
if (clientName != String (JUCE_JACK_CLIENT_NAME)
&& ! inputNames.contains (clientName))
{
inputNames.add (clientName);
inputIds.add (ports [j]);
}
++j;
}
free (ports);
}
// scan for input devices
ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsInput);
if (ports != 0)
{
int j = 0;
while (ports[j] != 0)
{
String clientName (ports[j]);
clientName = clientName.upToFirstOccurrenceOf (T(":"), false, false);
if (clientName != String (JUCE_JACK_CLIENT_NAME)
&& ! outputNames.contains (clientName))
{
outputNames.add (clientName);
outputIds.add (ports [j]);
}
++j;
}
free (ports);
}
juce::jack_client_close (client);
}
}
const StringArray getDeviceNames (const bool wantInputNames) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return wantInputNames ? inputNames : outputNames;
}
int getDefaultDeviceIndex (const bool forInput) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
return 0;
}
bool hasSeparateInputsAndOutputs() const { return true; }
int getIndexOfDevice (AudioIODevice* device, const bool asInput) const
{
jassert (hasScanned); // need to call scanForDevices() before doing this
JackAudioIODevice* const d = dynamic_cast <JackAudioIODevice*> (device);
if (d == 0)
return -1;
return asInput ? inputIds.indexOf (d->inputId)
: outputIds.indexOf (d->outputId);
}
AudioIODevice* createDevice (const String& outputDeviceName,
const String& inputDeviceName)
{
jassert (hasScanned); // need to call scanForDevices() before doing this
const int inputIndex = inputNames.indexOf (inputDeviceName);
const int outputIndex = outputNames.indexOf (outputDeviceName);
if (inputIndex >= 0 || outputIndex >= 0)
return new JackAudioIODevice (outputIndex >= 0 ? outputDeviceName
: inputDeviceName,
inputIds [inputIndex],
outputIds [outputIndex]);
return 0;
}
//==============================================================================
juce_UseDebuggingNewOperator
private:
StringArray inputNames, outputNames, inputIds, outputIds;
bool hasScanned;
JackAudioIODeviceType (const JackAudioIODeviceType&);
const JackAudioIODeviceType& operator= (const JackAudioIODeviceType&);
};
//==============================================================================
AudioIODeviceType* juce_createAudioIODeviceType_JACK()
{
return new JackAudioIODeviceType();
}
//==============================================================================
#else // if JACK is turned off..
AudioIODeviceType* juce_createAudioIODeviceType_JACK() { return 0; }
#endif
#endif

+ 513
- 0
src/native/mac/juce_mac_CoreGraphicsContext.mm View File

@@ -0,0 +1,513 @@
/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-9 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online at www.gnu.org/licenses.
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
// (This file gets included by juce_mac_NativeCode.mm, rather than being
// compiled on its own).
#if JUCE_INCLUDED_FILE
//==============================================================================
class CoreGraphicsContext : public LowLevelGraphicsContext
{
public:
CoreGraphicsContext (CGContextRef context_, const float flipHeight_)
: context (context_),
flipHeight (flipHeight_)
{
CGContextRetain (context);
}
~CoreGraphicsContext()
{
CGContextRelease (context);
}
//==============================================================================
bool isVectorDevice() const { return false; }
void setOrigin (int x, int y)
{
CGContextTranslateCTM (context, x, -y);
}
bool reduceClipRegion (int x, int y, int w, int h)
{
CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h));
return ! isClipEmpty();
}
bool reduceClipRegion (const RectangleList& clipRegion)
{
const int numRects = clipRegion.getNumRectangles();
CGRect* const rects = new CGRect [numRects];
for (int i = 0; i < numRects; ++i)
{
const Rectangle& r = clipRegion.getRectangle(i);
rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight());
}
CGContextClipToRects (context, rects, numRects);
delete[] rects;
return ! isClipEmpty();
}
void excludeClipRegion (int x, int y, int w, int h)
{
RectangleList r (getClipBounds());
r.subtract (Rectangle (x, y, w, h));
reduceClipRegion (r);
}
void saveState()
{
CGContextSaveGState (context);
}
void restoreState()
{
CGContextRestoreGState (context);
}
bool clipRegionIntersects (int x, int y, int w, int h)
{
return getClipBounds().intersects (Rectangle (x, y, w, h));
}
const Rectangle getClipBounds() const
{
CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context));
return Rectangle (roundFloatToInt (bounds.origin.x),
roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)),
roundFloatToInt (bounds.size.width),
roundFloatToInt (bounds.size.height));
}
bool isClipEmpty() const
{
return CGRectIsEmpty (CGContextGetClipBoundingBox (context));
}
//==============================================================================
void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents)
{
CGContextSetBlendMode (context, replaceExistingContents ? kCGBlendModeCopy : kCGBlendModeNormal);
CGContextSetAlpha (context, 1.0f);
setColour (colour);
CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h));
}
void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient)
{
CGContextSaveGState (context);
CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h));
flip();
drawGradient (gradient);
CGContextRestoreGState (context);
}
void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel /*quality*/)
{
CGContextSetAlpha (context, 1.0f);
CGContextSaveGState (context);
flip();
applyTransform (transform);
createPath (path);
setColour (colour);
CGContextSetBlendMode (context, kCGBlendModeNormal);
CGContextFillPath (context);
CGContextRestoreGState (context);
}
void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality)
{
CGContextSaveGState (context);
createPath (path, transform);
CGContextClip (context);
flip();
applyTransform (gradient.transform);
drawGradient (gradient);
CGContextRestoreGState (context);
}
void fillPathWithImage (const Path& path, const AffineTransform& transform,
const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel /*quality*/)
{
CGContextSaveGState (context);
createPath (path, transform);
CGContextClip (context);
blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0, alpha);
CGContextRestoreGState (context);
}
void fillAlphaChannelWithColour (const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour)
{
Image* singleChannelImage = createAlphaChannelImage (alphaImage);
CGImageRef image = createImage (*singleChannelImage, true);
CGContextSetAlpha (context, 1.0f);
CGContextSaveGState (context);
CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()),
alphaImage.getWidth(), alphaImage.getHeight());
CGContextClipToMask (context, r, image);
setColour (colour);
CGContextSetBlendMode (context, kCGBlendModeNormal);
CGContextFillRect (context, r);
CGContextRestoreGState (context);
CGImageRelease (image);
deleteAlphaChannelImage (alphaImage, singleChannelImage);
}
void fillAlphaChannelWithGradient (const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient)
{
Image* singleChannelImage = createAlphaChannelImage (alphaImage);
CGImageRef image = createImage (*singleChannelImage, true);
CGContextSaveGState (context);
CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()),
alphaImage.getWidth(), alphaImage.getHeight());
CGContextClipToMask (context, r, image);
flip();
drawGradient (gradient);
CGContextRestoreGState (context);
CGImageRelease (image);
deleteAlphaChannelImage (alphaImage, singleChannelImage);
}
void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY,
const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha)
{
Image* singleChannelImage = createAlphaChannelImage (alphaImage);
CGImageRef image = createImage (*singleChannelImage, true);
CGContextSaveGState (context);
CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()),
alphaImage.getWidth(), alphaImage.getHeight());
CGContextClipToMask (context, r, image);
blendImage (fillerImage, fillerImageX, fillerImageY,
fillerImage.getWidth(), fillerImage.getHeight(),
0, 0, alpha);
CGContextRestoreGState (context);
CGImageRelease (image);
deleteAlphaChannelImage (alphaImage, singleChannelImage);
}
//==============================================================================
void blendImage (const Image& sourceImage,
int destX, int destY, int destW, int destH, int sourceX, int sourceY,
float alpha)
{
CGContextSetBlendMode (context, kCGBlendModeNormal);
CGImageRef image = createImage (sourceImage, false);
CGContextSaveGState (context);
CGContextClipToRect (context, CGRectMake (destX, flipHeight - (destY + destH), destW, destH));
CGContextSetAlpha (context, alpha);
CGContextDrawImage (context, CGRectMake (destX - sourceX,
flipHeight - ((destY - sourceY) + sourceImage.getHeight()),
sourceImage.getWidth(),
sourceImage.getHeight()), image);
CGContextRestoreGState (context);
CGImageRelease (image);
}
void blendImageRescaling (const Image& sourceImage,
int dx, int dy, int dw, int dh,
int sx, int sy, int sw, int sh,
float alpha, const Graphics::ResamplingQuality quality)
{
if (sw > 0 && sh > 0)
{
if (sw == dw && sh == dh)
{
blendImage (sourceImage,
dx, dy, dw, dh,
sx, sy, alpha);
}
else
{
blendImageWarping (sourceImage,
sx, sy, sw, sh,
AffineTransform::translation ((float) -sx,
(float) -sy)
.scaled (dw / (float) sw,
dh / (float) sh)
.translated ((float) dx,
(float) dy),
alpha,
quality);
}
}
}
void blendImageWarping (const Image& sourceImage,
int srcClipX, int srcClipY, int srcClipW, int srcClipH,
const AffineTransform& transform,
float alpha, const Graphics::ResamplingQuality quality)
{
CGContextSetBlendMode (context, kCGBlendModeNormal);
CGImageRef fullImage = createImage (sourceImage, false);
CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH),
srcClipW, srcClipH));
CGImageRelease (fullImage);
CGContextSaveGState (context);
CGContextSetAlpha (context, alpha);
flip();
applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform));
CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality
? kCGInterpolationLow
: kCGInterpolationHigh);
CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(),
sourceImage.getHeight()), image);
CGImageRelease (image);
CGContextRestoreGState (context);
}
//==============================================================================
void drawLine (double x1, double y1, double x2, double y2, const Colour& colour)
{
CGContextSetAlpha (context, 1.0f);
CGContextSetRGBStrokeColor (context, colour.getFloatRed(), colour.getFloatGreen(),
colour.getFloatBlue(), colour.getFloatAlpha());
CGContextSetLineCap (context, kCGLineCapSquare);
CGContextSetLineWidth (context, 1.0f);
CGPoint line[] = { { x1 + 0.5f, flipHeight - (y1 + 0.5f) },
{ x2 + 0.5f, flipHeight - (y2 + 0.5f) } };
CGContextStrokeLineSegments (context, line, 1);
}
void drawVerticalLine (const int x, double top, double bottom, const Colour& colour)
{
setColour (colour);
CGContextSetBlendMode (context, kCGBlendModeNormal);
CGContextFillRect (context, CGRectMake (x, flipHeight - bottom, 1.0f, bottom - top));
}
void drawHorizontalLine (const int y, double left, double right, const Colour& colour)
{
setColour (colour);
CGContextSetBlendMode (context, kCGBlendModeNormal);
CGContextFillRect (context, CGRectMake (left, y, right - left, 1.0f));
}
private:
CGContextRef context;
const float flipHeight;
void setColour (const Colour& colour) const throw()
{
CGContextSetRGBFillColor (context,
colour.getFloatRed(), colour.getFloatGreen(),
colour.getFloatBlue(), colour.getFloatAlpha());
}
static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData)
{
const ColourGradient* const g = (const ColourGradient*) info;
const Colour c (g->getColourAtPosition (inData[0]));
outData[0] = c.getFloatRed();
outData[1] = c.getFloatGreen();
outData[2] = c.getFloatBlue();
outData[3] = c.getFloatAlpha();
}
CGShadingRef createGradient (const ColourGradient& gradient) const throw()
{
CGShadingRef result = 0;
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGFunctionCallbacks callbacks = { 0, gradientCallback, 0 };
CGFunctionRef function = CGFunctionCreate ((void*) &gradient, 1, 0, 4, 0, &callbacks);
CGPoint p1 = CGPointMake (gradient.x1, gradient.y1);
if (gradient.isRadial)
{
result = CGShadingCreateRadial (colourSpace,
p1, 0,
p1, hypotf (gradient.x1 - gradient.x2, gradient.y1 - gradient.y2),
function, true, true);
}
else
{
result = CGShadingCreateAxial (colourSpace, p1,
CGPointMake (gradient.x2, gradient.y2),
function, true, true);
}
CGColorSpaceRelease (colourSpace);
CGFunctionRelease (function);
return result;
}
void drawGradient (const ColourGradient& gradient) const throw()
{
CGContextSetBlendMode (context, kCGBlendModeNormal);
CGContextSetAlpha (context, 1.0f);
CGShadingRef shading = createGradient (gradient);
CGContextDrawShading (context, shading);
CGShadingRelease (shading);
}
void createPath (const Path& path) const throw()
{
CGContextBeginPath (context);
Path::Iterator i (path);
while (i.next())
{
switch (i.elementType)
{
case Path::Iterator::startNewSubPath:
CGContextMoveToPoint (context, i.x1, i.y1);
break;
case Path::Iterator::lineTo:
CGContextAddLineToPoint (context, i.x1, i.y1);
break;
case Path::Iterator::quadraticTo:
CGContextAddQuadCurveToPoint (context, i.x1, i.y1, i.x2, i.y2);
break;
case Path::Iterator::cubicTo:
CGContextAddCurveToPoint (context, i.x1, i.y1, i.x2, i.y2, i.x3, i.y3);
break;
case Path::Iterator::closePath:
CGContextClosePath (context); break;
default:
jassertfalse
break;
}
}
}
void createPath (const Path& path, const AffineTransform& transform) const throw()
{
CGContextBeginPath (context);
Path::Iterator i (path);
while (i.next())
{
switch (i.elementType)
{
case Path::Iterator::startNewSubPath:
transform.transformPoint (i.x1, i.y1);
CGContextMoveToPoint (context, i.x1, flipHeight - i.y1);
break;
case Path::Iterator::lineTo:
transform.transformPoint (i.x1, i.y1);
CGContextAddLineToPoint (context, i.x1, flipHeight - i.y1);
break;
case Path::Iterator::quadraticTo:
transform.transformPoint (i.x1, i.y1);
transform.transformPoint (i.x2, i.y2);
CGContextAddQuadCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2);
break;
case Path::Iterator::cubicTo:
transform.transformPoint (i.x1, i.y1);
transform.transformPoint (i.x2, i.y2);
transform.transformPoint (i.x3, i.y3);
CGContextAddCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2, i.x3, flipHeight - i.y3);
break;
case Path::Iterator::closePath:
CGContextClosePath (context); break;
default:
jassertfalse
break;
}
}
}
CGImageRef createImage (const Image& juceImage, const bool forAlpha) const throw()
{
int lineStride = 0;
int pixelStride = 0;
const uint8* imageData = juceImage.lockPixelDataReadOnly (0, 0, juceImage.getWidth(), juceImage.getHeight(),
lineStride, pixelStride);
CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0);
CGColorSpaceRef colourSpace = forAlpha ? CGColorSpaceCreateDeviceGray()
: CGColorSpaceCreateDeviceRGB();
CGImageRef imageRef = CGImageCreate (juceImage.getWidth(), juceImage.getHeight(),
8, pixelStride * 8, lineStride,
colourSpace,
(juceImage.hasAlphaChannel() && ! forAlpha)
? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little)
: kCGBitmapByteOrderDefault,
provider,
0, true, kCGRenderingIntentDefault);
CGColorSpaceRelease (colourSpace);
CGDataProviderRelease (provider);
juceImage.releasePixelDataReadOnly (imageData);
return imageRef;
}
static Image* createAlphaChannelImage (const Image& im) throw()
{
if (im.getFormat() == Image::SingleChannel)
return const_cast <Image*> (&im);
return im.createCopyOfAlphaChannel();
}
static void deleteAlphaChannelImage (const Image& im, Image* const alphaIm) throw()
{
if (im.getFormat() != Image::SingleChannel)
delete alphaIm;
}
void flip() const throw()
{
CGContextConcatCTM (context, CGAffineTransformMake (1, 0, 0, -1, 0, flipHeight));
}
void applyTransform (const AffineTransform& transform) const throw()
{
CGAffineTransform t;
t.a = transform.mat00;
t.b = transform.mat10;
t.c = transform.mat01;
t.d = transform.mat11;
t.tx = transform.mat02;
t.ty = transform.mat12;
CGContextConcatCTM (context, t);
}
};
#endif

Loading…
Cancel
Save