10b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/*****************************************************************************/
20b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener// Copyright 2006 Adobe Systems Incorporated
30b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener// All Rights Reserved.
40b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener//
50b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener// NOTICE:  Adobe permits you to use, modify, and distribute this file in
60b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener// accordance with the terms of the Adobe license agreement accompanying it.
70b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/*****************************************************************************/
80b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
90b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_area_task.h#1 $ */
100b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/* $DateTime: 2012/05/30 13:28:51 $ */
110b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/* $Change: 832332 $ */
120b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/* $Author: tknoll $ */
130b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
140b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/** \file
150b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener * Class to handle partitioning a rectangular image processing operation taking into account multiple processing resources and memory constraints.
160b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener */
170b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
180b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/*****************************************************************************/
190b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
200b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener#ifndef __dng_area_task__
210b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener#define __dng_area_task__
220b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
230b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/*****************************************************************************/
240b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
250b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener#include "dng_classes.h"
260b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener#include "dng_point.h"
270b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener#include "dng_types.h"
280b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
290b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/*****************************************************************************/
300b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
310b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/// \brief Abstract class for rectangular processing operations with support for partitioning across multiple processing resources and observing memory constraints.
320b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
330b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Krienerclass dng_area_task
340b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener	{
350b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
360b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener	protected:
370b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
380b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		uint32 fMaxThreads;
390b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
400b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		uint32 fMinTaskArea;
410b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
420b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		dng_point fUnitCell;
430b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
440b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		dng_point fMaxTileSize;
450b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
460b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener	public:
470b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
480b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		dng_area_task ();
490b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
500b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual ~dng_area_task ();
510b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
520b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Getter for the maximum number of threads (resources) that can be used for processing
530b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
540b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \retval Number of threads, minimum of 1, that can be used for this task.
550b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
560b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual uint32 MaxThreads () const
570b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			{
580b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			return fMaxThreads;
590b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			}
600b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
610b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Getter for minimum area of a partitioned rectangle.
620b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Often it is not profitable to use more resources if it requires partitioning the input into chunks that are too small,
630b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// as the overhead increases more than the speedup. This method can be ovreridden for a specific task to indicate the smallest
640b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// area for partitioning. Default is 256x256 pixels.
650b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
660b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \retval Minimum area for a partitoned tile in order to give performant operation. (Partitions can be smaller due to small inputs and edge cases.)
670b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
680b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual uint32 MinTaskArea () const
690b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			{
700b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			return fMinTaskArea;
710b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			}
720b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
730b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Getter for dimensions of which partitioned tiles should be a multiple.
740b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Various methods of processing prefer certain alignments. The partitioning attempts to construct tiles such that the
750b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// sizes are a multiple of the dimensions of this point.
760b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
770b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \retval a point giving preferred alignment in x and y
780b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
790b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual dng_point UnitCell () const
800b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			{
810b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			return fUnitCell;
820b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			}
830b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
840b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Getter for maximum size of a tile for processing.
850b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Often processing will need to allocate temporary buffers or use other resources that are either fixed or in limited supply.
860b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// The maximum tile size forces further partitioning if the tile is bigger than this size.
870b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
880b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \retval Maximum tile size allowed for this area task.
890b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
900b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual dng_point MaxTileSize () const
910b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			{
920b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			return fMaxTileSize;
930b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener			}
940b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
950b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Getter for RepeatingTile1.
960b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to establish a set of 0 to 3 tile patterns for which
970b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// the resulting partitions that the final Process method is called on will not cross tile boundaries in any of the
980b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// tile patterns. This can be used for a processing routine that needs to read from two tiles and write to a third
990b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// such that all the tiles are aligned and sized in a certain way. A RepeatingTile value is valid if it is non-empty.
1000b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Higher numbered RepeatingTile patterns are only used if all lower ones are non-empty. A RepeatingTile pattern must
1010b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// be a multiple of UnitCell in size for all constraints of the partitionerr to be met.
1020b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1030b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual dng_rect RepeatingTile1 () const;
1040b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1050b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Getter for RepeatingTile2.
1060b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to establish a set of 0 to 3 tile patterns for which
1070b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// the resulting partitions that the final Process method is called on will not cross tile boundaries in any of the
1080b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// tile patterns. This can be used for a processing routine that needs to read from two tiles and write to a third
1090b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// such that all the tiles are aligned and sized in a certain way. A RepeatingTile value is valid if it is non-empty.
1100b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Higher numbered RepeatingTile patterns are only used if all lower ones are non-empty. A RepeatingTile pattern must
1110b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// be a multiple of UnitCell in size for all constraints of the partitionerr to be met.
1120b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1130b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual dng_rect RepeatingTile2 () const;
1140b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1150b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Getter for RepeatingTile3.
1160b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to establish a set of 0 to 3 tile patterns for which
1170b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// the resulting partitions that the final Process method is called on will not cross tile boundaries in any of the
1180b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// tile patterns. This can be used for a processing routine that needs to read from two tiles and write to a third
1190b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// such that all the tiles are aligned and sized in a certain way. A RepeatingTile value is valid if it is non-empty.
1200b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Higher numbered RepeatingTile patterns are only used if all lower ones are non-empty. A RepeatingTile pattern must
1210b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// be a multiple of UnitCell in size for all constraints of the partitionerr to be met.
1220b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1230b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual dng_rect RepeatingTile3 () const;
1240b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1250b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Task startup method called before any processing is done on partitions.
1260b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// The Start method is called before any processing is done and can be overridden to allocate temporary buffers, etc.
1270b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
1280b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param threadCount Total number of threads that will be used for processing. Less than or equal to MaxThreads.
1290b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param tileSize Size of source tiles which will be processed. (Not all tiles will be this size due to edge conditions.)
1300b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param allocator dng_memory_allocator to use for allocating temporary buffers, etc.
1310b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param sniffer Sniffer to test for user cancellation and to set up progress.
1320b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1330b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual void Start (uint32 threadCount,
1340b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							const dng_point &tileSize,
1350b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							dng_memory_allocator *allocator,
1360b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							dng_abort_sniffer *sniffer);
1370b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1380b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Process one tile or fully partitioned area.
1390b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// This method is overridden by derived classes to implement the actual image processing. Note that the sniffer can be ignored if it is certain that a
1400b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// processing task will complete very quickly.
1410b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// This method should never be called directly but rather accessed via Process.
1420b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// There is no allocator parameter as all allocation should be done in Start.
1430b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
1440b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param threadIndex 0 to threadCount - 1 index indicating which thread this is. (Can be used to get a thread-specific buffer allocated in the Start method.)
1450b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param tile Area to process.
1460b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates.
1470b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1480b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual void Process (uint32 threadIndex,
1490b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							  const dng_rect &tile,
1500b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							  dng_abort_sniffer *sniffer) = 0;
1510b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1520b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Task computation finalization and teardown method.
1530b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Called after all resources have completed processing. Can be overridden to accumulate results and free resources allocated in Start.
1540b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
1550b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param threadCount Number of threads used for processing. Same as value passed to Start.
1560b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1570b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		virtual void Finish (uint32 threadCount);
1580b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1590b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Find tile size taking into account repeating tiles, unit cell, and maximum tile size.
1600b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param area Computation area for which to find tile size.
1610b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \retval Tile size as height and width in point.
1620b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1630b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		dng_point FindTileSize (const dng_rect &area) const;
1640b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1650b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Handle one resource's worth of partitioned tiles.
1660b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Called after thread partitioning has already been done. Area may be further subdivided to handle maximum tile size, etc.
1670b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// It will be rare to override this method.
1680b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		///
1690b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param threadIndex 0 to threadCount - 1 index indicating which thread this is.
1700b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param area Tile area partitioned to this resource.
1710b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param tileSize
1720b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates.
1730b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1740b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		void ProcessOnThread (uint32 threadIndex,
1750b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							  const dng_rect &area,
1760b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							  const dng_point &tileSize,
1770b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener							  dng_abort_sniffer *sniffer);
1780b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1790b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Default resource partitioner that assumes a single resource to be used for processing.
1800b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// Implementations that are aware of multiple processing resources should override (replace) this method.
1810b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// This is usually done in dng_host::PerformAreaTask .
1820b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param task The task to perform.
1830b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param area The area on which mage processing should be performed.
1840b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param allocator dng_memory_allocator to use for allocating temporary buffers, etc.
1850b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		/// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates.
1860b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1870b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener		static void Perform (dng_area_task &task,
1880b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener				  			 const dng_rect &area,
1890b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener				  			 dng_memory_allocator *allocator,
1900b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener				  			 dng_abort_sniffer *sniffer);
1910b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1920b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener	};
1930b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1940b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/*****************************************************************************/
1950b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1960b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener#endif
1970b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener
1980b098b759cf9a2a8659babaf8c3b74e1b48ca604Florian Kriener/*****************************************************************************/
199