Starting from 2021-07-01, all LRZ GitLab users will be required to explicitly accept the GitLab Terms of Service. Please see the detailed information at https://doku.lrz.de/display/PUBLIC/GitLab and make sure that your projects conform to the requirements.

Commit 0ac65c4f authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Started refactoring the ImageData conversion API:

Conversions between image representations are now managed at one central place: The ImageRepresentationConverter singleton uses the proven and established registration through static template instantiation idiom to register conversion functors during static initialization. Therefore, the ConversionFunctionRegistrar registers a conversion functor to a target representation type.

As proof-of-concept implementation, the former conversion API through T::tryConvertFrom, where T is a specific image representation, has been converted to the new API and merged into imagerepresentationconversioncore.h providing a conversion functor for each campvis-core representation.

Furthermore, implemented conversion from ImageRepresentationGL to GenericImageRepresentationLocal<>.

refs #553
refs #474
parent db69d006
......@@ -159,15 +159,6 @@ namespace campvis {
virtual ~GenericImageRepresentationLocal();
/**
* Performs a conversion of \a source to an ImageRepresentationLocal if feasible.
* Returns 0 if conversion was not successful or source representation type is not compatible.
* \note The callee, respectively the callee's parent, has the ownership of the returned pointer.
* \param source Source image representation for conversion.
* \return A pointer to a local representation of \a source or 0 on failure. The caller does \b not have ownership.
*/
static GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>* tryConvertFrom(const AbstractImageRepresentation* source);
/// \see AbstractImageRepresentation::clone()
virtual ThisType* clone(ImageData* newParent) const;
......@@ -307,61 +298,6 @@ namespace campvis {
delete [] _data;
}
template<typename BASETYPE, size_t NUMCHANNELS>
GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>* campvis::GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::tryConvertFrom(const AbstractImageRepresentation* source) {
if (const ImageRepresentationDisk* tester = dynamic_cast<const ImageRepresentationDisk*>(source)) {
// converting from disk representation
if (tester->getBaseType() == TypeTraits<BASETYPE, NUMCHANNELS>::weaklyTypedPointerBaseType && tester->getParent()->getNumChannels() == NUMCHANNELS) {
WeaklyTypedPointer wtp = tester->getImageData();
return create(tester->getParent(), static_cast<ElementType*>(wtp._pointer));
}
else {
LWARNING("Could not convert since base type or number of channels mismatch.");
}
}
if (const ImageRepresentationDisk* tester = dynamic_cast<const ImageRepresentationDisk*>(source)) {
// converting from disk representation
if (tester->getBaseType() == TypeTraits<BASETYPE, NUMCHANNELS>::weaklyTypedPointerBaseType && tester->getParent()->getNumChannels() == NUMCHANNELS) {
WeaklyTypedPointer wtp = tester->getImageData();
return create(tester->getParent(), static_cast<ElementType*>(wtp._pointer));
}
else {
LWARNING("Could not convert since base type or number of channels mismatch.");
}
}
else if (const ThisType* tester = dynamic_cast<const ThisType*>(source)) {
// just to ensure that the following else if case is really a conversion
LDEBUG("Trying to convert into the same type - this should not happen, since it there is no conversion needed...");
return tester->clone(const_cast<ImageData*>(tester->getParent()));
}
else if (const ImageRepresentationLocal* tester = dynamic_cast<const ImageRepresentationLocal*>(source)) {
// converting from other local representation of different data type
// (we ensured with the else if above that at least one of the template parameters does not match)
if (tester->getParent()->getNumChannels() == NUMCHANNELS) {
LDEBUG("Performing conversion between data types, you may lose information or the resulting data may show other unexpected features.");
size_t numElements = tester->getNumElements();
ElementType* newData = new ElementType[numElements];
// traverse each channel of each element and convert the value
for (size_t i = 0; i < numElements; ++i) {
for (size_t channel = 0; channel < NUMCHANNELS; ++channel) {
// get original value normalized to float
float tmp = tester->getElementNormalized(i, channel);
// save new value denormalized from float
TypeTraits<BASETYPE, NUMCHANNELS>::setChannel(newData[i], channel, TypeNormalizer::denormalizeFromFloat<BASETYPE>(tmp));
}
}
return create(tester->getParent(), newData);
}
else {
LWARNING("Could not convert since number of channels mismatch.");
}
}
return 0;
}
template<typename BASETYPE, size_t NUMCHANNELS>
GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>* campvis::GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::clone(ImageData* newParent) const {
size_t numElements = getNumElements();
......
......@@ -33,6 +33,7 @@
#include "core/datastructures/abstractdata.h"
#include "core/datastructures/abstractimagerepresentation.h"
#include "core/datastructures/imagemappinginformation.h"
#include "core/datastructures/imagerepresentationconverter.h"
#include <vector>
......@@ -237,7 +238,7 @@ namespace campvis {
// target type. This does not harm thread-safety but may lead to multiple
// representations of the same type for a single image.
for (tbb::concurrent_vector<const AbstractImageRepresentation*>::const_iterator it = _representations.begin(); it != _representations.end(); ++it) {
const T* tester = T::tryConvertFrom(*it);
const T* tester = ImageRepresentationConverter::getRef().tryConvertFrom<T>(*it);
if (tester != 0) {
return tester;
}
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitt Mnchen
// Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "imagerepresentationconversioncore.h"
#include "tgt/assert.h"
#include "tgt/logmanager.h"
#include "tgt/glcontextmanager.h"
#include "core/tools/opengljobprocessor.h"
#include "core/tools/job.h"
namespace campvis {
namespace conversion {
ImageRepresentationGL* GlConversion::tryConvertFrom(const AbstractImageRepresentation* source) {
if (source == nullptr)
return nullptr;
// test source image type via dynamic cast
if (const ImageRepresentationDisk* tester = dynamic_cast<const ImageRepresentationDisk*>(source)) {
WeaklyTypedPointer wtp = tester->getImageData();
if (wtp._pointer == nullptr) {
LERRORC("CAMPVis.core.datastructures.GlConversion", "Could not load image from disk during conversion.");
return nullptr;
}
ImageRepresentationGL* toReturn = ImageRepresentationGL::create(const_cast<ImageData*>(tester->getParent()), wtp);
switch (wtp._baseType) {
case WeaklyTypedPointer::UINT8:
delete [] static_cast<uint8_t*>(wtp._pointer);
break;
case WeaklyTypedPointer::INT8:
delete [] static_cast<int8_t*>(wtp._pointer);
break;
case WeaklyTypedPointer::UINT16:
delete [] static_cast<uint16_t*>(wtp._pointer);
break;
case WeaklyTypedPointer::INT16:
delete [] static_cast<int16_t*>(wtp._pointer);
break;
case WeaklyTypedPointer::UINT32:
delete [] static_cast<uint32_t*>(wtp._pointer);
break;
case WeaklyTypedPointer::INT32:
delete [] static_cast<int32_t*>(wtp._pointer);
break;
case WeaklyTypedPointer::FLOAT:
delete [] static_cast<float*>(wtp._pointer);
break;
default:
tgtAssert(false, "Should not reach this - wrong base data type!");
break;
}
return toReturn;
}
else if (const ImageRepresentationLocal* tester = dynamic_cast<const ImageRepresentationLocal*>(source)) {
ImageRepresentationGL* toReturn = ImageRepresentationGL::create(const_cast<ImageData*>(tester->getParent()), tester->getWeaklyTypedPointer());
return toReturn;
}
#ifdef CAMPVIS_HAS_MODULE_ITK
else if (const AbstractImageRepresentationItk* tester = dynamic_cast<const AbstractImageRepresentationItk*>(source)) {
ImageRepresentationGL* toReturn = ImageRepresentationGL::create(const_cast<ImageData*>(tester->getParent()), tester->getWeaklyTypedPointer());
return toReturn;
}
#endif
return nullptr;
}
ImageRepresentationLocal* LocalConversion::tryConvertFrom(const AbstractImageRepresentation* source) {
if (source == 0)
return 0;
// test source image type via dynamic cast
if (const ImageRepresentationDisk* tester = dynamic_cast<const ImageRepresentationDisk*>(source)) {
return ImageRepresentationLocal::create(tester->getParent(), tester->getImageData());
}
else if (const ImageRepresentationGL* tester = dynamic_cast<const ImageRepresentationGL*>(source)) {
OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerCopy();
if (wtp._pointer != nullptr)
return ImageRepresentationLocal::create(source->getParent(), wtp);
return nullptr;
}
#ifdef CAMPVIS_HAS_MODULE_ITK
// There is no way to determine basetype, number of channels and dimensionality of
// an ITK image at runtime. So there are currently 7*4*2 = 56 different possibilities
// what source could be. Thank god, there exists macro magic to create the 56
// different templated conversion codes.
#define CONVERT_ITK_TO_GENERIC_LOCAL(basetype, numchannels, dimensionality) \
if (const GenericImageRepresentationItk<basetype, numchannels, dimensionality>* tester = dynamic_cast< const GenericImageRepresentationItk<basetype, numchannels, dimensionality>* >(source)) { \
typedef GenericImageRepresentationItk<basetype, numchannels, dimensionality>::ItkImageType ItkImageType; \
typedef ItkImageType::PixelType ItkElementType; \
typedef GenericImageRepresentationItk<basetype, numchannels, dimensionality>::ElementType ElementType; \
const ItkElementType* pixelData = tester->getItkImage()->GetBufferPointer(); \
\
ItkImageType::RegionType region; \
region = tester->getItkImage()->GetBufferedRegion(); \
\
ItkImageType::SizeType s = region.GetSize(); \
tgt::svec3 size(s[0], 1, 1); \
if (dimensionality >= 2) \
size.y = s[1]; \
if (dimensionality == 3) \
size.z = s[2]; \
\
ElementType* pixelDataCopy = new ElementType[tgt::hmul(size)]; \
memcpy(pixelDataCopy, pixelData, tgt::hmul(size) * TypeTraits<basetype, numchannels>::elementSize); \
return GenericImageRepresentationLocal<basetype, numchannels>::create(const_cast<ImageData*>(source->getParent()), pixelDataCopy); \
}
#define DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(numchannels, dimensionality) \
CONVERT_ITK_TO_GENERIC_LOCAL(uint8_t, numchannels, dimensionality) \
else CONVERT_ITK_TO_GENERIC_LOCAL(int8_t, numchannels, dimensionality) \
else CONVERT_ITK_TO_GENERIC_LOCAL(uint16_t, numchannels, dimensionality) \
else CONVERT_ITK_TO_GENERIC_LOCAL(int16_t, numchannels, dimensionality) \
else CONVERT_ITK_TO_GENERIC_LOCAL(uint32_t, numchannels, dimensionality) \
else CONVERT_ITK_TO_GENERIC_LOCAL(int32_t, numchannels, dimensionality) \
else CONVERT_ITK_TO_GENERIC_LOCAL(float, numchannels, dimensionality)
#define DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_D(dimensionality) \
DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(1, dimensionality) \
else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(2, dimensionality) \
else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(3, dimensionality) \
else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_ND(4, dimensionality)
// okay we've defined our macros. Now we just need to call them so that they call
// each other and create 56 different conversion checks - hooray
DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_D(2)
else DISPATCH_ITK_TO_GENERIC_LOCAL_CONVERSION_D(3)
#endif
return 0;
}
}
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#ifndef IMAGEREPRESENTATIONCONVERSIONCORE_H__
#define IMAGEREPRESENTATIONCONVERSIONCORE_H__
#include "core/coreapi.h"
#include "core/datastructures/imagerepresentationconverter.h"
#include "core/datastructures/imagerepresentationdisk.h"
#include "core/datastructures/imagerepresentationlocal.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/datastructures/genericimagerepresentationlocal.h"
namespace campvis {
namespace conversion {
// = Declare converter classes ====================================================================
/// Conversion class to convert to ImageRepresentationGL.
struct CAMPVIS_CORE_API GlConversion {
static ImageRepresentationGL* tryConvertFrom(const AbstractImageRepresentation* source);
};
/// Conversion class to convert to ImageRepresentationLocal.
struct CAMPVIS_CORE_API LocalConversion {
static ImageRepresentationLocal* tryConvertFrom(const AbstractImageRepresentation* source);
};
/// Conversion class to convert to GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>.
template<typename BASETYPE, size_t NUMCHANNELS>
struct GenericLocalConversion {
static GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>* tryConvertFrom(const AbstractImageRepresentation* source);
};
// = Instantiate converter templates to register converters =======================================
// Register converters with corresponding target representations
template class ConversionFunctionRegistrar<ImageRepresentationGL, GlConversion>;
template class ConversionFunctionRegistrar<ImageRepresentationLocal, LocalConversion>;
// for GenericImageRepresentationLocal we use some macro magic to instantiate all necessary converters:
#define INSTANTIATE_TEMPLATE_BN(BASETYPE, NUMCHANNELS) template class ConversionFunctionRegistrar< GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS> , GenericLocalConversion<BASETYPE, NUMCHANNELS> >
#define INSTANTIATE_TEMPLATE_N(NUMCHANNELS) \
INSTANTIATE_TEMPLATE_BN(uint8_t, NUMCHANNELS); INSTANTIATE_TEMPLATE_BN(int8_t, NUMCHANNELS); \
INSTANTIATE_TEMPLATE_BN(uint16_t, NUMCHANNELS); INSTANTIATE_TEMPLATE_BN(int16_t, NUMCHANNELS); \
INSTANTIATE_TEMPLATE_BN(uint32_t, NUMCHANNELS); INSTANTIATE_TEMPLATE_BN(int32_t, NUMCHANNELS); \
INSTANTIATE_TEMPLATE_BN(float, NUMCHANNELS);
INSTANTIATE_TEMPLATE_N(1);
INSTANTIATE_TEMPLATE_N(2);
INSTANTIATE_TEMPLATE_N(3);
INSTANTIATE_TEMPLATE_N(4);
// = Template definition ==========================================================================
template<typename BASETYPE, size_t NUMCHANNELS>
GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>* GenericLocalConversion<BASETYPE, NUMCHANNELS>::tryConvertFrom(const AbstractImageRepresentation* source) {
if (const ImageRepresentationDisk* tester = dynamic_cast<const ImageRepresentationDisk*>(source)) {
// converting from disk representation
if (tester->getBaseType() == TypeTraits<BASETYPE, NUMCHANNELS>::weaklyTypedPointerBaseType && tester->getParent()->getNumChannels() == NUMCHANNELS) {
WeaklyTypedPointer wtp = tester->getImageData();
return GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::create(tester->getParent(), static_cast<GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::ElementType*>(wtp._pointer));
}
else {
LWARNINGC("CAMPVis.core.datastructures.GenericLocalConversion", "Could not convert since base type or number of channels mismatch.");
}
}
else if (const ImageRepresentationGL* tester = dynamic_cast<const ImageRepresentationGL*>(source)) {
// converting from GL representation
OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
if (tester->getTexture()->getDataType() != TypeTraits<BASETYPE, NUMCHANNELS>::glDataType)
LDEBUGC("CAMPVis.core.datastructures.GenericLocalConversion", "Performing conversion between data types, you may lose information or the resulting data may show other unexpected features.");
WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerConvert(TypeTraits<BASETYPE, NUMCHANNELS>::glDataType);
if (wtp._pointer != nullptr)
return GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::create(tester->getParent(), static_cast<GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::ElementType*>(wtp._pointer));
return nullptr;
}
else if (const GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::ThisType* tester = dynamic_cast<const GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::ThisType*>(source)) {
// just to ensure that the following else if case is really a conversion
LDEBUGC("CAMPVis.core.datastructures.GenericLocalConversion", "Trying to convert into the same type - this should not happen, since it there is no conversion needed...");
return tester->clone(const_cast<ImageData*>(tester->getParent()));
}
else if (const ImageRepresentationLocal* tester = dynamic_cast<const ImageRepresentationLocal*>(source)) {
// converting from other local representation of different data type
// (we ensured with the else if above that at least one of the template parameters does not match)
if (tester->getParent()->getNumChannels() == NUMCHANNELS) {
LDEBUGC("CAMPVis.core.datastructures.GenericLocalConversion", "Performing conversion between data types, you may lose information or the resulting data may show other unexpected features.");
size_t numElements = tester->getNumElements();
GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::ElementType* newData = new GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::ElementType[numElements];
// traverse each channel of each element and convert the value
for (size_t i = 0; i < numElements; ++i) {
for (size_t channel = 0; channel < NUMCHANNELS; ++channel) {
// get original value normalized to float
float tmp = tester->getElementNormalized(i, channel);
// save new value denormalized from float
TypeTraits<BASETYPE, NUMCHANNELS>::setChannel(newData[i], channel, TypeNormalizer::denormalizeFromFloat<BASETYPE>(tmp));
}
}
return GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::create(tester->getParent(), newData);
}
else {
LWARNINGC("CAMPVis.core.datastructures.GenericLocalConversion", "Could not convert since number of channels mismatch.");
}
}
return nullptr;
}
}
}
#endif // IMAGEREPRESENTATIONCONVERSIONCORE_H__
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#include "imagerepresentationconverter.h"
#include <functional>
#include "core/properties/abstractproperty.h"
namespace campvis {
tbb::atomic<ImageRepresentationConverter*> ImageRepresentationConverter::_singleton;
ImageRepresentationConverter& ImageRepresentationConverter::getRef() {
if (_singleton == 0) {
std::cout << "creating ImageRepresentationConverter...\n";
ImageRepresentationConverter* tmp = new ImageRepresentationConverter();
if (_singleton.compare_and_swap(tmp, 0) != 0) {
delete tmp;
}
}
return *_singleton;
}
void ImageRepresentationConverter::deinit() {
delete _singleton;
_singleton = nullptr;
}
size_t ImageRepresentationConverter::registerConversionFunction(const std::type_info& type, ConversionFunctionPointer funcPtr) {
tbb::spin_mutex::scoped_lock lock(_mutex);
std::type_index typeIndex(type);
if (funcPtr != nullptr) {
_conversionFunctionMap.insert(std::make_pair(typeIndex, funcPtr));
}
return _conversionFunctionMap.size();
}
}
\ No newline at end of file
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012-2013, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
//
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
// except in compliance with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions
// and limitations under the License.
//
// ================================================================================================
#ifndef IMAGEREPRESENTATIONCONVERTER_H__
#define IMAGEREPRESENTATIONCONVERTER_H__
#include "tgt/logmanager.h"
#include "tgt/singleton.h"
#include <tbb/atomic.h>
#include <tbb/spin_mutex.h>
#include "core/coreapi.h"
#include "core/datastructures/abstractimagerepresentation.h"
#include <map>
#include <string>
#include <typeindex>
#include <typeinfo>
#include <vector>
class QWidget;
namespace campvis {
/**
* Factory for creating PropertyWidgets depending on the Property type.
* Using some template-magic, ImageRepresentationConverter is able to register PropertyWidgets during static
* initialization in cooperation with the ConversionFunctionRegistrar.
*
* \note ImageRepresentationConverter is a thread-safe lazy-instantiated singleton.
*/
class CAMPVIS_CORE_API ImageRepresentationConverter {
public:
/// Typedef for a function pointer to convert between image representations.
typedef const AbstractImageRepresentation* (*ConversionFunctionPointer) (const AbstractImageRepresentation*);
/**
* Returns a reference to the PipelineFactory singleton.
* Creates the singleton in a thread-safe fashion, if necessary.
* \return *_singleton
*/
static ImageRepresentationConverter& getRef();
/**
* Deinitializes the Singleton.
*/
static void deinit();
/**
* Registers the the property of type \a type to have widgets created with the given function pointers.
* \note The template instantiation of ConversionFunctionRegistrar takes care of calling this method.
* \param type Property type to register.
* \param funcPtr Pointer to a conversion function.
* \return
*/
size_t registerConversionFunction(const std::type_info& type, ConversionFunctionPointer funcPtr);
template<typename T>
const T* tryConvertFrom(const AbstractImageRepresentation* source) {
auto itPair = _conversionFunctionMap.equal_range(std::type_index(typeid(T)));
// try conversion with all registered convertes for the given target type
for (auto it = itPair.first; it != itPair.second; ++it) {
const T* tester = static_cast<const T*>(it->second(source));
if (tester != nullptr)
return tester;
}
return nullptr;
};
private:
mutable tbb::spin_mutex _mutex; ///< Mutex protecting the singleton during initialization
static tbb::atomic<ImageRepresentationConverter*> _singleton; ///< the singleton object
/// Typedef for the map associating property types with conversion function pointers
typedef std::multimap<std::type_index, ConversionFunctionPointer> ConversionFunctionMapType;
/// map associating property types with creator function pointers
ConversionFunctionMapType _conversionFunctionMap;
};
// ================================================================================================
/**
* Templated class exploiting the CRTP to allow easy registration of property widgets crossing
* DLL bounds using a single template instantiation.
* \tparam WidgetType Property widget type to register.
*/
template<typename ConversionTarget, typename ConverterClass>
class ConversionFunctionRegistrar {
public:
static const AbstractImageRepresentation* tryConvertFrom(const AbstractImageRepresentation* source) {
return ConverterClass::tryConvertFrom(source);
}